C++ WinSocket TCP文件发送偶发失败 加Sleep后正常问题排查咨询
问题根因
- 发送循环结构错误,短发送场景下数据丢失
TCP协议中send()返回值小于传入的长度是正常现象(内核发送缓冲区不足时就会返回已成功拷贝到内核的字节数,也就是短发送)。你当前的代码把fread()和send()都包在了内层do-while循环中,一旦出现短发送,下一轮内层循环会直接重新读取文件覆盖缓冲区,未发送完成的剩余数据直接丢失。 - 发送完成后未做TCP优雅关闭,内核缓冲区数据被丢弃
send()执行成功仅代表数据被拷贝到操作系统内核的发送缓冲区,不代表数据已经通过网络发送到对端。你在发送完最后一批数据后立刻执行WSACleanup()、退出程序,操作系统会直接丢弃还没来得及发送的内核缓冲区数据,直接发送RST强制断开连接,导致对端收不到完整数据。加Sleep或者调试运行时,停顿的时间足够内核把所有数据发送完成,因此问题消失。 - 类型与错误判断逻辑错误,无法捕获异常
send()返回值为int类型,失败时返回SOCKET_ERROR(值为-1),你用无符号size_t存储返回值,会导致-1被转换为极大正整数,test < 0的错误判断永远不会生效,发送异常无法被感知。fread()返回值为无符号size_t,bytes_read < 0的判断完全无效,无法捕获文件读取异常。fopen_s返回值的赋值逻辑存在运算符优先级错误,虽然不影响功能但属于不规范写法。
修复方案
- 拆分读取和发送逻辑:先读取一批文件内容到缓冲区,再单独循环发送这批内容,直到全部发完再读取下一批
- 所有数据发送完成后调用
shutdown(SendSocket, SD_SEND)触发TCP四次挥手,等待对端确认后再关闭socket - 修正变量类型和所有错误判断逻辑
修复后完整代码
#include <iostream> #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <iphlpapi.h> #include <string> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } SOCKET SendSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (SendSocket == INVALID_SOCKET) { perror("Error at socket: "); WSACleanup(); return 1; } struct sockaddr_in myAddr; memset(&myAddr, '\0', sizeof myAddr); myAddr.sin_family = AF_INET; myAddr.sin_port = htons(2000); inet_pton(AF_INET, "127.0.0.1", &(myAddr.sin_addr)); memset(myAddr.sin_zero, '\0', sizeof myAddr.sin_zero); if (connect(SendSocket, (struct sockaddr*)&myAddr, sizeof(myAddr)) < 0) { perror("Error at connect: "); closesocket(SendSocket); WSACleanup(); return 1; } char buffer[512]; const char fileName[] = "test.txt"; FILE* fd; errno_t openErr = fopen_s(&fd, fileName, "rb"); if (openErr != 0) { perror("File not opened:"); closesocket(SendSocket); WSACleanup(); return 1; } size_t bytes_read = 0; // 先读文件,再循环发送,读完为止 while ((bytes_read = fread(buffer, 1, sizeof(buffer), fd)) > 0) { size_t total_sent = 0; // 同一批数据反复发送直到发完 while (total_sent < bytes_read) { int send_len = send(SendSocket, buffer + total_sent, bytes_read - total_sent, 0); if (send_len == SOCKET_ERROR) { perror("Error send"); fclose(fd); closesocket(SendSocket); WSACleanup(); return 1; } total_sent += send_len; } } // 检查文件读取是否出错 if (ferror(fd)) { std::cout << "file read error" << std::endl; fclose(fd); closesocket(SendSocket); WSACleanup(); return 1; } fclose(fd); // 优雅关闭TCP连接,等待内核发送完所有数据 shutdown(SendSocket, SD_SEND); // 等待对端返回FIN确认所有数据接收完成 char dummy; recv(SendSocket, &dummy, 1, 0); closesocket(SendSocket); WSACleanup(); system("PAUSE"); return 0; }
内容的提问来源于stack exchange,提问作者Mevestoo
相关产品推荐
相关产品推荐

