Windows7下C++ Socket接收大量数据块失败的问题求助
问题:TCP Socket接收大量数据块中途停止的解决方法
我在Windows 7电脑上尝试使用C++的Socket流式传输多个代表图像的数据块,使用的代码如下:
int main() { std::string _ipaddress = "192.168.0.1"; int dport = 6342; SOCKADDR_IN dtarget; dtarget.sin_family = AF_INET; dtarget.sin_port = htons(dport); dtarget.sin_addr.s_addr = inet_addr(_ipaddress.c_str()); SOCKET _data_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (connect(_data_sock, (SOCKADDR*)&dtarget, sizeof(dtarget)) == SOCKET_ERROR) { return 1; } for (int i = 0; i < 100000; i++) { fd_set read_flags, write_flags; FD_ZERO(&read_flags); FD_ZERO(&write_flags); FD_SET(_data_sock, &read_flags); struct timeval waitd; waitd.tv_sec = 5; int sel; sel = select(_data_sock + 1, &read_flags, &write_flags, (fd_set*)0, &waitd); if (FD_ISSET(_data_sock, &read_flags)) { FD_CLR(_data_sock, &read_flags); int readCount = 0; int nread = 0; int chunkSize = 8576; char* chunkBuf = (char*)calloc(chunkSize, 1); do { if ((nread = recv(_data_sock, chunkBuf + readCount, chunkSize - readCount, 0)) != SOCKET_ERROR) { readCount += nread; }; } while (nread != 0 && readCount < chunkSize && nread != SOCKET_ERROR); // do something with chunkBuf } } return 0; }
程序先连接SOCKET _data_sock后进入for循环,在循环内声明、初始化并设置fd_set,随后使用recv()接收数据块到缓冲区chunkBuf。
代码能正常接收约30000个数据块(数量略有浮动),但之后突然无法继续接收数据。我怀疑是文件描述符数量限制,但将相关声明移到循环外也无法解决问题。请问如何修改代码以支持接收无限数量的数据块?
问题分析与修复方案
核心问题拆解
- 内存泄漏:每次循环都用
calloc分配chunkBuf但未释放,3万次循环后内存占用超200MB,系统内存耗尽导致程序停摆。 select处理不完整:未处理select返回错误或超时的情况,可能导致后续数据接收逻辑被跳过。- 循环次数限制:
for循环最多仅执行10万次,无法实现无限接收。 - Winsock初始化缺失:Windows下必须调用
WSAStartup初始化套接字库,原代码遗漏了这一步,可能导致隐性错误。
修改后的完整代码
#include <winsock2.h> #include <ws2tcpip.h> #include <iostream> #include <string> #pragma comment(lib, "ws2_32.lib") int main() { // 初始化Winsock库(Windows下必填) WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup初始化失败" << std::endl; return 1; } std::string _ipaddress = "192.168.0.1"; int dport = 6342; SOCKADDR_IN dtarget; dtarget.sin_family = AF_INET; dtarget.sin_port = htons(dport); // 替换不安全的inet_addr,用inet_pton解析IP地址 if (inet_pton(AF_INET, _ipaddress.c_str(), &dtarget.sin_addr) != 1) { std::cerr << "IP地址格式无效" << std::endl; WSACleanup(); return 1; } SOCKET _data_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (_data_sock == INVALID_SOCKET) { std::cerr << "创建套接字失败:" << WSAGetLastError() << std::endl; WSACleanup(); return 1; } if (connect(_data_sock, (SOCKADDR*)&dtarget, sizeof(dtarget)) == SOCKET_ERROR) { std::cerr << "连接失败:" << WSAGetLastError() << std::endl; closesocket(_data_sock); WSACleanup(); return 1; } // 缓冲区移到循环外,重复使用避免内存泄漏 const int chunkSize = 8576; char* chunkBuf = (char*)calloc(chunkSize, 1); if (!chunkBuf) { std::cerr << "内存分配失败" << std::endl; closesocket(_data_sock); WSACleanup(); return 1; } // 无限循环接收,直到连接断开或出错 while (true) { fd_set read_flags; FD_ZERO(&read_flags); FD_SET(_data_sock, &read_flags); struct timeval waitd; waitd.tv_sec = 5; waitd.tv_usec = 0; // 补全微秒字段,避免select行为异常 int sel = select(_data_sock + 1, &read_flags, nullptr, nullptr, &waitd); if (sel == SOCKET_ERROR) { std::cerr << "select调用失败:" << WSAGetLastError() << std::endl; break; } else if (sel == 0) { // 超时处理,可根据需求调整(比如打印日志或继续等待) std::cout << "接收超时,继续等待..." << std::endl; continue; } if (FD_ISSET(_data_sock, &read_flags)) { int readCount = 0; int nread = 0; do { nread = recv(_data_sock, chunkBuf + readCount, chunkSize - readCount, 0); if (nread == SOCKET_ERROR) { std::cerr << "接收数据失败:" << WSAGetLastError() << std::endl; goto cleanup; } else if (nread == 0) { // 对方主动关闭连接 std::cout << "连接已被对方关闭" << std::endl; goto cleanup; } readCount += nread; } while (readCount < chunkSize); // 处理接收到的数据块 // do something with chunkBuf // 重置缓冲区(如果需要) memset(chunkBuf, 0, chunkSize); } } cleanup: // 统一清理资源 free(chunkBuf); closesocket(_data_sock); WSACleanup(); return 0; }
关键修改说明
- 解决内存泄漏:将缓冲区分配移到循环外,循环内重复使用,退出时统一释放。
- 完善Winsock生命周期管理:添加
WSAStartup初始化和WSACleanup清理,符合Windows套接字规范。 - 处理所有异常情况:覆盖
select错误、超时,recv错误、连接关闭等场景,避免程序无响应。 - 移除次数限制:用
while(true)实现无限接收,直到连接正常断开或出错。 - 优化IP解析:用
inet_pton替代inet_addr,支持更规范的IP地址格式且安全性更高。 - 补全
timeval字段:初始化tv_usec,避免select因未初始化字段产生异常行为。
内容的提问来源于stack exchange,提问作者Pommesgabel
相关产品推荐
相关产品推荐

