1

我用 mfc 做了一个简单的检查登录程序。我使用 WSAAsyncselect 函数来创建一个非阻塞套接字。每次有人登录时,服务器都会向所有当前客户端发送一条消息“用户 x 已登录”,然后他们将在他们的消息日志中显示该消息。

通常,在将数据发送到服务器后,我只能从调用 WSAAsyncselect 函数的按钮更新值。例如

void CClientDlg::OnBnClickedLogin()
{
    // TODO: Add your control notification handler code here
    UpdateData(TRUE);
    if ((m_username == "") || (m_password == ""))
    {
        return;
    }
    client = socket(AF_INET, SOCK_STREAM, 0);
    if (client == INVALID_SOCKET)
    {
        return;
    }
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(PORT);
    char* IPstr = CT2A(IP);
    serverAddr.sin_addr.s_addr = inet_addr(IPstr);
    int error = connect(client, (sockaddr*)&serverAddr, sizeof(serverAddr));
    if (error == SOCKET_ERROR) {
        return;
    }

    modestr = _T("1 ") + m_username + _T(",") + m_password;
    SendInfo(modestr);
    WSAAsyncSelect(client, m_hWnd, WM_SOCKET, FD_READ | FD_CLOSE);
    UpdateData(FALSE);
}

在服务器上,我有这个

BEGIN_MESSAGE_MAP(CServerDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_MESSAGE(WM_SOCKET, SockMsg)
END_MESSAGE_MAP()
LRESULT CServerDlg::SockMsg(WPARAM wParam, LPARAM lParam) {
    if (WSAGETSELECTERROR(lParam))
    {
        closesocket(wParam);
        MessageBox(_T("error"));
    }
    switch (WSAGETSELECTEVENT(lParam))
    {
    case FD_ACCEPT:
    {
        //accept client
        clientSocks.push_back(CLIENT(accept(wParam, NULL, NULL), "unidentified user"));

        break;
    }
    case FD_READ:
    {
        //check login codes
        //check login codes 
        //check login codes
                for (int i = 0; i < num_clients; i++) {
                    CString message = _T("0 ") + CString(user.c_str()) + _T("login\r\n");
                    SendResponse(clientSocks[i].clientSocket, message); //a send funtion to send data to 
                    //another socker
                }
}

在服务器向这些客户端发送数据后,无论如何都会自动更新数据

4

1 回答 1

1

不回答您的问题,因为此文本太长,无法发表评论。

我认为你完全误解了它的WSAAsyncSelect工作原理。该函数不会打开基于 Windows 消息的从“客户端”到“服务器”的第二个通信通道。(已经有一个通道:通过套接字的网络连接。)

您用于WSAAsyncSelect允许数据传输发生在后台,而用户界面的消息循环可以继续运行并可以响应用户活动。

在您的情况下,该nMsg参数WM_SOCKET不会发送到不同的可执行文件,而是在请求的事件发生时发送到应用程序本身。这些告诉“嘿,我刚刚收到数据!”,“嘿,网络连接已关闭”,“嘿,现在有空间写入更多数据”。

FD_READ在您的示例中,当应用程序不读取数据时请求事件是没有意义的。

此外,由于您的“客户端”和“服务器”似乎都是用户界面应用程序,因此都应该使用WSAASyncSelect. 发送端会监听FD_WRITE | FD_CLOSE事件(因为它必须写入数据),接收端会监听FD_READ | FD_CLOSE事件(因为它必须读取数据)。

请注意,一般情况下,使用的发送端WSAAsyncSelect实现起来非常复杂:它不仅仅是一个简单的单write(),因为它必须准备好不能一次发送所有数据。相反,它必须保留要发送的数据,必须记录已发送的数据和尚未发送的数据。因此,当数据包很小(最多几百字节)且不频繁时,我不会在发送方使用异步通信,并且只希望操作系统中的网络层能够为我缓存数据并且不会阻止write()通话。

于 2020-06-14T06:25:50.707 回答