【问题标题】:boost.asio transfer files - not completely transferd? - memory mapping performance?boost.asio 传输文件 - 没有完全传输? - 内存映射性能?
【发布时间】:2020-04-08 11:02:22
【问题描述】:

我目前正在尝试使用 boost.asio 在 linux 下实现一个文件传输应用程序。我对这个主题(一般学习cpp)完全陌生,过去几天我试图弄清楚这可能是如何工作的。我已经失去理智了。

我取得了一些进展,但我无法完全传输文件,而只是获取文件的一部分。有谁知道为什么缓冲区没有红色或完全写入?

我做的很简单,就是一串命令,后面我会面向对象实现。

其次我想知道是否有另一种方法可以更有效地将文件映射到内存中?假设有人要传输 2 tb 的文件?

我正在使用这个二进制文件进行测试:blah.bin

要成功构建它,您需要: g++ -std=c++17 -Wall -Wextra -g -Iinclude -Llib src/main.cpp -o bin/main -lboost_system -lpthread

服务器

//server
#include <boost/asio.hpp>
#include <iostream>
#include <fstream>

using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;

int main() {
  boost::asio::io_service io_service;

//listen 
  tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 3333));

//socket  
  tcp::socket socket_(io_service);

//waiting
  acceptor_.accept(socket_);

//read
  boost::asio::streambuf buf;
  boost::asio::read_until(socket_, buf, "\nend\n");
  auto data = boost::asio::buffer_cast<const char*>(buf.data());

  std::ofstream file("transferd.bin");
  cout << data;
  file << data;
  file.close();

//response
  boost::asio::write(socket_, boost::asio::buffer("data recived"));

  return 0;
}

客户

//client
#include <boost/asio.hpp>
#include <iostream>
#include <fstream>
#include <vector>

using namespace boost::asio;
using ip::tcp;
using std::string;
using std::cout;
using std::endl;
using std::vector;

const vector<char> fileVec(const std::string & fileName) {
    std::ifstream file(fileName, std::ios::in | std::ios::binary);
  vector<char> tempVec ((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
  file.close();
    return tempVec;
};

int main() {
  boost::asio::io_service io_service;

//socket
  tcp::socket socket(io_service);
//connection
  socket.connect(tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 3333));

//write to server
    auto vdata = fileVec("example.bin");
    vdata.push_back('\n');
    vdata.push_back('e');
    vdata.push_back('n');
    vdata.push_back('d');
    vdata.push_back('\n');
  boost::system::error_code error;
  boost::asio::write(socket, boost::asio::buffer(vdata), error);

//response from server
  boost::asio::streambuf receive_buffer;
  boost::asio::read(socket, receive_buffer, boost::asio::transfer_all(), error);
  const char* response = boost::asio::buffer_cast<const char*>(receive_buffer.data());
  cout << response;

  return 0;
}

【问题讨论】:

  • 一个小建议...永远不要返回 const vector&lt;...&gt; 这避免 RVO 并且没有多大意义...

标签: c++ linux boost-asio file-transfer memory-mapped-files


【解决方案1】:

问题不在于套接字,而在于您如何在服务器中写入文件。

std::ofstream file("transferd.bin");
cout << data;   // you cannot print binary data like this on the standard output!
file << data;
file.close();

上面的sn-p是错误的,因为&lt;&lt;操作符用于ASCII而不是二进制数据!

一个简单的解决方法是用以下 sn-p 替换它:

std::ofstream file("transferd.bin");
file.write(data, buf.size());

问题的第二部分当然更难,它需要大量更改代码。

关键是你不能一次传输所有的内容,但是你应该把传输分成小块。

一种解决方案是发送一个带有一些信息的小标头,例如总传输字节数,以便服务器可以逐块读取,直到整个传输完成。

消息有一个包含总消息大小、块数的头文件。每个块都有一个小标题,指示块大小或例如块索引,以防你想切换到 UDP。

跟随服务器sn-p

#include <array>
#include <boost/asio.hpp>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <vector>

using namespace boost::asio;
using ip::tcp;
using std::cout;
using std::endl;
using std::string;

struct MessageHeader {
  int64_t totalSize;
  int64_t chunkCount;
};

struct ChunkHeader {
  int64_t index;
  int64_t size;
};

MessageHeader parseHeader(const char* data) {
  MessageHeader header;
  memcpy(&header, data, sizeof(MessageHeader));
  return header;
}

ChunkHeader parseChunkHeader(const char* data) {
  ChunkHeader header;
  memcpy(&header, data, sizeof(MessageHeader));
  return header;
}

MessageHeader readHeader(tcp::socket& socket) {
  std::array<char, sizeof(MessageHeader)> buffer;
  boost::asio::read(socket, boost::asio::buffer(buffer));
  return parseHeader(buffer.data());
}

ChunkHeader readChunkHeader(tcp::socket& socket) {
  std::array<char, sizeof(ChunkHeader)> buffer;
  boost::asio::read(socket, boost::asio::buffer(buffer));
  return parseChunkHeader(buffer.data());
}

std::vector<char> readChunkMessage(tcp::socket& socket) {
  auto chunkHeader = readChunkHeader(socket);
  std::vector<char> chunk(chunkHeader.size);
  boost::asio::read(socket, boost::asio::buffer(chunk));
  return chunk;
}

int main() {
  boost::asio::io_service io_service;

  // listen
  tcp::acceptor acceptor_(io_service, tcp::endpoint(tcp::v4(), 3333));

  // socket
  tcp::socket socket_(io_service);

  // waiting
  acceptor_.accept(socket_);

  auto messageHeader = readHeader(socket_);

  for (auto chunkIndex = 0ll; chunkIndex != messageHeader.chunkCount; ++chunkIndex) {
    auto chunk = readChunkMessage(socket_);

    // open the file in append mode
    std::ofstream file("transferd.bin", std::ofstream::app);
    file.write(chunk.data(), chunk.size());
  }

  // response
  boost::asio::write(socket_, boost::asio::buffer("data recived"));

  return 0;
}

上述解决方案有缺点,因为一切都是同步的,如果客户端在传输过程中退出,服务器将被卡住:D

更好的解决方案是将其转换为异步代码......但对于初学者来说,一次太多了!

【讨论】:

  • 我不知道该怎么感谢你。我对你的回答很满意。现在,我可以更好地理解根本问题。通过您的示例,我可以轻松地进入 boost dev 。对于初学者来说这是相当复杂的:)!我要完成的工作还有很多工作要做,但这真的很完美!竖起大拇指!
  • 另一个建议...尝试很好地隔离不同的部分...例如,通常我有一个类Protocol 可以生成和解析数据包(使用parsemakeChunkMessage(...) 之类的方法, 类 Communication 使用 readsend 之类的方法。这样你就可以很好地分割代码的不同部分
  • 我也喜欢在有事情发生时在交流中加入一些事件。像communication.onChunkRead([] (Chunk chunk){ file.append(chunk);
  • @mikee 如果觉得回复有用请采纳
猜你喜欢
  • 1970-01-01
  • 2015-05-16
  • 1970-01-01
  • 2014-02-12
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 1970-01-01
  • 2017-06-08
相关资源
最近更新 更多