使用 boost::asio libary 进行文件传输

问题描述 投票:0回答:1

遗憾的是,当我尝试将文件从 nfoserver 传输到 PC 时,读取的文件大小不正确,并且永远不会结束 do while 循环。任何帮助将不胜感激!当发送了正确的文件大小但未达到 eof 时,它会停止,因为它从服务器读取了错误的文件大小。我真的不知道是什么原因造成的,因为我对 boost::asio 库或一般网络没有太多经验。这就是为什么任何帮助将不胜感激,这样我就可以更好地理解图书馆了:)

客户:

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

我希望它能够读取正确的文件大小并传输它,但输出如下:

接收文件。文件大小:12894362189 字节。

已接收:12894362189 字节中的 512 字节 (0%)。

服务器输出:

文件读取成功。文件大小:2975744 字节。

文件内容发送成功。发送的字节数:2975744 该文件大约有 2.9mb 大。

过去几天我尝试了很多东西,但它从未用 eof 标志打破循环。因此,如果您能帮助我,那会让我非常高兴!谢谢。

c++ tcp boost-asio
1个回答
0
投票

您永远不会在

send_file_contents
中发送尺寸。你为什么想读它?

即使您这样做,您也需要确保以可移植的方式发送它(例如,考虑到字节顺序),例如使用

 using NetworkSize = boost::endian::big_uint64_t;

我会简化发送,并包括尺寸:

static std::vector<char> read_file(path const& fspec) {
    std::ifstream file(fspec, std::ios::binary);
    return std::vector<char>(std::istreambuf_iterator<char>(file), {});
}

void send_file_content_over_tcp(path const& fspec, tcp::socket& socket) {
    auto const contents = read_file(fspec);
    std::cout << "File read successfully. File size: " << contents.size() << " bytes." << std::endl;

    // Send the file content
    NetworkSize fileSize = contents.size();

    std::vector<asio::const_buffer> payload{
        asio::buffer(&fileSize, sizeof(fileSize)),
        asio::buffer(contents),
    };
    error_code ec;
    auto       n = write(socket, payload, ec);

    std::cout << "Content bytes sent: " << n << " (" << ec.message() << ")\n";
}

现在您可以完全对称地接收它了:

void receive_file_content_over_tcp(path fspec, tcp::socket& socket) {
    error_code  ec;
    NetworkSize expected = 0;
    read(socket, asio::buffer(&expected, sizeof(expected)), ec);
    std::cout << "Expected file size: " << expected << " bytes (" << ec.message() << ")" << std::endl;

    if (!ec.failed()) {
        std::vector<char> data(expected);
        size_t actual = read(socket, asio::buffer(data), ec);

        std::cout << "Received " << actual << "/" << expected << ": " << ec.message();

        if (!ec || (actual == expected && ec == asio::error::eof)) {
            std::cout << "File content received successfully." << std::endl;
            std::ofstream(fspec, std::ios::binary | std::ios::trunc) //
                .write(data.data(), data.size());
        }
    }
}

可以测试其是否有效:Live On Coliru

int main() {
    asio::io_context ioc;

    std::thread server([ex = ioc.get_executor()] {
        auto s = tcp::acceptor(ex, {{}, 7878}).accept();
        send_file_content_over_tcp("main.cpp", s);
    });

    ::sleep(1); // make sure server is accepting
    {
        tcp::socket client(ioc);
        client.connect({{}, 7878});
        receive_file_content_over_tcp("main-clone.cpp", client);
    }

    server.join();
}

印刷:

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
md5sum *.cpp
File read successfully. File size: 2265 bytes.
Content bytes sent: 2273 (Success)
Expected file size: 2265 bytes (Success)
Received 2265/2265: SuccessFile content received successfully.
d47f7c90dfe3ca271f81ca203d53798e  main-clone.cpp
d47f7c90dfe3ca271f81ca203d53798e  main.cpp

流媒体

您的发送未进行流式传输,因此我认为这并不重要。如果是,请逐块阅读:Live On Coliru

std::ofstream ofs(fspec, std::ios::trunc);
// ofs.exceptions(std::ios::badbit | std::ios::failbit);

for (std::array<char, 1024> buf; expected && !ec;) {
    size_t actual = read(socket, asio::buffer(buf), ec);
    assert(actual <= expected);

    std::cout << "Received " << actual << "/" << expected << ": " << ec.message() << "\n";
    ofs.write(buf.data(), actual);

    expected -= actual;
}
if (ofs.good() && expected == 0 && (!ec || ec == asio::error::eof))
    std::cout << "File content received successfully." << std::endl;

印刷

g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
md5sum *.cpp
File read successfully. File size: 2342 bytes.
Content bytes sent: 2350 (Success)
Expected file size: 2342 bytes (Success)
Received 1024/2342: Success
Received 1024/1318: Success
Received 294/294: End of file
File content received successfully.
fc9f108bcee2fd7f711ab7a63d81baae  main-clone.cpp
fc9f108bcee2fd7f711ab7a63d81baae  main.cpp

streambuf
呢?

这不是适合这项工作的工具。您当然应该对所有读取使用单个缓冲区。如果你必须的话,我认为以下内容会很不错:

void receive_file_content_over_tcp(path fspec, tcp::socket& socket) {
    asio::streambuf buf;
    NetworkSize     expected = 0;

    error_code ec;
    auto       n = read(socket, buf, asio::transfer_exactly(sizeof(expected)), ec);
    std::memcpy(&expected, buf.data().data(), n);
    buf.consume(n);

    std::cout << "Expected file size: " << expected << " bytes (" << ec.message() << "), REMAINING "
              << buf.size() << std::endl;

    std::ofstream ofs(fspec, std::ios::trunc);
    // ofs.exceptions(std::ios::badbit | std::ios::failbit);

    while (expected && !ec) {
        size_t actual = read(socket, buf, asio::transfer_at_least(1024), ec);
        assert(actual <= expected);
        assert(buf.size() == actual);

        std::cout << "Received " << actual << "/" << expected << ": " << ec.message() << "\n";
        ofs << &buf;

        expected -= actual;
    }
    if (ofs.good() && expected == 0 && (!ec || ec == asio::error::eof))
        std::cout << "File content received successfully." << std::endl;
}
© www.soinside.com 2019 - 2024. All rights reserved.