Странное поведение с использованием SO_SNDBUF на неблокирующем сокете TCP под windows - PullRequest
2 голосов
/ 15 марта 2011

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

Я создаю неблокируемый сокет TCP, устанавливаю SO_SNDBUF равным 1024, проверяю, что он настроен правильно, затем подключаюсь (пробовал до и после вызова для соединения без разницы).

Проблема в том, что, когда мое приложение фактически приходит и отправляет вызовы (отправка около 2 МБ), а не возвращает около 1024 байтов, вызов отправки, очевидно, принимает все данные и возвращает отправленное значение 2 МБ (что именно Я прошел в). Все работает должным образом (это HTTP PUT, я получаю ответ и т. Д.), Но в конечном итоге я отображаю в своем индикаторе выполнения загрузку, сидящую на уровне 100% в течение 30 секунд, после чего приходит ответ.

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

Ответы [ 3 ]

3 голосов
/ 16 марта 2011

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

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

Это означает, что код на самом деле будет сообщать о том, что 2 МБ отправлено, но это просто означает, что ваши 2 МБ данных были успешно записаны во внутренний буфер, и это не означает / не гарантирует, что данные уже были отправлены.

Я работал над подобными проектами с потоковым видео и tcp-связью, и эта информация где-то доступна на форумах MSDN и в Technet, но требует некоторого действительно подробного поиска того, как все это на самом деле работает.

2 голосов
/ 16 марта 2012

Настройка ничего не влияет на сетевую карту;это затрагивает буфер ядра.По умолчанию он равен 8 КБ как для отправки, так и для получения.

Причина поведения, которое вы видите, заключается в следующем: размер буфера отправки НЕ является пределом суммы, которую вы можете отправить за один раз, это «номинальное значение»." размер буфера.Это действительно влияет только на последующие отправки, когда в буфере все еще есть данные, ожидающие отправки.

Например:

  1. Установите буфер отправки на 101 байт
  2. Отправьте 10 байтов, это будет буферизовано
  3. Отправьте еще 10 байтов, это будет буферизовано
  4. ... продолжайте, пока в буфере не будет 100 байтов
  5. Отправьте еще 10 байтов

    На этом этапе WinSock использует некоторую логику, чтобы определить, принять ли новые 10 байтов (и сделать буфер 110 байтов) или блок.Я точно не помню поведение, но оно на MSDN.

  6. Отправить еще 10 байтов

Этот последний будет определенно заблокирован до некоторогобуферное пространство доступно.

Таким образом, по сути, буфер отправки имеет большой размер и:

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

Извините за неопределенность и отсутствие ссылок;Я немного тороплюсь, но случайно вспомнил эти детали из сетевого продукта, который я недавно написал.

2 голосов
/ 25 декабря 2011

Я наблюдал то же самое в Windows, используя неблокирующий канал Java.

Согласно http://support.microsoft.com/kb/214397

При необходимости Winsock может буферизовать значительно больше, чем размер буфера SO_SNDBUF.

Это имеет смысл; отправка инициируется программой на локальном компьютере, которая считается взаимодействующей, а не враждебной. Если у ядра достаточно памяти, нет смысла отклонять отправляемые данные; кто-то должен буферизовать его в любом случае. (Буфер приема для удаленной программы, которая может быть враждебной)

Ядро имеет ограничения на эту буферизацию отправляемых данных. Я делаю сокет сервера, и ядро ​​принимает не более 128 КБ на отправку; не похоже на 2 МБ в вашем примере, который предназначен для клиентского сокета.

Кроме того, согласно той же статье, только ядро ​​отправляет буфер 2; следующая неблокирующая отправка должна немедленно вернуться, сообщив 0 записанных байтов. Поэтому, если мы будем отправлять только небольшое количество данных каждый раз, программа будет ограничена принимающей стороной, и ваш индикатор прогресса будет работать нормально.

...