Меню Рубрики

Windows udp buffer size

Максимальный размер входного буфера UDP-сокета

У меня возникли очередные вопросы по сокетам.
Если на сокет отправляется большое количество пакетов с разных клиентов, то данный буфер может быстро переполнится. По умолчанию, вроде как, он ставится равным 8Кб (всего-навсего).
Например, в C# c помощью функции ReceiveBufferSize можно задать размер входного буфера для UDP-сокета.

1. Какой максимальный размер буфера можно задать для UDP-сокета?
2. Существует ли ограничения ОС на выделяемый размер памяти для буфера сокета? Прочитал на форумах, что кто-то выставляет размер в 100 МБ (. ) и все работает, у кого-то и с 10МБ происходят ошибки.
3. Где-то читал, что нужно быть аккуратным с такими вещами, чтобы размер буфера не превысил размер памяти, которая выделена для приложения, и нужно за этим следить. Правда это или нет? Если да, то как выделить дополнительную память для приложения?

P.S. Речь идет о внешнем буфере сокета, куда складываются пакеты, откуда уже мы их считываем. При переполнении этого буфера пакеты просто пропадают. Я так понимаю, что даже исключения не появляются.
Почему вдруг возник такой вопрос — если клиентов много (16 или вообще 32), то может получиться так, что они разом пришлют свои пакеты и забьют буфер сокета, который не успеет все прочитать. Вариант создать для каждого клиента свой сокет в данный момент не подходит.

Благодарен за любую помощь!

Ramm
Тебе нужно посмотреть с какой скоростью твой сервер может разгребать входящие пакеты, и с какой скоростью тебе их шлют клиенты. Очевидно, что если сервер в среднем обрабатывает их медленнее, то увеличивать размер буфера бесполезно — он всё равно начнёт переполняться. Если же, как ты говоришь, может происходить пиковая нагрузка, то нужно посмотреть её величину и выставить соответствующий (с запасом) размер буфера. Размер UDP пакетов мал, и обычно большой буфер не требуется.

Но вообще, чаще такой вопрос решается именно оптимизацией сервера. Возможно тебе нужно просто быстрее доставать пакеты из буфера сокета и складывать в свою очередь на обработку. Эта очередь тоже может переполняться, но у тебя над ней полный контроль. И собственно обработку самих пакетов/сообщений нужно ускорять. Обязательно использовать асинхронную работу с сокетами, если таковая ещё не используется.

Alexander K
Да, именно пиковая нагрузка (разовая), при этом в документации указывается, что дефолтный размер буфера у сокета на C# — 8Кб, поэтому столкнуться с его разовым заполнением можно запросто.
Если я поставлю размер буфера в 8 Мб (8*1024*1024), то любая ОС справится с таким буфером? В т.ч. Линукс-подобные? Я еще где-то читал, что есть некий низкоуровневый буфер, куда пакеты будут заходить, если размер буфера установить в 0.
А что будет, если установить размер буфера в 80 Мб?
Alexander K
> асинхронную работу с сокетами,
Для каждого сокета я создаю свой поток.

Ramm
> Для каждого сокета я создаю свой поток
тебе хорошо расписал Alexander K, если вдруг ты читаешь сообщение, потом долго обрабатываешь его, например, лезешь в базу данных, и только потом готов читать новое, то это ни разу не асинхронная работа сколько бы тредов ни было : )

Sh.Tac.
Смотря как организовать работу, ведь можно считать и закинуть в некоторый массив через критическую секцию, а другой поток уже лезет в БД.
Ну хорошо, предположим, что поток считывает пакеты порциями, затем что-то еще делает, все равно надо чтобы буфер был достаточным для прихода десятков пакетов, 8Кб — это очень-очень мало!
Если выставить в 8Мб — работать всегда будет? И на всех ОС? А если в 80? =)
И почему дефолтный буфер так мал?
И какой вы выставляете размер буфера у UDP-сокетов?

Нужен суммарный размер буферов или размер буфера под одно сообщение?
Максимальный размер udp-дейтаграммы можно спросить через свойства сокета SO_MAX_MSG_SIZE. Он разный может быть, но не выше 64кб. Выделять больше нет смысла, если меньше — есть шанс получить только часть дейтаграммы.
Суммарный размер буферов действительно где-то в системе устанавливаться должен, не в приложении. Но там ни о каких 8кб речь не пойдет, другие порядки величин.

Zab
> Нужен суммарный размер буферов или размер буфера под одно сообщение?
Нужен размер буфера сокета в который эти сообщения приходят. Узнать и установить больше, если нужно.
Увидел функцию для его установки (на C#, правда для TCP):
https://msdn.microsoft.com/ru-ru/library/system.net.sockets.socke… ivebuffersize(v=vs.110).aspx
Опять же, кто-то говорит, что эта функция подойдет и для UDP.
И увидел эту тему:
https://stackoverflow.com/questions/2408212/how-can-i-set-the-buf… t-udp-c-sharp
И впал в ступор.

Тут язык вообще не важен, у сокета где-то надо размер этого внутреннего буфера увеличивать.

Для TCP размер буфера — только вопрос эффективности, которую ты почти наверняка понизишь, если будет играться с ней сам, без понимания что делаешь. В TCP нет сообщений, там поток байт, он по любому придет в том виде, как отсылался. Порежется как удобно системе и придет.

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

Ramm
> Если выставить в 8Мб — работать всегда будет? И на всех ОС? А если в 80? =)
Лучше опираться на свой собственный буфер, тогда и таких вопросов не будет. На потоках ты это можешь сделать так — один поток занимается исключительно тем, что читает пакеты из сокета и кладёт их в твой буфер — очередь на обработку. Другие потоки уже берут пакеты из этой очереди и обрабатывают их. Потом однажды перейдёшь на асинхронный IO.

Что касается же размера внутреннего буфера UDP сокета, то да, в Windows он вроде бы действительно 8кб по умолчанию, что мало, если ты шлёшь пакеты, скажем, по 1кб. Можно проверить, что пакеты теряются из-за этого, если они перестают теряться при увеличении размера внутреннего буфера, скажем до 1 мб. Синтетический стресс-тест в помощь.
Кроссплатформенность же тебе должен гарантировать фреймворк/библиотека, через который ты эти опции для сокета выставляешь.

Alexander K
Alexander K
> На потоках ты это можешь сделать так — один поток занимается исключительно
> тем, что читает пакеты из сокета и кладёт их в твой буфер — очередь на
> обработку.
И все равно не исключено, что может разом придти более 8 Кб.
Zab
> Можно проверить, что пакеты теряются из-за этого, если они перестают теряться
> при увеличении размера внутреннего буфера, скажем до 1 мб.
Буду тестить. Сегодня-завтра отпишусь.
Zab
> Для UDP ты обязан зарезервировать место по максимуму под одно сообщение, иначе
> ты его не получишь.
Да, это так, но сейчас речь о другом буфере, у сокета есть внутренний буфер, и он не безграничен. Если отправить 1000 пакетов на слушающий UDP сокет и не прочитать их — сколько поместятся? И вообще, куда они складываются? Они складываются во внутренний буфер сокета.
Я так понимаю.
Причем там еще куча тонкостей.
Вот я и задался целью изменить его размер, т.к. 8 кб — этого мне не хватит.

Ramm
> Если отправить 1000 пакетов на слушающий UDP сокет и не прочитать их — сколько
> поместятся? И вообще, куда они складываются? Они складываются во внутренний буфер сокета.
Нет никакого внутреннего буфера сокета, есть ограничение на количество буферов в операционной системе.

> Вот я и задался целью изменить его размер, т.к. 8 кб — этого мне не хватит.
8кб — это похоже ограничение чисто C#, на С++ его нет. С операционной системой оно не связано, это хранение полученного буфера языковыми средствами, как я думаю. Если это так, можно менять его на 64к, больше все равно не будет никогда. Еще лучше — поменять на значение свойства SO_MAX_MSG_SIZE, сам уж посмотри, как до этого свойства из C# добраться, должно быть протянуто, по идее.

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

> Нет никакого внутреннего буфера сокета
Что по твоему тогда делает опция SO_RCVBUF и настройка net.core.rmem_max в линуксе?

Ghost2
> Что по твоему тогда делает опция SO_RCVBUF
А это как раз вредительская опция, позволяющая узнать размер конкретного пришедшего пакета, если я не перепутал. Вопрос только, зачем тебе знать длину, если сам пакет ты после этого получить не сможешь, он уничтожается при запросе свойств.

Итак, в ходе опытов было выяснено, что размер буфера составляет заявленные 8Кб (не обманули в msdn =))
Как проводились опыты: отправляю на сервер сообщение с просьбой отправить мне N пакетов по M байт.
Затем ставлю поток на паузу (Sleep), на несколько секунд, чтобы быть уверенным, что пакеты придут ВСЕ.
Сервер после получения просьбы сразу начинает отправку.
Я попросил отправить 100 пакетов по 1000 байт, через 3 секунды на сокете было всего 9.
Я попросил отправить 150 пакетов по 90 байт, через 3 секунды на сокете было всего 92.
Я попросил отправить 250 пакетов по 90 байт, через 5 секунд на сокете было всего 92.
Затем я сделал так у сокета (шарп):

Но! Если на 8-килобайтный буфер отправить пакет, например, в 12кб, то он пройдет, я где-то читал, что так и должно быть, именно поэтому я получал 9 пакетов по 1000 байт, а не 8, 92 пакета по 90, а не 91.
И в случае переполнения буфера никаких иключений не происходит.
Так что вот так.
А еще есть буфер для отправки))) Вот это песня. Но я не уверен, что его стоит трогать, т.к. и в стандартном состоянии он справляется с отправкой большого кол-ва данных, в моей ситуации — достаточного.
Zab
> Нет никакого внутреннего буфера сокета, есть ограничение на количество буферов
> в операционной системе.
Хотите сказать, если вы создадите сокет, я пришлю на него 100 тыщщ пакетов, и все эти сотни мегабайтов засядут у вас в ОЗУ? Или как?
П.С. Попробую призвать Dampire.

Источник

Windows udp buffer size

Спрашивающий

Вопрос

Все ответы

If you have to ask how high it can go, you are probably doing something wrong.

The default is 8192, which is probably too low for UDP, so be sure to raise it somewhat.

This page is not Windows-centric, but it might be a good read: http://www.29west.com/docs/THPM/udp-buffer-sizing.html

They give an equation:

Buffer Size = Max Latency * Average Rate

Make sure your buffer is at least that big. For example, if you plan to let data accumulate in the buffer no more than 5 seconds and data comes in at 2KB/sec, you need at least a 10KB buffer. (Be sure to include size of headers in the byte calculation.)

«The default is 8192, which is probably too low for UDP, so be sure to raise it somewhat.»

Is there a way to increase the buffer size at the windows kernel/registry level (similar to ‘sysctl -w net.core.rmem_max=8388608’ on a linux system)? We are receiving UDP packets

4000 per second/350 bytes each — and would like to have control over the receive buffer size however an API solution is not viable.

I know this is a .net forum but a ny help would be appreciated.

SocketException: Message too long

kindly reply if anybody knows

If you are sending a 10M message I would not use UDP/multicasdt I would instead use TCP. UDP will only send each packet once. If yo uare dropping packets then you should be using TCP which willsend packets multiple times until each packet is achnowledge.

Is you Multicast all in the same subnet? Is you multicast going through any routers? Make sure Multicast forwarding is turned on.

With multicast you should make sure you don’t have any routing loops. Duplicate packets won’t get forwarded. You also may have problems if fragmentation is turned on.

Источник

UDP и проблема доставки ответа


Ниже — перевод статьи о проблеме работы с udp в сетевых приложениях. Переводчик позволил себе сменить примеры: в исходном тексте другие сетевые адреса и код на ruby. В переводе использован простенький скрипт на перле. Суть проблемы и решение от этого не меняются.
Кроме того, местами добавлены мои комментарии (в скобках, выделены курсивом).
Картинка для привлечения внимания взята из текста замечательной книги «learnyousomeerlang.com»

Тяжкая работа лёгких протоколов

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

Для примера разберём ситуацию с получением ответа, когда UDP датаграмма с начальным запросом посылается на дополнительный IP адрес на интерфейсе (alias или secondary IP).
Есть интерфейс eth1:

Как обычно выглядит код для получения пакета по udp? Ну, echo сервер может выглядеть как-то очень похоже на то, что под катом:

Это достаточно простой скрипт на перле, который покажет от кого пришёл udp пакет, содержимое пакета и отправит этот пакет обратно отправителю. Проще некуда. Теперь запустим наш сервер:

Посмотрим, что он слушает:

И после этого подключимся с удалённой машины к нашему серверу по основному IP:

Как это выглядит в tcpdump’е на нашей машине (ну, или должно выглядеть):

Просто фантастика — я отправляю пакет и получаю пакет обратно. В netcat’е мы получаем обратно что бы мы не напечатали (смешной эффект, если печатать «стрелочки»).

А теперь тоже самое на вторичный адрес на том же интерфейсе:

Как это сумасшествие выглядит в tcpdump’е на этот раз:

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

То что происходит, на первый взгляд кажется полным бредом. Но на самом деле, это обычный дефект для протокола без установки сессии, такого как UDP. Видите ли, наш сокет слушает любой адрес, (пустой параметр LocalAddr при создании сокета передаётся системе как адрес вида «0.0.0.0», любой доступный, что заставляет сокет слушать на всех доступных адресах. И нет, я тоже не знаю, почему это так. Это не особенно интуитивное действие). Когда мы получаем пакет в нашем приложении с помощью socket->recv(), мы не получаем информации о том, на какой конкретно адрес был послан пакет. Мы лишь знаем, что операционная система решила, что пакет был для нас (вот вам и инкапсуляция). Всё что мы знаем, это откуда пакет пришёл к нам. И из-за того, что ядро не хранит никакой информации о соединениях для сокета (ядро штука логичная, просили без соединений — будет без соединений), когда приходит время отправлять пакет обратно, всё что мы можем сообщить это «куда» отправить пакет. (В перле это делается в неявном виде. С объектом $socket связаны адрес и порт отправителя датаграммы, так что в вызове send его указывать не надо).
Но настоящая мозголомка начинается, когда мы пытаемся проставить адрес отправителя в ответной датаграмме. Ещё раз: ядро не хранит никакой информации об отправителе или получателе, так как мы работаем без соединений. И посколько мы слушаем «любой» интерфейс, операционная система думает, что у неё есть карт-бланш на отправку пакета с того адреса, который ей «по душе». В линуксе, похоже, выбирается основной адрес того интерфейса, с которого пакет будет отправлен. (А на самом деле, адрес определяется в соответсвии с RFC1122, 3.3.4.2 «Multihoming Requirements», по таблице маршрутизации — примечание переводчика). Так что для распространнёного случая «один адрес — один интерфейс» — всё работает. Но как только дело доходит до менее распространённых ситуаций, начинают проявляться нюансы.
Решение в том, чтобы создавать сокеты слушающие конкретные адреса. И отправлять пакет с этих сокетов: ядро будет знать, с какого адреса вы хотите отправлять пакеты и всё будет отлично. Выглядит достаточно просто, ага? Ну и естественно, любое вменяемое сетевое приложение уже так делает, ага? Так что очевидно, что реализация UDP в Ruby просто дерьмо(в оригинале исходники на руби, — примечание переводчика;). Именно так я подумал в начале, и не виню вас, если вы подумали так же. Но пока РУБИкон войны с авторами Ruby’ового UDPSocket’а не перейдён, давайте проведём небольшой эксперимент с другими часто используемыми приложениями. Например, SNMPd. Демон из пакета net-snmpd в убунте подвержен той же проблеме, что и наше тестовое приложение выше. Не похоже, что это какие-то новые грабли, на которые только наступили и рассыпались кучей патчей для испрвалений.
Так что в целом, все страдают одной и той же «болезнью». Под «всеми» подразумевается «некоторые UDP сервера». Есть некоторое количество ПО, которое не подвержено подобной проблеме с алиасами на интерфейсах. На ум сразу приходит Bind и NTPd работает нормально, если запущен после того, как вы сконфигурировали все интерфейсы. В чём же разница? Разница в том, что эти сервисы несколько «умнее» и биндятся на все адреса в системе по отдельности. На примере bind’а:

Это очень круто и решает проблему. Исключение составляет ситуация, когда вы добавляете лишний алиас уже после того, как демон стартовал. Bind не подцепит новый адрес и вам придётся рестартовать сервер. Кроме того, это несколько усложняет код, так как вам приходится как ладить с кучей сокетов внутри программы (например, использовать select() вместо простого блокирования на попытке приёма.) В общем-то, никто не любит лишних сложностей, но с этой можно справится. Однако, настоящая проблема это правило «не добавляйте адресов после старта демона». Необходимость проверять, не добавилось ли в системе ip-адресов, и рестартовать службу после добавляения адреса станет настоящей проблемой.
Однако, есть некоторый workaround и для этой проблемы. Здесь мы вспомним про ntpd. Порты, которые слушает он, выглядят следующим образом:

NTPd слушает каждый адрес в отдельности и дополнительно слушает на любом адресе, доступном системе. Я не знаю точно, зачем это нужно. Если просто слушать на каждом адресе в отдельности, то всё будет хорошо, как в случае с байндом. Но если вы добавляете ещё один адрес на интерфейс после старта ntpd, то начинает проявляться та же самая проблема, что и в случае с udp-echo-сервером. Так что я не думаю, что слушание на «любом» интерфейсе даёт какой либо плюс. Однако, это заставляет ntpd вести себя несколько отлично от Bind’а: когда вы посылаете пакет на интерфейс, добавляенный после старта Bind’а, то он просто игнорирует вас (у него нет сокета, который бы слушал ваши запросы). Ntpd же пытается отправить ответ и страдает от проблемы неправильного адреса в ответах. (Зато можно менять primary адреса на интерфейсах и создавать новые интерфейсы, примечание переводчика).

На текущий момент, лучшим решением кажется следовать пути Bind’а и ntpd и слушать на всех адреса в отдельности с «фокусом» от ntpd: слушать дополнительно и на 0.0.0.0. При этом, если я получил пакет на 0.0.0.0, то надо запускать сканирования доступных в системе адресов и биндится дополнительно и на них. Это должно решить проблему.
Осталось только заставить это работать (и решить кучу проблем, которые наверняка вылезут на пути). Пожелайте мне удачи. Крики боли и мучений которые вы слышите (не имеет значение, где вы находитесь) наверняка мои.

UPD: в комментариях появилось интересное пояснение от Quasar_ru. Всё таки реализация UDP в скриптовых языках неоднозначна: на чистом С можно написать такое клиентское приложение, которое сможет принять ответ от сервера с другого адреса. Польза от такой реализации — спорная, но всё же реализация возможна.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

  • Windows ucun oyun yukle
  • Windows tweak guide ebook что это
  • Windows trust root ca
  • Windows trial version что это
  • Windows tracking disable tool