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

基于boost::asio的文件传输问题:文件大小读取错误致循环无法终止

问题描述

使用boost::asio库从NFOServer向PC传输文件时遇到两个核心问题:

  • 客户端读取到的文件大小与服务器实际文件大小不符:服务器显示文件大小为2975744字节(约2.9MB),但客户端读取到的文件大小为12894362189字节;
  • 服务器发送完正确大小的文件后,客户端未触发EOF标志,接收循环无法终止。

客户端代码

void receive_file_content_over_tcp(const std::string& filePath, boost::asio::ip::tcp::socket& socket) {
    std::ofstream file(filePath, std::ios::binary | std::ios::trunc);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for receiving." << std::endl;
        return;
    }

    boost::asio::streambuf receive_buffer;
    boost::asio::streambuf fileSizeBuffer;
    boost::system::error_code error;

    std::size_t totalBytesReceived = 0;
    std::size_t fileSize = 0;

    // Receive file size
    boost::asio::read(socket, fileSizeBuffer, boost::asio::transfer_exactly(sizeof(std::size_t)), error);

    if (error) {
        std::cerr << "Failed to receive file size: " << error.message() << std::endl;
        return;
    }

    // Extract file size from the buffer
    std::istream fileSizeStream(&fileSizeBuffer);
    fileSizeStream.read(reinterpret_cast<char*>(&fileSize), sizeof(std::size_t));

    std::cout << "Receiving file. File size: " << fileSize << " bytes." << std::endl;

    // Receive file content
    while (totalBytesReceived < fileSize) {
        std::size_t bytesRead = boost::asio::read(socket, receive_buffer, boost::asio::transfer_at_least(1), error);

        if (error && error != boost::asio::error::eof) {
            std::cerr << "Error while receiving file content: " << error.message() << std::endl;
            return;
        }

        if (bytesRead > 0) {
            // Write the received data to the file
            totalBytesReceived += bytesRead;
            std::istream is(&receive_buffer);
            file << is.rdbuf();

            // Display progress
            std::cout << "Received: " << totalBytesReceived << " bytes out of " << fileSize << " bytes (" << (totalBytesReceived * 100 / fileSize) << "%)." << std::endl;
        }
    }

    std::cout << "File content received successfully. Total bytes received: " << totalBytesReceived << std::endl;
}

服务器端代码

void send_file_content_over_tcp(const std::string& filePath, boost::asio::ip::tcp::socket& socket) {
    std::ifstream file(filePath, std::ios::binary | std::ios::ate);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for sending." << std::endl;
        return;
    }

    // Get the file size
    std::size_t fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    // Create a buffer to hold the file content
    std::vector<char> buffer(fileSize);
    if (!file.read(buffer.data(), fileSize)) {
        std::cerr << "Failed to read file for sending." << std::endl;
        return;
    }
    std::cout << "File read successfully. File size: " << fileSize << " bytes." << std::endl;

    // Send the file content
    boost::system::error_code error;
    std::size_t bytesSent = boost::asio::write(socket, boost::asio::buffer(buffer), error);

    if (error) {
        std::cerr << "Error while sending file content: " << error.message() << std::endl;
    }
    else {
        std::cout << "File content sent successfully. Bytes sent: " << bytesSent << std::endl;
    }
}

客户端输出

Receiving file. File size: 12894362189 bytes.
Received: 512 bytes out of 12894362189 bytes (0%).

服务器端输出

File read successfully. File size: 2975744 bytes.
File content sent successfully. Bytes sent: 2975744

问题分析与解决方案

核心问题1:文件大小读取错误

服务器代码完全没有发送文件大小,但客户端却在尝试读取sizeof(std::size_t)字节作为文件大小,导致客户端读取到的是文件内容的前8字节(64位系统下),这就是文件大小显示异常巨大的直接原因。

核心问题2:接收循环无法终止

客户端错误认为需要接收超大量数据,而服务器仅发送了实际文件内容,之后未关闭连接写端,客户端会一直等待更多数据,无法触发EOF标志,循环无法终止。

修正方案

服务器端修改:先发送文件大小,分块发送文件内容

void send_file_content_over_tcp(const std::string& filePath, boost::asio::ip::tcp::socket& socket) {
    std::ifstream file(filePath, std::ios::binary | std::ios::ate);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for sending." << std::endl;
        return;
    }

    // 获取文件大小
    std::size_t fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    std::cout << "File read successfully. File size: " << fileSize << " bytes." << std::endl;

    boost::system::error_code error;
    // 先发送文件大小
    boost::asio::write(socket, boost::asio::buffer(&fileSize, sizeof(std::size_t)), error);
    if (error) {
        std::cerr << "Error while sending file size: " << error.message() << std::endl;
        return;
    }

    // 分块发送文件内容,避免大文件占用过多内存
    std::vector<char> buffer(8192);
    std::size_t totalBytesSent = 0;
    while (file.read(buffer.data(), buffer.size()) || file.gcount() > 0) {
        std::size_t bytesToSend = file.gcount();
        boost::asio::write(socket, boost::asio::buffer(buffer.data(), bytesToSend), error);
        if (error) {
            std::cerr << "Error while sending file content: " << error.message() << std::endl;
            return;
        }
        totalBytesSent += bytesToSend;
    }

    std::cout << "File content sent successfully. Bytes sent: " << totalBytesSent << std::endl;
    // 关闭socket写端,触发客户端EOF
    socket.shutdown(boost::asio::ip::tcp::socket::shutdown_send, error);
}

客户端修改:优化接收逻辑,避免缓冲区残留问题

void receive_file_content_over_tcp(const std::string& filePath, boost::asio::ip::tcp::socket& socket) {
    std::ofstream file(filePath, std::ios::binary | std::ios::trunc);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for receiving." << std::endl;
        return;
    }

    boost::system::error_code error;
    std::size_t totalBytesReceived = 0;
    std::size_t fileSize = 0;

    // 接收文件大小
    boost::asio::read(socket, boost::asio::buffer(&fileSize, sizeof(std::size_t)), error);
    if (error) {
        std::cerr << "Failed to receive file size: " << error.message() << std::endl;
        return;
    }

    std::cout << "Receiving file. File size: " << fileSize << " bytes." << std::endl;

    // 分块接收文件内容,每次最多接收8KB
    std::vector<char> buffer(8192);
    while (totalBytesReceived < fileSize) {
        std::size_t bytesToRead = std::min(buffer.size(), fileSize - totalBytesReceived);
        std::size_t bytesRead = boost::asio::read(socket, boost::asio::buffer(buffer, bytesToRead), error);

        if (error) {
            if (error == boost::asio::error::eof) {
                std::cerr << "Connection closed before receiving full file." << std::endl;
            } else {
                std::cerr << "Error while receiving file content: " << error.message() << std::endl;
            }
            return;
        }

        file.write(buffer.data(), bytesRead);
        totalBytesReceived += bytesRead;

        // 显示进度
        std::cout << "Received: " << totalBytesReceived << " bytes out of " << fileSize << " bytes (" 
                  << static_cast<double>(totalBytesReceived) / fileSize * 100 << "%)." << std::endl;
    }

    std::cout << "File content received successfully. Total bytes received: " << totalBytesReceived << std::endl;
}

额外说明

  1. 原服务器代码一次性将整个文件读入内存,大文件场景下会造成内存压力,修改后改为分块读取发送;
  2. 客户端原代码使用streambuf未清理,易引发数据残留问题,改为固定大小vector缓冲区更可靠;
  3. 服务器发送完成后调用shutdown关闭写端,确保客户端能触发EOF,避免无限等待。

内容的提问来源于stack exchange,提问作者Johannes W.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 23:35:59