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++ 跨线程发送消息的主要内容,如果未能解决你的问题,请参考以下文章
从主线程发送信号给子线程,子线程里的connect函数怎么个写法