boost::asio отправляет данные быстрее, чем получает по TCP. Или как отключить буферизацию

Я создал программу клиент/сервер, клиент запускает экземпляр класса Writer, а сервер запускает экземпляр класса Reader. Затем устройство записи будет асинхронно записывать DATA_SIZE байт данных в устройство чтения каждые USLEEP миллисекунд.

Каждый последующий запрос async_write от Writer выполняется только в том случае, если был вызван обработчик «при записи» из предыдущего запроса.

Проблема в том, что если Writer (клиент) записывает в сокет больше данных, чем Reader (сервер) может получить, это похоже на поведение:

  • Writer начнет запись в (я думаю) системный буфер, и даже если данные еще не были получены Reader, он будет вызывать обработчик «при записи» без ошибки.

  • Когда буфер заполнен, boost::asio больше не будет запускать обработчик «при записи», пока буфер не станет меньше.

  • Тем временем Reader все еще получает небольшие порции данных.

  • Тот факт, что Reader продолжает получать байты после закрытия программы Writer, кажется, подтверждает правильность этой теории.

Чего мне нужно добиться, так это предотвратить эту буферизацию, потому что данные должны быть «в реальном времени» (насколько это возможно).

Я предполагаю, что мне нужно использовать некоторую комбинацию параметров сокета, которые предлагает asio, например, no_delay или send_buffer_size, но я просто предполагаю, поскольку мне не удалось поэкспериментировать с ними.

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

ПРИМЕЧАНИЕ 1. До того, как я начал экспериментировать с асинхронными операциями в библиотеке asio, я реализовал тот же сценарий с использованием потоков, блокировок и asio::sockets и в то время не сталкивался с такой буферизацией. Мне пришлось переключиться на асинхронный API, потому что asio, похоже, не допускает временных прерываний синхронных вызовов.

ПРИМЕЧАНИЕ 2. Вот рабочий пример, демонстрирующий проблему: http://pastie.org/3122025< /а>

EDIT: я провел еще один тест, в своем ПРИМЕЧАНИЕ 1 я упомянул, что при использовании asio::iosockets у меня не было такой буферизации. Поэтому я хотел быть уверенным и создал этот тест: http://pastie.org/3125452 Оказывается, что < strong>событие буферизации с asio::iosockets, поэтому должно быть что-то еще, что заставило его работать гладко, возможно, более низкий FPS.


person Peter Jankuliak    schedule 04.01.2012    source источник
comment
Похоже на неэффективный сервер или медленную/перегруженную/неисправную сеть. Когда я получаю подобную проблему, я пытаюсь разделить проблему, запуская сервер и клиент на двух машинах, соединенных одним витым кабелем (без маршрутизаторов, без лишнего трафика), просто чтобы посмотреть, что произойдет.   -  person Martin James    schedule 04.01.2012


Ответы (1)


TCP/IP определенно предназначен для максимизации пропускной способности, поскольку целью большинства сетевых приложений является передача данных между хостами. В таких сценариях ожидается, что передача N байтов займет T секунд, и, очевидно, не имеет значения, если получатель немного медленнее обрабатывает данные. На самом деле, как вы заметили, в протоколе TCP/IP реализовано скользящее окно, которое позволяет отправителю буферизовать некоторые данные, чтобы они всегда были готовы к отправке, но оставляет получателю полный контроль над регулированием. Приемник может работать на полной скорости, изменять темп или даже приостанавливать передачу.

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

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

Если вам нужно быть очень близко, но в то же время вам все равно, будут ли пакеты потеряны из-за того, что старые данные пакетов будут заменены новыми данными, тогда UDP/IP может быть лучшей альтернативой. Однако, а) если у вас есть надежные требования к доставке, вы можете в конечном итоге заново изобрести часть колеса tcp/ip и б) иметь в виду, что некоторые сети (корпоративные брандмауэры) имеют тенденцию блокировать UDP/IP, разрешая трафик TCP/IP и c ) даже UDP/IP не будет точно работать в реальном времени.

person DXM    schedule 04.01.2012