Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Форум / Максимальный размер входного буфера UDP-сокета

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

Страницы: 1 2 3 Следующая »
RammПостоялецwww12 янв. 20188:48#0
Добрый день!

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

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

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

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

Alexander KПостоялецwww12 янв. 201811:40#1
Ramm
Тебе нужно посмотреть с какой скоростью твой сервер может разгребать входящие пакеты, и с какой скоростью тебе их шлют клиенты. Очевидно, что если сервер в среднем обрабатывает их медленнее, то увеличивать размер буфера бесполезно - он всё равно начнёт переполняться. Если же, как ты говоришь, может происходить пиковая нагрузка, то нужно посмотреть её величину и выставить соответствующий (с запасом) размер буфера. Размер UDP пакетов мал, и обычно большой буфер не требуется.

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

Правка: 12 янв. 2018 11:41

RammПостоялецwww12 янв. 201812:01#2
Alexander K
Да, именно пиковая нагрузка (разовая), при этом в документации указывается, что дефолтный размер буфера у сокета на C# - 8Кб, поэтому столкнуться с его разовым заполнением можно запросто.
Если я поставлю размер буфера в 8 Мб (8*1024*1024), то любая ОС справится с таким буфером? В т.ч. Линукс-подобные? Я еще где-то читал, что есть некий низкоуровневый буфер, куда пакеты будут заходить, если размер буфера установить в 0.
А что будет, если установить размер буфера в 80 Мб?
Alexander K
> асинхронную работу с сокетами,
Для каждого сокета я создаю свой поток...
Sh.Tac.Постоялецwww12 янв. 201812:25#3
Ramm
> Для каждого сокета я создаю свой поток
тебе хорошо расписал Alexander K, если вдруг ты читаешь сообщение, потом долго обрабатываешь его, например, лезешь в базу данных, и только потом готов читать новое, то это ни разу не асинхронная работа сколько бы тредов ни было : )
RammПостоялецwww12 янв. 201812:42#4
Sh.Tac.
Смотря как организовать работу, ведь можно считать и закинуть в некоторый массив через критическую секцию, а другой поток уже лезет в БД...
Ну хорошо, предположим, что поток считывает пакеты порциями, затем что-то еще делает, все равно надо чтобы буфер был достаточным для прихода десятков пакетов, 8Кб - это очень-очень мало!
Если выставить в 8Мб - работать всегда будет? И на всех ОС? А если в 80? =)
И почему дефолтный буфер так мал?
И какой вы выставляете размер буфера у UDP-сокетов?
ZabПостоялецwww12 янв. 201812:46#5
Нужен суммарный размер буферов или размер буфера под одно сообщение?
Максимальный размер udp-дейтаграммы можно спросить через свойства сокета SO_MAX_MSG_SIZE. Он разный может быть, но не выше 64кб. Выделять больше нет смысла, если меньше - есть шанс получить только часть дейтаграммы.
Суммарный размер буферов действительно где-то в системе устанавливаться должен, не в приложении. Но там ни о каких 8кб речь не пойдет, другие порядки величин.
RammПостоялецwww12 янв. 201813:13#6
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
И впал в ступор...

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

Правка: 12 янв. 2018 13:17

ZabПостоялецwww12 янв. 201813:24#7
Для TCP размер буфера - только вопрос эффективности, которую ты почти наверняка понизишь, если будет играться с ней сам, без понимания что делаешь. В TCP нет сообщений, там поток байт, он по любому придет в том виде, как отсылался. Порежется как удобно системе и придет.

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

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

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

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

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

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

Правка: 12 янв. 2018 15:47

Ghost2Постоялецwww12 янв. 201819:14#11
Zab

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

ZabПостоялецwww12 янв. 201819:25#12
Ghost2
> Что по твоему тогда делает опция SO_RCVBUF
А это как раз вредительская опция, позволяющая узнать размер конкретного пришедшего пакета, если я не перепутал. Вопрос только, зачем тебе знать длину, если сам пакет ты после этого получить не сможешь, он уничтожается при запросе свойств.
Alexander KПостоялецwww12 янв. 201820:11#13
Zab
+ Показать
RammПостоялецwww13 янв. 201811:39#14
Итак, в ходе опытов было выяснено, что размер буфера составляет заявленные 8Кб (не обманули в msdn =))
Как проводились опыты: отправляю на сервер сообщение с просьбой отправить мне N пакетов по M байт.
Затем ставлю поток на паузу (Sleep), на несколько секунд, чтобы быть уверенным, что пакеты придут ВСЕ.
Сервер после получения просьбы сразу начинает отправку.
Я попросил отправить 100 пакетов по 1000 байт, через 3 секунды на сокете было всего 9...
Я попросил отправить 150 пакетов по 90 байт, через 3 секунды на сокете было всего 92...
Я попросил отправить 250 пакетов по 90 байт, через 5 секунд на сокете было всего 92...
Затем я сделал так у сокета (шарп):
udp.ReceiveBufferSize=8*1024*1024;
Т.е. увеличил буфер сокета до 8Мб.
И о чудо, все пакеты стали сохраняться.
Я увеличил буфер до 80Мб, запросил отправку 30000 пакетов по килобайту, поставил задержку (паузу) на 25 секунд, в итоге через эти 25 секунд почти все 30000 прочитались.

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

Правка: 13 янв. 2018 11:41

Страницы: 1 2 3 Следующая »

/ Форум / Программирование игр / Сеть

2001—2018 © GameDev.ru — Разработка игр