Asio: лучший способ сохранить сообщение для трансляции - PullRequest
2 голосов
/ 17 февраля 2012

Я хочу создать буфер символов, записать в него, используя sprintf, а затем передать его нескольким вызовам async_write () (т.е. распространить его среди клиентов).Мой вопрос: какая структура данных лучше всего использовать для этого?Если есть компромиссы, то я предполагаю, что приоритеты для определения «наилучшего» будут следующими:

  1. меньше циклов ЦП
  2. ясность кода
  3. меньше использования памяти

Вот то, что у меня есть в данный момент, похоже, работает:

function broadcast(){
  char buf[512];
  sprintf(buf,"Hello %s","World!");
  boost::shared_ptr<std::string> msg(new std::string(buf));
  msg->append(1,0);   //NUL byte at the end

  for(std::vector< boost::shared_ptr<client_session> >::iterator i=clients.begin();
    i!=clients.end();++i) i->write(buf);
}

Тогда:

void client_session::write(boost::shared_ptr<std::string> msg){
  if(!socket->is_open())return;
  boost::asio::async_write(*socket,
    boost::asio::buffer(*msg),
    boost::bind(&client_session::handle_write, shared_from_this(),_1,_2,msg)
    );
}

ПРИМЕЧАНИЯ:

  • Типичное сообщениеразмер будет меньше 64 байт;размер буфера 512 - просто паранойя.
  • Я передаю NUL-байт, чтобы отметить конец каждого сообщения;это часть протокола.
  • msg должен превзойти мой первый фрагмент кода (требование asio), следовательно, использование общего указателя.

Я думаюЯ могу сделать лучше, чем это по всем моим критериям.Я задавался вопросом об использовании boost :: shared_array?Или создать asio :: buffer (обернутый в умный указатель) прямо из моего char buf [512]?Но чтение документации по этим и другим вариантам оставило меня перегруженным всеми возможностями.

Кроме того, в моем текущем коде я передаю msg в качестве параметра handle_write (), чтобы гарантировать, что умный указатель не будет освобожден, пока handle_write() достигнут.Это требуется, не так ли?

ОБНОВЛЕНИЕ: Если вы можете утверждать, что в целом это лучше, я открыт для замены sprintf на std::stringstream или аналогичный.Суть вопроса в том, что мне нужно составить сообщение и затем передать его, и я хочу сделать это эффективно.

ОБНОВЛЕНИЕ № 2 (26 февраля 2012 г.): я ценюнеприятные люди пошли публиковать ответы, но я чувствую, что никто из них не ответил на этот вопрос.Никто не опубликовал код, показывающий лучший способ, и не дал никаких цифр, чтобы поддержать их.На самом деле у меня создается впечатление, что люди думают, что нынешний подход так же хорош, как и сейчас.

Ответы [ 3 ]

3 голосов
/ 17 февраля 2012

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

Если вы планируете отправлять простой текстсообщения, вы могли бы просто использовать std::string и std::stringstream для начала, не нужно передавать массивы фиксированного размера.

Если вам нужно сделать больше двоичного / байтового форматирования, я бы, конечно, начал с заменымассив фиксированного размера вектором символов.В этом случае я бы также не стал сначала преобразовывать его в строку, а создал бы буфер asio непосредственно из байтового вектора.Если вам не нужно работать с предопределенным протоколом, еще лучшим решением будет использовать что-то вроде Protocol Buffers или Thrift или любую приемлемую альтернативу.Таким образом, вам не нужно беспокоиться о таких вещах, как порядковый номер, повторение, элементы переменной длины, обратная совместимость, ....

Трюк shared_ptr действительно необходим, вам нужно хранить данные, которыессылка где-то в буфере, пока буфер не будет использован.Не забывайте, что есть альтернативы, которые могут быть более понятными, например, хранить их просто в самом объекте client_session.Однако, насколько это возможно, зависит от того, как создаются ваши объекты сообщений;).

1 голос
/ 21 февраля 2012

Как я понял, вам нужно отправлять одни и те же сообщения многим клиентам.Реализация будет немного сложнее.

Я бы порекомендовал подготовить сообщение как boost::shared_ptr<std::string> (как рекомендуется @KillianDS), чтобы избежать дополнительного использования памяти и копирования с вашего char buf[512]; (это ни в коем случае не безопасно, вы не можете быть уверены, какПрограмма будет развиваться в будущем и будет ли этого потенциала достаточно во всех случаях).

Затем отправьте это сообщение каждому внутреннему клиенту std::queue.Если очередь пуста и записи не ожидают (для этого конкретного клиента используйте логический флаг, чтобы проверить это) - извлеките сообщение из очереди и async_write в сокет, передавая shared_ptr в качестве параметра в обработчик завершения (функтор)что вы передаете async_write).После вызова обработчика завершения вы можете получить следующее сообщение из очереди.shared_ptr Счетчик ссылок будет поддерживать сообщение до тех пор, пока последний клиент не отправит его в сокет.

Кроме того, я бы рекомендовал ограничить максимальный размер очереди, чтобы замедлить создание сообщения при недостаточной скорости сети.

РЕДАКТИРОВАТЬ

Обычно sprintf является более эффективным с точки зрения стоимости безопасности.Если производительность критична и std::stringstream является узким местом , вы все равно можете использовать sprintf с std::string:

std::string buf(512, '\0');
sprintf(&buf[0],"Hello %s","World!");

Обратите внимание, что std::string не гарантирует сохранение данныхв непрерывном блоке памяти, в отличие от std::vector (пожалуйста, исправьте меня, если это изменилось для C ++ 11).Практически во всех популярных реализациях std::string используется непрерывная память.В качестве альтернативы вы можете использовать std::vector в приведенном выше примере.

1 голос
/ 21 февраля 2012

Вы можете сохранить std::list<boost::shared_ptr<std::string> > в вашем объекте client_session, и client_session::write() сделать push_back() для него. Я думаю, что это умно избегает функциональности boost.asio, однако.

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