You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

多线程Socket编程:服务器无法接收客户端多条消息求助

问题分析与解决方案

首先,咱们来定位你遇到的问题:发送客户端在发送第一条消息后卡住,无法继续输入下一条,核心原因出在你的发送客户端代码里的recv调用上。

为什么会卡住?

看你的发送客户端代码,每次调用send发送消息后,立刻调用了recv等待服务器的响应:

int sendResult = send(sock, userInput.c_str(), userInput.size() + 1, 0);
if (sendResult != SOCKET_ERROR) {
    ZeroMemory(buf, 4096);
    int bytesReceived = recv(sock, buf, 4096, 0); // 这里会阻塞!
    if (bytesReceived > 0) {
        std::cout << "SERVER> " << std::string(buf, 0, bytesReceived) << std::endl;
    }
}

但你的服务器逻辑是把发送客户端的消息转发给固定的显示客户端,并没有给发送客户端回复任何内容。recv是阻塞式调用,它会一直等待服务器发送数据,而服务器永远不会发,所以你的客户端就卡在这一步,无法回到循环开头等待用户输入下一条消息。

而PuTTY作为客户端时,它不会主动去调用recv等待服务器回复(或者说你没触发这个行为),所以不会出现卡住的情况。


解决方案

根据你的需求,有两种修复方式:

方案1:移除发送客户端的recv调用(推荐,因为你不需要服务器给发送客户端回复)

既然你的服务器不需要给发送客户端返回数据,那直接删掉send之后的recv相关代码即可,这样发送完消息后程序会立刻回到循环,等待用户输入下一条。

修改后的发送客户端核心代码:

do {
    std::cout << "> ";
    std::getline(std::cin, userInput);
    if (userInput.size() > 0) {
        int sendResult = send(sock, userInput.c_str(), userInput.size() + 1, 0);
        if (sendResult == SOCKET_ERROR) {
            std::cerr << "Send failed, Err #" << WSAGetLastError() << std::endl;
            break; // 发送失败就退出循环
        }
        // 移除这里的recv代码
    }
} while (userInput.size() > 0);

方案2:修改服务器,给发送客户端发送确认消息

如果你希望服务器给发送客户端返回一个“消息已收到”的确认,那需要完善服务器的转发逻辑,在把消息发给显示客户端后,给发送客户端回复一条确认信息。

比如服务器代码中处理客户端消息的部分(补充你未写完的逻辑):

else {
    char buf[4096];
    ZeroMemory(buf, 4096);
    int bytesReceived = recv(sock, buf, 4096, 0);
    if (bytesReceived <= 0) {
        // 客户端断开连接
        closesocket(sock);
        FD_CLR(sock, &master);
    } else {
        // 转发给显示客户端(假设你已经记录了显示客户端的socket)
        SOCKET displayClient = ...; // 替换为你实际的显示客户端socket变量
        send(displayClient, buf, bytesReceived, 0);
        
        // 给发送客户端回复确认
        std::string ackMsg = "Message received and forwarded\r\n";
        send(sock, ackMsg.c_str(), ackMsg.size() + 1, 0);
    }
}

这样发送客户端的recv就能收到服务器的确认,不会卡住,能继续输入下一条消息。


额外的小建议

  1. C++标准中main函数应该返回int,而不是void,建议修改为int main(),最后加return 0;
  2. 在服务器代码中,要记得处理客户端断开的情况(比如recv返回0或错误时,从fd_set中移除该socket并关闭)。
  3. 显示客户端的代码中,socket创建失败后没有调用WSACleanup就返回了,这是资源泄漏,建议补充:
if (sock == INVALID_SOCKET) {
    std::cerr << "Can't create socket, Err #" << WSAGetLastError() << std::endl;
    WSACleanup(); // 补充这句
    return;
}

内容的提问来源于stack exchange,提问作者Seren

火山引擎 最新活动