You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
}

核心问题分析

  1. 发送长度错误
    • 客户端发送文件路径时使用sizeof(rV),这获取的是std::string对象自身的内存大小(通常24/40字节,和编译器有关),而非字符串实际长度。导致仅发送了字符串对象的内部结构,而非完整路径内容。
  2. 接收逻辑不完整
    • GetReply函数单次recv后直接返回,未处理TCP分包情况。TCP是流式协议,单次recv可能仅收到部分数据,剩余数据会留在缓冲区,后续接收时和新数据混杂引发乱码。
  3. 字符串构造未指定长度
    • 用(std::string)recvbuf构造字符串时,默认以第一个\0为结束符。若缓冲区有残留数据或接收内容含提前空字符,会直接截断字符串。
  4. 依赖不可靠的延迟
    • 用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粘包/分包问题,建议采用长度前缀协议:

  1. 发送端先发送4字节的网络字节序整数(表示后续数据的长度)
  2. 接收端先读取这个长度值,再根据长度读取对应字节数的内容
  3. 此方式可精准区分每个数据包的边界,无需依赖空字符或延迟

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 20:30:46