Socket仅接收46字节数据求助:远程文件传输截断异常
Socket数据接收截断及乱码问题排查
问题现象
- 开发的个人远程文件浏览器,传输超过46字节的文件路径时,服务器接收内容会截断并出现乱码
- 客户端发送示例:
C:\Users\Test\NTUSER.DAT{53b39e88-18c4-11ea-a811-000d3aa4692b}.TM.blf(实际长度69) - 服务器接收结果:
C:\Users\Test\NTUSER.DAT{53b39e88-18c4-1■◄_y·⌂(仅接收46字节且乱码) - 双方缓冲区默认大小均为512字节
相关代码
客户端发送代码
//Gets Path From Server(recvbuf&recvbuflen buffer size = 512) std::string fldr = GetReply(ConnectSocket, recvbuf, recvbuflen); std::cout << "Reply: " << fldr << "\n"; //Gets Directory Struct using given path from server FileManager::Directory* dir = f.GetDir(fldr); //Sends initial size to iterate through on server side send(ConnectSocket, std::to_string(dir->TotalList.size()).c_str(), dir->TotalList.size(), 0); //dir->TotalList holds a vector of strings that contain paths in directory for (std::string rV : dir->TotalList) { //Sleep for 75ms, without it the data would be sent to fast Sleep(75); //Sends the elements in TotalList //had to use sizeof(rV) because (int)strlen would overwrite the buffer fsr send(ConnectSocket, rV.c_str(), sizeof(rV), 0); std::cout << "Sent: " << rV << " Size: " << (int)strlen(rV.c_str()) << "\n"; }
服务器接收代码
while (1) { //Send Command, memset(sndbuf, 0, sizeof(sndbuf) is called before loop std::cout << "\n\nEnter Folder: "; std::cin.getline(sndbuf, sizeof(sndbuf)); //DirList is a string vector holding all the previous paths that clients sent //CheckIfExists iterates through the vector and checks if sndbuf is a valid path //if false it restarts loop if (CheckIfExists(sndbuf, DirList)) { //if true sends path, again if I used (int)strlen(sndbuf) //instead of sizeof(sndbuf) it would cause it to overwrite //the buffer on client side, again idky send(cSock, sndbuf, sizeof(sndbuf), 0); //zeros out the buffer for the replys from client memset(recvbuf, 0, sizeof(recvbuf)); //Display reply loops recv and clears then adds new elements to vector DisplayReply(cSock, recvbuf, recvbuflen, &DirList); break; } else std::cout << "Folder Doesn't Exist..."; }
服务器DisplayReply代码
void DisplayReply(SOCKET cSock, char* recvbuf, int recvbuflen, std::vector<std::string>* list) { //Gets the # of paths client has int r = std::stoi(GetReply(cSock, recvbuf, recvbuflen)); std::cout << "Size: " << r << "\n"; //Again zeroing the buffer, then clearing the vector memset(recvbuf, 0, sizeof(recvbuf)); list->clear(); for (int i = 0; i < r; i++) { //Sleep for 75ms so all data sends properly Sleep(75); //Recieve replys from client, GetReply returns string //Using any other variable doesnt work either, all caps out at 46 bytes //ex const char* or char* std::string rV = GetReply(cSock, recvbuf, recvbuflen); std::cout << rV << " Size: " << (int)strlen(rV.c_str()) << "\n"; //Add path to vector using vector pointer list->push_back(recvbuf); } }
服务器GetReply代码
std::string GetReply(SOCKET cSock, char* recvbuf, int recvbuflen) { int iResult = 0; do { iResult = recv(cSock, recvbuf, recvbuflen, 0); if (iResult > 0) { return (std::string)recvbuf; } else { printf("recv failed with error: %d\n", WSAGetLastError()); return ""; } } while (iResult > 0); }
核心问题分析
- 发送长度错误
- 客户端发送文件路径时使用
sizeof(rV),这获取的是std::string对象自身的内存大小(通常24/40字节,和编译器有关),而非字符串实际长度。导致仅发送了字符串对象的内部结构,而非完整路径内容。
- 客户端发送文件路径时使用
- 接收逻辑不完整
GetReply函数单次recv后直接返回,未处理TCP分包情况。TCP是流式协议,单次recv可能仅收到部分数据,剩余数据会留在缓冲区,后续接收时和新数据混杂引发乱码。
- 字符串构造未指定长度
- 用
(std::string)recvbuf构造字符串时,默认以第一个\0为结束符。若缓冲区有残留数据或接收内容含提前空字符,会直接截断字符串。
- 用
- 依赖不可靠的延迟
- 用
Sleep(75)避免发送过快,本质是规避协议设计缺陷,无法从根本上解决数据边界问题。
- 用
修复方案
客户端发送代码修正
//Gets Path From Server(recvbuf&recvbuflen buffer size = 512) std::string fldr = GetReply(ConnectSocket, recvbuf, recvbuflen); std::cout << "Reply: " << fldr << "\n"; //Gets Directory Struct using given path from server FileManager::Directory* dir = f.GetDir(fldr); //Sends initial size to iterate through on server side std::string size_str = std::to_string(dir->TotalList.size()); send(ConnectSocket, size_str.c_str(), size_str.size(), 0); //dir->TotalList holds a vector of strings that contain paths in directory for (std::string rV : dir->TotalList) { // 使用rV.size()获取实际字符串长度发送 int send_len = send(ConnectSocket, rV.c_str(), rV.size(), 0); std::cout << "Sent: " << rV << " Size: " << rV.size() << " Sent Bytes: " << send_len << "\n"; }
服务器GetReply函数修正
std::string GetReply(SOCKET cSock, char* recvbuf, int recvbuflen) { std::string result; int iResult = 0; do { memset(recvbuf, 0, recvbuflen); iResult = recv(cSock, recvbuf, recvbuflen, 0); if (iResult > 0) { // 按实际接收字节数追加结果,避免空字符截断 result.append(recvbuf, iResult); } else if (iResult == 0) { printf("Connection closed\n"); break; } else { printf("recv failed with error: %d\n", WSAGetLastError()); return ""; } // 循环接收直到无数据(更可靠的方式是先接收长度再接收对应内容) } while (iResult == recvbuflen); return result; }
服务器发送路径代码修正
while (1) { memset(sndbuf, 0, sizeof(sndbuf)); std::cout << "\n\nEnter Folder: "; std::cin.getline(sndbuf, sizeof(sndbuf)); if (CheckIfExists(sndbuf, DirList)) { // 发送实际路径长度,而非整个缓冲区大小 std::string path_str(sndbuf); send(cSock, path_str.c_str(), path_str.size(), 0); memset(recvbuf, 0, sizeof(recvbuf)); DisplayReply(cSock, recvbuf, recvbuflen, &DirList); break; } else std::cout << "Folder Doesn't Exist..."; }
更可靠的协议升级建议
为彻底解决TCP粘包/分包问题,建议采用长度前缀协议:
- 发送端先发送4字节的网络字节序整数(表示后续数据的长度)
- 接收端先读取这个长度值,再根据长度读取对应字节数的内容
- 此方式可精准区分每个数据包的边界,无需依赖空字符或延迟
内容的提问来源于stack exchange,提问作者GenZKaren
相关产品推荐
相关产品推荐

