C++即时通讯系统Socket数据无法发送至服务器问题排查
问题诊断与修复方案
先直接说最致命的问题:你的客户端代码错误地调用了bind()函数,这是导致客户端显示连接成功但服务端完全没反应的核心原因。
核心错误分析
客户端无需手动绑定端口:
客户端发起TCP连接时,完全不需要调用bind()——操作系统会自动为客户端分配一个可用的临时端口。你在客户端里绑定了127.0.0.1:9999,这和服务端监听的端口直接冲突了!虽然客户端的connect()返回了成功,但实际上这个异常的绑定操作让连接处于异常状态,服务端根本无法正确接收到你的连接请求。服务端的连接处理逻辑缺陷:
服务端在accept到一个客户端后,直接调用recv读取一次消息就回到循环等待下一个连接,这会导致两个问题:- 第一个客户端只能发送一条消息,后续消息服务端完全不会处理
- 无法同时处理多个客户端的连接请求
修正后的代码
客户端修正(移除错误的bind调用)
#include <stdio.h> #include <tchar.h> #include <winsock2.h> #include <stdlib.h> #include <iostream> #include <thread> #include <vector> #include <Ws2tcpip.h> #include <string> #define _WINSOCK_DEPRECATED_NO_WARNINGS void sendMessage(SOCKET s); void recvMessage(SOCKET s); int main() { std::locale::global(std::locale("fr-FR")); WSAData wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "Error initializing winsock"; return -1; } SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (clientSocket == INVALID_SOCKET) { std::cout << "Error initializing the socket "; return -2; } sockaddr_in addr; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_family = AF_INET; addr.sin_port = htons(9999); // 移除客户端的bind调用!这是核心错误 if (connect(clientSocket, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { std::cout << "Erreur when connecting : " << WSAGetLastError() << std::endl; return -4; } std::cout << "You are connected to server" << std::endl; std::thread sending(sendMessage, clientSocket); std::thread receiving(recvMessage, clientSocket); // 用join替代detach,确保线程执行完毕再退出程序 sending.join(); receiving.join(); // 记得释放资源 closesocket(clientSocket); WSACleanup(); return 0; } void sendMessage(SOCKET s) { while (true) { std::string buff; std::cout << "Type your message :" << std::endl; std::getline(std::cin, buff); // 发送实际长度的消息(包含字符串结束符),不要固定发1024字节 int Bytes = send(s, buff.c_str(), buff.size() + 1, 0); if (Bytes <= 0) { std::cout << "Send failed or connection closed" << std::endl; break; } } } void recvMessage(SOCKET s) { while (true) { char buffer[1025] = {0}; // 初始化缓冲区,避免打印垃圾数据 int Bytes = recv(s, buffer, 1024, 0); if (Bytes <= 0) { std::cout << "Connection closed by server" << std::endl; break; } std::cout << "The server sent : " << buffer << std::endl; } }
服务端修正(支持多客户端连接)
#include <stdio.h> #include <tchar.h> #include <winsock2.h> #include <stdlib.h> #include <iostream> #include <thread> #include <vector> #include <Ws2tcpip.h> #include <string> #include <mutex> // 维护客户端列表,加锁保证线程安全 std::vector<SOCKET> clients; std::mutex clientsMutex; // 单独线程处理每个客户端的消息 void handleClient(SOCKET clientSocket) { char buffer[1025]; while (true) { int Bytes = recv(clientSocket, buffer, 1024, 0); if (Bytes <= 0) { std::cout << "Client disconnected" << std::endl; closesocket(clientSocket); // 从客户端列表移除 std::lock_guard<std::mutex> lock(clientsMutex); auto it = std::find(clients.begin(), clients.end(), clientSocket); if (it != clients.end()) { clients.erase(it); } break; } std::cout << "Message received from client : " << buffer << std::endl; // 回发消息给客户端,发送实际接收的字节数 send(clientSocket, buffer, Bytes, 0); } } int main() { std::locale::global(std::locale("fr-FR")); WSAData wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cout << "Error initializing winsock"; return -1; } SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (server == INVALID_SOCKET) { std::cout << "Error initializing the socket "; return -2; } const unsigned short port = 9999; sockaddr_in addr; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); addr.sin_family = AF_INET; int res = bind(server, (sockaddr*)&addr, sizeof(addr)); if (res != 0) { std::cout << "Error when binding"; return -3; } res = listen(server, SOMAXCONN); if (res != 0) { std::cout << "Error on calling listen"; return -4; } std::cout << "Server successfully launched" << std::endl; while (true) { sockaddr_in from = { 0 }; int addrlen = sizeof(from); SOCKET newClient = accept(server, (SOCKADDR*)(&from), &addrlen); if (newClient != INVALID_SOCKET) { std::cout << "Client connected successfully" << std::endl; // 添加到客户端列表 std::lock_guard<std::mutex> lock(clientsMutex); clients.push_back(newClient); // 启动线程处理这个客户端 std::thread clientThread(handleClient, newClient); clientThread.detach(); } } // 释放服务端socket资源(实际这里循环不会退出,只是规范写法) closesocket(server); WSACleanup(); return 0; }
额外注意事项
- 消息长度控制:不要固定发送1024字节,应该发送实际的消息长度(包括字符串结束符
\0),避免传输多余的垃圾数据。 - 缓冲区初始化:接收缓冲区一定要初始化,否则可能打印出内存中的随机垃圾数据。
- 线程与资源管理:客户端用
join()替代detach(),确保线程执行完毕再退出;服务端要注意客户端断开后的资源清理,避免内存泄漏。 - 资源释放:程序退出时必须调用
closesocket()和WSACleanup(),释放Socket相关的系统资源。
内容的提问来源于stack exchange,提问作者Soufiane
相关产品推荐
相关产品推荐

