【问题标题】:Writing simple file-transfer program using boost::asio. Have major send\receive desync使用 boost::asio 编写简单的文件传输程序。主要发送\接收不同步
【发布时间】:2025-12-24 12:55:06
【问题描述】:

我正在学习 boost::asio 网络编程,并尝试使用阻塞套接字进行简单的文件传输练习,因此遇到了奇怪的问题。

服务器(接收器)循环如下:

while (true){
    int bufSize{ static_cast<int>(pow(2, 18)) };
    char* buf{ new char[bufSize] };

    __int64 currPos{ 0 };
    __int64 fileSize;
    std::string fileName;
    mSocket->receive(buffer(buf, bufSize)); // here we get pre-defined packet with XML
    ParseDescXML(std::string{ buf }, &fileName, &fileSize); // and parse this XML, get name and size

    std::ofstream ofs(mSavePath + fileName, std::ios::binary);
    if (ofs.is_open()){
        while (currPos != fileSize) {
            if (bufSize > fileSize - currPos){
                delete[] buf;
                bufSize = fileSize - currPos;
                buf = new char[bufSize];
            }
            mSocket->receive(buffer(buf, bufSize));
            ofs.write(buf, bufSize);
            currPos += bufSize;
            std::cout << "SERVER " << currPos << std::endl;
        }
    }

    delete[] buf;
    ofs.close();
    slDebug("Receive completed"); // output some stuff, not related to issue
}

客户端(发送者)循环如下:

mWorkerOccupied = true;
std::ifstream ifs(filePath, std::ios::binary);

if (!ifs.is_open()){
    mWorkerOccupied = false;
    return false;
}

mFileName = filePath.substr(filePath.find_last_of('\\') + 1, filePath.length());
mCurrPos = 0;
mFileSize = GetFileSize(&ifs);

std::string xmlDesc{ MakeXMLFileDesc(mFileName, mFileSize) }; // here we make XML description
xmlDesc.push_back('\0');

int bufSize{ static_cast<int>(pow(2, 18)) };
char* buf{ new char[bufSize] };

mSocket->send(buffer(xmlDesc.c_str(), bufSize)); // and send it.

while (mCurrPos != mFileSize){
    if (bufSize > mFileSize - mCurrPos){
        delete[] buf;
        bufSize = mFileSize - mCurrPos;
        buf = new char[bufSize];
    }
    ifs.read(buf, bufSize);
    mSocket->send(buffer(buf, bufSize));
    mCurrPos += bufSize;

    std::cout << "CLIENT " << mCurrPos << std::endl;
}

ifs.close();
delete[] buf;
mWorkerOccupied = false;

slDebug("SendFile completed");

所有这些东西都在并行线程中运行。 据我了解,它应该以这种方式工作:

  1. 服务器线程运行服务器并挂起,直到传入连接(按预期工作,所以我没有在此处包含此代码)。
  2. 客户端线程在一段时间后运行并连接到服务器(按预期工作)
  3. 服务器等待第一个数据包,包含 XML(按预期工作)
  4. 客户端发送 XML,服务器获取它(按预期工作)
  5. 客户端开始发送实际的二进制数据,服务器得到它。这是我们遇到的主要问题。

我在客户端和服务器循环中都有文件当前位置的输出。 我希望它是这样的:

CLIENT 228 // first we send some data
SERVER 228 // Server gets it and outputs the same file pos

CLIENT 228
CLIENT 456
SERVER 228
SERVER 456

但我实际得到的 - 让我感到困惑......

SERVER 499384320
SERVER 499646464
CLIENT 88604672
SERVER 499908608
CLIENT 88866816
SERVER 500170752
SERVER 500432896
SERVER 500695040
SERVER 500957184

关于通过服务器接收东西的消息比关于发送的客户端消息要多得多。怎么可能?从字面上看,看起来客户端只发送了 80mb 的数据,而服务器已经收到了 500mb 的数据......我想,服务器线程应该等待接收(),因为我使用的是阻塞套接字,但这很奇怪。谁能解释一下,为什么我有这么大的不同步?

【问题讨论】:

    标签: c++ sockets boost-asio ifstream ofstream


    【解决方案1】:

    您假设receive 一次读取整个缓冲区大小,但不一定:

    The receive operation may not receive all of the requested number of bytes. Consider using
    the read function if you need to ensure that the requested amount of data is read before the
    blocking operation completes
    

    receive 返回读取的数据量,您应该将代码更改为:

    size_t recvd = mSocket->receive(buffer(buf, bufSize));
    ofs.write(buf, recvd);
    currPos += recvd;
    

    【讨论】: