передача файлов boost.asio - не полностью перенесено? производительность отображения памяти? - PullRequest
0 голосов
/ 08 апреля 2020

В настоящее время я пытаюсь реализовать приложение для передачи файлов в linux, используя boost.asio. Я совершенно новичок в этой теме c (общее обучение cpp), в последние дни я пытался понять, как это может работать. Я уже схожу с ума.

Я добился определенного прогресса, но не могу полностью передать файл, вместо этого я просто получаю часть файла. Кто-нибудь знает, почему буфер не красный или записан полностью?

Я сделал это действительно просто, это просто серия команд, я буду реализовывать его объектно-ориентированный позже.

Во-вторых, я Интересно, есть ли другой способ отобразить файл в памяти более эффективно? Скажите, что кто-то хочет передать файл размером 2 ТБ?

Я использую этот двоичный файл для тестирования: 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;
}

1 Ответ

0 голосов
/ 08 апреля 2020

Проблема не в сокете, а в том, как вы пишете файл на сервере.

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

Приведенный выше фрагмент неверен, поскольку оператор << используется для ASCII, а не для двоичных данных!

Простым решением было бы заменить его следующим фрагментом:

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

Вторая часть вопроса, конечно, более сложная и требует большого изменения кода.

Суть в том, что вы не можете передать весь контент одновременно, но вы должны разделить передачу на маленькие кусочки.

Одним из решений может быть отправка небольшого заголовка с некоторой информацией, например, общим количеством байтов передачи. таким образом, сервер может читать чанк по чанку до тех пор, пока не будет завершена вся передача.

Сообщение содержит файл заголовка, содержащий общий размер сообщения, количество чанков. У каждого чанка есть небольшой заголовок, указывающий размер чанка или, например, индекс чанка, если вы хотите переключиться на UDP.

После фрагмента сервера

#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

Лучшее решение - превратить это в асин c код ... но это слишком много всего для начинающий!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...