как получить буфер неизвестного размера в асинхронном режиме boost :: asio - PullRequest
0 голосов
/ 28 ноября 2018

Я пытаюсь получить буфер неизвестного размера, часть моего кода выглядит как:

void Connection::asyncRead() 
{
    auto self(shared_from_this());
    socket_.async_read_some(boost::asio::buffer(buffer_out),
        [this, self](boost::system::error_code ec, std::size_t length)
    {
        OnRead(ec, length);
    });
}

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

1 Ответ

0 голосов
/ 28 ноября 2018

Если вы хотите отправлять / получать сообщения, которые не имеют фиксированного размера, вы можете использовать подход, при котором вы определяете заголовок вашего сообщения, например, с 4-байтовым полем для хранения размера содержимого вашего сообщения:

[header(4 bytes) to store the size of message][content of message]

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

Другой способ - выключение сокет (ниже псевдокод)

The receiving side             |  the sending side
--------------------------------------------------------------------------------
error_code ec;                 |
asio::read(sock,buf,ec) [2]    |
                               | prepare some buffer with unknown size
                               | string buf;
                               | buf += ...; // add data
                               | asio::write(sock,buf)
                               | sock.shutdown(socket_base::shutdown_send); [1]

, позвонив sock.shutdown() на отправляющей стороне [1], вы можете сообщить получающей стороне, что все сообщение было отправлено.Затем на принимающей стороне после прочтения сообщения [2] необходимо проверить состояние переменной ec error-code, является ли она boost::asio::eof.Если вы получите end-of-file, вы знаете, что сообщение завершено.Если ec отличается от eof, это означает, что произошла ошибка.

Начиная с версии повышения версии 1.66 вы можете использовать dynamic_buffer для хранения данных, она адаптирует строку или вектор в качестве буфера.Или вы можете рассмотреть streambuf для чтения нефиксированных буферов.


EDIT

Добавлено использование dynamic_buffer.В соответствии со ссылкой dynamic_buffer может использоваться в свободных функциях, таких как async_read, async_until, но не в async_read_some в качестве функции-члена сокета.Ниже приведены коды клиента и сервера:

Сервер:

using namespace boost;

struct Data  {
  std::shared_ptr<asio::ip::tcp::socket> sock;
  std::string buf; // buf is empty [1]
};

void readHandler (
    const boost::system::error_code& ec,
    size_t length,
    std::shared_ptr<Data> d) {
  std::cout << "readHandler" << std::endl;
  if (ec == boost::asio::error::eof)
  {
    // here we got the whole message
    std::cout << d->buf << std::endl;
  }
  else 
  {
    std::cout << "Error" << std::endl;
  }
}

int main() {
  try {
    asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(),9999);
    asio::io_service ios;
    asio::ip::tcp::acceptor acceptor(ios, ep); 

    std::shared_ptr<asio::ip::tcp::socket> sock{new asio::ip::tcp::socket(ios)};

    acceptor.accept(*sock);

    std::shared_ptr<Data> data(new Data);
    data->sock = move(sock);

    boost::asio::async_read (*(data->sock), asio::dynamic_buffer(data->buf), 
        std::bind(readHandler,std::placeholders::_1, std::placeholders::_2,data)); // [2]

    ios.run(); // wait until async_write is complete
  }
  catch (system::system_error &e) {
    std::cout << "error " << e.what() << std::endl;
  }
  return 0;
}

в [1] мы создаем пустой буфер как строковый объект, в [2] мы вызываем async_read для получения данныхиспользуя dynamic_buffer.Обработчик, переданный в async_read, вызывается, когда отправляющая сторона закрывает сокет на своей стороне.

Клиент:

using namespace boost;

int main() {
  try {
    asio::ip::tcp::endpoint ep(asio::ip::address::from_string("127.0.0.1"),9999);
    asio::io_service ios;
    asio::ip::tcp::socket sock(ios, ep.protocol());
    sock.connect(ep);

    std::string buf = "some message";
    for (int i = 0; i < buf.size(); ++i) {
       // synchronous function was used to make simpler code
       asio::write(sock,asio::buffer(buf.c_str()+i,1)); // send 1 char
       std::this_thread::sleep_for(std::chrono::seconds(1)); // delay 1 second
    }
    sock.shutdown(asio::socket_base::shutdown_send);
  }
  catch (system::system_error &e) {
    std::cout << "Error " << e.what() << std::endl;
  }
  return 0;
}

, как вы можете видеть, мы отправляем символ за символом из строки свторая задержкаПоэтому, когда вы запускаете сервер, а затем клиент, сервер должен получить сообщение целиком через ~ 12 секунд.async_read ожидает на сервере, пока не придет eof - он отправляется клиентом по вызову shutdown на сокете.

...