Как я могу узнать, заполнен ли буфер сокета? - PullRequest
13 голосов
/ 26 ноября 2009

Как узнать, заполнен ли буфер сокета чтения или буфер сокета записи пуст?

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

ОБНОВЛЕНИЕ: Как насчет этого: я хотел бы получить обратный вызов или сигнал, когда либо буфер чтения сокета заполнен, либо буфер записи сокета пуст. Таким образом, я могу остановить обработку, чтобы позволить большему количеству ввода-вывода происходить на проводе, так как привязка ввода-вывода всегда является проблемой при отправке данных на провод.

Вызов select() - это то, как вы проверяете, есть ли в буфере чтения что-то. Не , когда он заполнен (я думаю).

Ответы [ 8 ]

12 голосов
/ 18 октября 2011

Я знаю, что это старая ветка, но для тех, кто наткнулся на это через поисковик, я отвечу на вопрос, так как на самом деле выше не было ответа.

Перед тем, как начать, преодолеть зависание системного вызова - вы не можете взаимодействовать с сетевыми стеками на основе ядра (* nix) без включения и выключения пространства ядра. Ваша цель должна состоять в том, чтобы понять особенности стека, чтобы вы могли извлечь максимальную пользу из своей системы.

Как определить, заполнен ли буфер сокета чтения

На эту часть ответили - нет, потому что это не то, как вы должны думать.

Если отправитель (плохо) фрагментирует свои кадры TCP (обычно из-за отсутствия буферизации маршалированных данных на выходе и отключения алгоритма Nagle с помощью TCP_NDELAY), ваша идея уменьшить количество системных вызовов, которые вы делаете, является хорошей идея. Подход, который вы должны использовать, включает установку «низкого водяного знака» для чтения. Сначала вы устанавливаете разумный размер буфера приема, устанавливая SO_RCVBUF с помощью setsockopt (). Затем прочитайте реальный размер буфера чтения с помощью getsockopt (), так как вы можете не получить то, что просили :) К сожалению, не все реализации позволяют вам снова читать SO_RCVBUF, поэтому ваш пробег может отличаться. Затем решите, сколько данных вы хотите представить для чтения, прежде чем захотите их прочитать. Установите SO_RCVLOWAT с этим размером, используя setsockopt (). Теперь файловый дескриптор сокета будет выбирать только для чтения, когда есть по крайней мере того количества данных, которые читаются для чтения.

или буфер сокета записи пуст?

Это интересный вопрос, поскольку мне нужно было сделать это недавно, чтобы убедиться, что каждый мой ADB MODBUS / TCP занял свои собственные кадры TCP, что требуется спецификацией MODBUS (@steve: управление фрагментацией - один раз, когда вы делаете нужно знать, когда буфер отправки пуст!). Что касается оригинального постера, я очень сомневаюсь, что он действительно этого хочет, и верю, что ему будет гораздо лучше знать размер буфера отправки до его запуска и периодически проверять объем данных в буфере отправки во время отправки, используя методы, уже описанные. Это позволит получить более детальную информацию о пропорции используемого буфера отправки, которую можно было бы использовать для более плавного регулирования производства.

Для тех, кто все еще интересуется, как (асинхронно) определять, когда буфер отправки пуст (если вы уверены, что действительно то, что вы хотите), ответ прост - вы устанавливаете низкий уровень отправки. водяной знак (SO_SNDLOWAT) равен размеру буфера отправки. Таким образом, дескриптор файла сокета будет выбираться как доступный для записи только тогда, когда буфер отправки пуст.

Не случайно мои ответы на ваши вопросы вращаются вокруг использования select (). Практически во всех случаях (и я понимаю, что сейчас я направляюсь на религиозную территорию!) Приложения, которым необходимо перемещать большое количество данных (внутри и между хостами), лучше всего структурировать как однопоточные конечные автоматы, используя маски выбора и цикл обработки, основанный на pselect (). В наши дни некоторые ОС (Linux, чтобы назвать одну) даже позволяют вам управлять обработкой сигналов, используя выбор файловых дескрипторов. Какая роскошь - когда я был мальчиком ...:)

Peter

12 голосов
/ 26 ноября 2009

Опрос файлового дескриптора с select и нулевым тайм-аутом - если select говорит, что он доступен для записи, буфер отправки не заполнен.

(Ох ... без системного вызова. Нет, нет.)

Добавление:

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

10 голосов
/ 26 ноября 2009

Вы можете попробовать ioctl. FIONREAD сообщает вам, сколько байтов доступно для чтения. Если он совпадает с размером буфера (который вы можете получить и / или установить с помощью другого вызова icotl), тогда буфер заполнен. Аналогично, если вы можете записать столько байтов, сколько размер буфера вывода, то буфер вывода будет пустым.

Я не знаю, насколько широко поддерживаются FIONREAD, FIONWRITE и SIOCGIFBUFS (или их эквиваленты). Я не уверен, что когда-либо использовал какой-либо из них, хотя у меня есть подлое чувство, что я по той или иной причине использовал похожую функциональность в Symbian.

Требуется ли в вызове режим ядра для вычисления, это зависит от платформы. Неопределенная попытка избежать системных вызовов - недопустимый метод оптимизации.

Базовый интерфейс сокетов в стиле BSD ничего не говорит о буферах чтения и записи. Когда имеет значение, пуст ли буфер отправки? Это, конечно, не означает, что все данные были получены в другой конечной точке сокета - они могут находиться где-то в каком-либо маршрутизаторе. Аналогично, заполнение «вашего» буфера чтения не гарантирует блокировку записи на другом конце.

Вообще говоря, вы просто читаете / пишете столько, сколько можете, и позволяете слою сокетов справляться со сложностью. Если вы видите много операций ввода-вывода, выполненных с крошечными размерами, возможно, есть проблема с производительностью Но помните, что потоковый сокет будет отправлять / получать пакет одновременно, содержащий блок данных. Если не установлен TCP_NODELAY, это не значит, что байты поступают на сетевую карту, и вы можете в конечном итоге сделать один вызов чтения на байт. Они поступают в виде пакетов, поэтому, скорее всего, они станут читаемыми одновременно, возможно, по 1 тыс. Раз за раз. Вы вряд ли сможете ускорить процесс, отложив чтение до тех пор, пока не станет много читать. Фактически, вы можете сделать это еще хуже, потому что к тому времени, когда буфер чтения вашей конечной точки заполнен, существует риск, что входящие данные будут отброшены, потому что их негде хранить, что приведет к задержкам и повторным отправкам.

3 голосов
/ 26 ноября 2009

Учитывая, что буфер ядра для сокетов находится в пространстве ядра, я сомневаюсь, что есть какой-либо способ запросить размер без системного вызова.
С syscalls вы можете попробовать recv с PEEK.

ret = recv(fd, buf, len, MSG_PEEK);

Даст сделать recv, но без очистки буфера.

2 голосов
/ 26 ноября 2009

Это невозможно без системного вызова. Но в чем проблема с системными вызовами?

0 голосов
/ 19 октября 2011

@ полыхать,

Linux и SO_RCVLOWAT

С уважением, мой опыт отличается от вашего. Начиная с FC5, я использую низкие значения водяного знака в буфере приема в Linux в продуктах, которые распространяют видео по IP (как UDP, так и TCP), поэтому я понимаю, насколько важно максимально использовать возможности сетевого стека. Фактически, Linux был одной из первых реализаций, которая позволила вам прочитать низкую отметку уровня воды (а некоторые до сих пор этого не позволяют). :)

Вы упоминаете poll () и select () как не соблюдающие SO_RCVLOWAT. Я использую pselect () столько, сколько себя помню, так что, возможно, проблема в select () и poll (). В любом случае, вы должны всегда использовать pselect () или ppoll (), где это возможно, в предпочтении старым вызовам, потому что они могут атомарно изменять маску сигнала программы, когда вы входите / выходите из вызова. Если вы понимаете, что это значит, вы поймете, почему это важно в коммерческом программном обеспечении. Если нет, то такое обсуждение будет оправдывать свою нить. :)

Peter

0 голосов
/ 18 октября 2011

Если вы выполняете read () в отдельном потоке, SO_RCVLOWAT может помочь заблокировать это чтение, пока в буфере не будет достаточно данных. К сожалению, poll () и select () игнорируют этот параметр сокета по крайней мере в Linux и всегда проверяют наличие одного байта.

0 голосов
/ 27 ноября 2009

Я думаю, что есть фундаментальная причина, почему ваш подход ошибочен / обречен. Система не хочет сообщать вам, когда буфер чтения заполнен / буфер записи пуст, потому что эти события указывают на разрыв в договоре между вами и системой. Если дело доходит до этой точки (особенно в направлении чтения), слишком поздно , чтобы обеспечить бесперебойную работу стека протоколов. Некоторые данные могут появиться, пока вы наконец решите прочитать буфер. Вы должны прочитать буфер до того, как он заполнится , вот и весь смысл буферизованного ввода-вывода.

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