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

ippsafx7  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(100)

可悲的是,当我试图从我的nfoserver传输文件到我的PC是读取文件大小不正确,永远不会结束做while循环.任何帮助将appriciated!它停止时,正确的文件大小已发送,但没有达到eof,因为它从服务器读取错误的文件大小.我真的不知道是什么原因导致这一点,因为我没有太多的经验与提升::asio libary或一般的网络。这就是为什么任何帮助都将非常感谢,所以我可以更好地理解libary:)
委托方:

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标志的循环。所以如果你能帮助我,这将使我的一天非常!谢谢。

wdebmtf2

wdebmtf21#

你从来不发送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呢?

这不是正确的工具的工作.你当然应该使用一个单一的缓冲区的所有读取.如果你必须,我认为以下would be decent

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

相关问题