Boost.Asio async_send вопрос - PullRequest
       0

Boost.Asio async_send вопрос

6 голосов
/ 17 марта 2011

Я использую Boost.Asio для серверного приложения, которое я пишу.

async_send требует от вызывающего абонента сохранять права собственности на данные, которые отправляются, до тех пор, пока данные не будут успешно отправлены.Это означает, что мой код (который выглядит следующим образом) потерпит неудачу, и это происходит, потому что data больше не будет действительным объектом.

void func()
{
    std::vector<unsigned char> data;

    // ...
    // fill data with stuff
    // ...

    socket.async_send(boost::asio::buffer(data), handler);
}

Так что моим решением было сделать что-то вроде этого:

std::vector<unsigned char> data;

void func()
{        
    // ...
    // fill data with stuff
    // ...

    socket.async_send(boost::asio::buffer(data), handler)
}

Но теперь мне интересно, если у меня будет несколько клиентов, нужно ли мне создавать отдельный вектор для каждого соединения?

Или я могу использовать этот единственный вектор?Если я смогу использовать этот единственный вектор, если я перезапишу содержимое внутри него, это испортит данные, которые я посылаю всем своим клиентам?

Ответы [ 7 ]

13 голосов
/ 18 марта 2011

Возможное исправление - использовать shared_ptr для хранения вашего локального vector и изменить подпись обработчика, чтобы получить shared_ptr, чтобы продлить срок действия data до завершения отправки (благодаря Тим за то, что указал мне):

void handler( boost::shared_ptr<std::vector<char> > data )
{
}

void func()
{
    boost::shared_ptr<std::vector<char> > data(new std::vector<char>);
    // ...
    // fill data with stuff
    // ...

    socket.async_send(boost::asio::buffer(*data), boost:bind(handler,data));
}
6 голосов
/ 18 марта 2011

Я решил похожую проблему, передав shared_ptr своим данным в функцию-обработчик. Поскольку asio удерживает функтор обработчика до тех пор, пока он не будет вызван, а хандер-функтор сохраняет ссылку shared_ptr, данные остаются распределенными до тех пор, пока к ним открыт запрос.

изменить - вот код:

Здесь объект соединения сохраняет текущий записываемый буфер данных, поэтому shared_ptr относится к объекту соединения, а вызов bind присоединяет функтор метода к ссылке на объект, а вызов asio поддерживает работу объекта. .

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

boost::asio::async_write(
    mSocket,
    buffers,
    mHandlerStrand.wrap(
        boost::bind(
            &TCPConnection::InternalHandleAsyncWrite,
            shared_from_this(),
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred)));

void TCPConnection::InternalHandleAsyncWrite(
    const boost::system::error_code& e,
    std::size_t bytes_transferred)
{
5 голосов
/ 18 марта 2011

Но теперь мне интересно, если у меня несколько клиентов, нужно ли мне создавать отдельный вектор для каждого соединения?

Да, хотя каждый вектор не обязательно должен быть вглобальный охват.Типичное решение этой проблемы - сохранить buffer в качестве члена объекта и связать функцию-член этого объекта с функтором, переданным обработчику завершения async_write.Таким образом, буфер будет сохраняться в области действия в течение всего времени асинхронной записи.Примеры asio замусорены этим использованием функций-членов привязки, использующих this и shared_from_this.В общем, предпочтительнее использовать shared_from_this для упрощения времени жизни объекта, особенно в грани из io_service:stop() и ~io_service().Хотя для простых примеров этот каркас часто не нужен.

Описанная выше последовательность уничтожения позволяет программам упростить управление ресурсами с помощью shared_ptr <>.Если время жизни объекта привязано к времени жизни соединения (или некоторой другой последовательности асинхронных операций), shared_ptr объекта будет привязан к обработчикам для всех асинхронных операций, связанных с ним.

Хорошее место для начала - это асинхронный эхо-сервер из-за его простоты.

boost::asio::async_write(
    socket,
    boost::asio::buffer(data, bytes_transferred),
    boost::bind(
        &session::handle_write,
        this,
        boost::asio::placeholders::error
    )
);
3 голосов
/ 18 марта 2011

Способ, которым я занимался, заключается в том, чтобы по-настоящему принять концепцию «TCP - поток». Таким образом, у меня есть boost::asio::streambuf для каждого соединения, чтобы представлять то, что я отправляю клиенту.

Как и большинство примеров в boost, у меня есть класс tcp_connection с объектом на соединение. У каждого есть член boost::asio::streambuf response_;, и когда я хочу отправить что-то клиенту, я просто делаю это:

std::ostream responce_stream(&response_);
responce_stream << "whatever my responce message happens to be!\r\n";

boost::asio::async_write(
    socket_,
    response_,
    boost::bind(
        &tcp_connection::handle_write,
        shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));
2 голосов
/ 17 марта 2011

Крит объяснил повреждение данных, поэтому я дам вам предложение по реализации.

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

2 голосов
/ 17 марта 2011

Вы не можете использовать один вектор, если не отправляете одинаковые и постоянные данные всем клиентам (например, сообщение-подсказку).Это вызвано природой асинхронного ввода-вывода.Если вы отправляете, система сохранит указатель на ваш буфер в своей очереди вместе с некоторой структурой пакета AIO.Как только это будет сделано с некоторыми предыдущими операциями отправки в очереди, и в собственном буфере будет свободное место, система начнет формировать пакеты для ваших данных и скопировать куски вашего буфера в соответствующие места в кадрах TCP.Поэтому, если вы по ходу измените содержимое буфера, вы повредите данные, отправленные клиенту.Если вы получаете, система может оптимизировать его еще больше и направить ваш буфер в NIC в качестве цели для работы DMA.В этом случае значительное количество циклов ЦП может быть сохранено при копировании данных, поскольку это выполняется контроллером DMA.Однако, вероятно, эта оптимизация будет работать, только если сетевая карта поддерживает аппаратную разгрузку TCP.

ОБНОВЛЕНИЕ: В Windows Boost.Asio использует перекрывающийся WSO IO с уведомлениями о завершении через IOCP .

0 голосов
/ 18 марта 2011

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

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

Просто мысль.

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