提问人:Johannes W. 提问时间:11/16/2023 最后编辑:Johannes W. 更新时间:11/18/2023 访问量:59
使用 boost::asio 库进行文件传输
File transfer using the boost::asio libary
问:
可悲的是,当我尝试将文件从我的 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字节。
接收:512 字节(12894362189 字节 (0%)。
服务器输出:
文件读取成功。文件大小:2975744字节。
文件内容已成功发送。发送的字节数:2975744 文件大小为 ~2.9mb。
在过去的几天里,我尝试了很多东西,但它从未打破 eof 标志的循环。所以,如果你能帮助我,那会让我的一天变得非常愉快!谢谢。
答:
0赞
sehe
11/16/2023
#1
您永远不会发送大小。你为什么会期望阅读它?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());
}
}
}
这可以测试是否有效: 住在科里鲁
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;
}
评论
0赞
Johannes W.
11/18/2023
使用确切代码输出: 预期文件大小:8872762386475541857 字节(操作成功完成) 按任意键继续 . . .可悲的是,这也行不通。
0赞
sehe
11/18/2023
我可以说这不是我的确切代码,因为您可以在在线演示中看到它正在实时工作。您可以编辑现场演示以重现问题吗?如果您需要更大的文件,请考虑删除优化并使用 a.out 进行测试
0赞
sehe
11/18/2023
例如,内存中 coliru.stacked-crooked.com/a/12d941046e17da5a 或块流式 coliru.stacked-crooked.com/a/0fbe39205ab5baec
0赞
sehe
11/20/2023
自上次以来,你去过任何地方吗?
0赞
Johannes W.
11/23/2023
可悲的是,我现在使用不同的方法,从 https 服务器流式传输。
评论