Visual C++ 跨线程发送消息

Posted

技术标签:

【中文标题】Visual C++ 跨线程发送消息【英文标题】:Visual C++ Sending message across thread 【发布时间】:2017-03-30 07:06:44 【问题描述】:

我正在编写一个小型服务器应用程序,它有 ServerSocket 对象等待并创建连接套接字。 每个连接都有一个 SocketListener 和一个 SocketSender 用于传输数据。

每个 SocketListener 都是一个单独的线程。当客户端断开连接时,SocketListener 会向 ServerSocket 发送一条消息,通知它正在关闭,以便 ServerSocket 可以从列表中清除该连接的句柄。

但是,不知道为什么 SocketListener 线程没有收到消息。我试图缩小消息过滤器的范围,但没有运气。有人可以帮我解决这个问题。

DWORD ServerSocket::m_ThreadFunc() 
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) 
    printf("WSAStartup failed\n");
    return 1;


ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;

// Resolve the server address and port
iResult = getaddrinfo(NULL, DEFAULT_PORT_STR, &hints, &result);
if (iResult != 0) 
    printf("getaddrinfo failed\n");
    WSACleanup();
    return 1;


// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) 
    printf("socket failed\n");
    freeaddrinfo(result);
    WSACleanup();
    return 1;


// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) 
    printf("bind failed\n");
    freeaddrinfo(result);
    closesocket(ListenSocket);
    WSACleanup();
    return 1;


freeaddrinfo(result);

iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) 
    printf("listen failed\n");
    closesocket(ListenSocket);
    WSACleanup();
    return 1;


// allow listener thread to report back its state
MSG msg;
ZeroMemory(&msg, sizeof(MSG));


//force create message queue
PeekMessage(&msg, NULL, WM_USER, WM_USER + 100, PM_NOREMOVE);

printf("Listing for clients on port %s \n", DEFAULT_PORT_STR);
while (listening) 
    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket != INVALID_SOCKET) 
        printf("Client connected\n");

        sender = new SocketSender(ClientSocket);

        listener = new SocketListener(ClientSocket, this->m_threadId);
        printf("Listener created\n");

        listener->setSender(sender);
        listener->startThread();

        printf("Listener started\n");

        listenerList.push_back(listener);
        senderList.push_back(sender);

        printf("Listener list size: %d \n", listenerList.size());

        printf("Listener pushed to list\n");

        //delete socket data if listener close itself due to connection lost or disconnect.

    
    else 
        int error = WSAGetLastError();
        printf("accept failed\n");
        switch (error) 
        case 10093:
            listening = false;
            try 
                closesocket(ListenSocket);
            
            catch (...) 
            return 1;
        
    

    printf("Check message queue for thread message\n");
    //check thread message queue
    //GetMessage(&msg, NULL, 0, 0); //this blocks untill a message is get.

    PeekMessage(&msg, NULL, WM_USER, WM_USER + 100, PM_REMOVE);



    if (msg.message == WM_USER + 1)
    
        //ProcessCustomMessage(msg);
        m_deleteListener((SocketListener*)msg.wParam);
        printf("Recieved message from ThreadID: %d \n", msg.wParam);
    
    printf("Recieved message from ThreadID: %d \n", msg.message);

    printf("Server socket complete 1 loop\n");


return 0;

DWORD SocketListener::m_ThreadFunc() 
listening = true;

rapidxml::xml_document<> doc;

printf("Start thread with ID: %d \n", this->m_threadId);
printf("Parent Thread ID: %d \n", this->_iParentID);


while (listening) 
    int iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
    if (iResult > 0) 
        printf("Bytes received: %d\n", iResult);
        recvbuf[iResult - 1] = 0; // null terminate the string according to the length

        // The message spec indicates the XML will end in a "new line"
        // character which can be either a newline or caraiage feed
        // Search for either and replace with a NULL to terminate
        if (recvbuf[iResult - 2] == '\n' || recvbuf[iResult - 2] == '\r')
            recvbuf[iResult - 2] = 0;

        try  
            doc.parse<0>(&recvbuf[0]);
            HandleXMLMessage(&doc);
        

        catch (...) 

    
    else       
        printf("Thread %d is being closed, sending signal to parent (%d)\n", this->m_threadId, this->_iParentID);


        if (PostThreadMessage(this->_iParentID, WM_APP + 1, NULL, NULL) == 0)
        
            printf("Client cant send Message before closing the conn ! \n");
            printf("Last error %d", GetLastError());
        
        else 
            printf("Client sent Closing Message successfully \n");
        


        closesocket(ClientSocket);
        return 1;
    


printf("Server terminate connection\n");

printf("Thread %d is closed, sending signal to parent (%d)\n", this->m_threadId,this->_iParentID);

PostThreadMessage(this->_iParentID, WM_USER + 1, (WPARAM)this->m_threadId, NULL);


return 0;

非常感谢。

【问题讨论】:

【参考方案1】:

accept 函数是一个阻塞调用,所以看起来在接受第一个连接后你的循环卡住了

ClientSocket = accept(ListenSocket, NULL, NULL);

并且在建立下一个连接之前不会检查线程消息队列。

您还需要检查 PeekMessage 的返回结果,以了解消息是否真的被偷看,否则 msg 变量将包含垃圾。

【讨论】:

好吧,但我可以看到它接受超过 1 个连接,并且仍然执行循环中的所有任务,除了 PeakMessage 和之后的所有任务。如果我创建 3 个连接,然后断开 1 个连接,然后再创建 2 个,一切正常,除了关闭消息,因此列表中总共有 5 个连接。 正如我上面提到的,你不检查 PeekMessage 的结果。而且您也不检查 PostThreadMessage 的结果。此外,当您发布 WM_USER+1 消息时,您将 this->m_threadId 作为 WPARAM 传递,但是当您查看此消息时,您将 WPARAM 转换为 SocketListener *,这很可能会导致崩溃。好吧,我建议你制作一个简单的线程程序,它将创建主线程和一些调用 Sleep(random_delay) 的工作线程来模拟工作,然后通知主线程。这样你就可以保持你的线程逻辑分离,并确保它在不处理网络问题的情况下正常工作。 感谢您的快速回复。实际上我确实检查了 PostThreadMessage 错误code if (PostThreadMessage(this->_iParentID, WM_APP + 1, NULL, NULL) == 0) printf("Client cant send Message before close the conn ! \n"); printf("最后一个错误 %d", GetLastError()); code 没有错误。你是对的,我得到消息参数并转换为其他东西(SocketListener *)。我会解决这个问题,看看是否有帮助。非常感谢。 感谢@dodo951 的帮助。我发现了我的问题。 嗯,这实际上是你的答案@dodo951。昨天,我不明白你对 accept() 函数的回答。对不起,这是我的错。你的回答是正确的,但我想我的名声很低,所以我不能投票给它。我将在下面删除我的答案,因为它最初是你的

以上是关于Visual C++ 跨线程发送消息的主要内容,如果未能解决你的问题,请参考以下文章

PyQt跨线程发出信号

跨线程操作Treeview

从主线程发送信号给子线程,子线程里的connect函数怎么个写法

socket.io 如何跨多个服务器发送消息?

MFC SendMessage和PostMessage 区别

使用 Discord.js 跨渠道发送消息会产生“未定义”错误