ВНИМАНИЕ! Наша конференция посвящена космической тематике и компьютерным играм. Политические вопросы и происходящие в мире события в данный момент на нашем сайте не обсуждаются!
|
» Higload UDP сервер. Архитектура. Варианты решения. | страница 1 |
 |
Страница 1 из 1 |
|
|
|
Канал Игры Мечты: «Higload UDP сервер. Архитектура. Варианты решения.» |
|
|
Варсик
545 EGP
    Рейтинг канала: 4(81) Репутация: 117 Сообщения: 4041 Откуда: Москва Зарегистрирован: 22.12.2002
 |
|
Сервер MMORPG/FPS игры в космосе. Рассматривается физический сервер мира.
Начал искать инфу по UDP и как его сделать скоростным и... ненашел.
Итак - базовый сервер пишется так (Win и Nix):
сокет, баинд, луп из recvfrom.
Для Windows я ненашел нормального объяснения - можно-ли из нескольких потоков делать recvfrom или нет. Однако WSARecv говорит что несколько вызовов их разных потоков приведут к непредсказуемому поведению. То есть - вроде-бы нет. А значит - надо городить отдельный поток на луп из recvcrfom и шедуллить полученные данные в другие потоки, тем самым убивая скорость. Или делать локи на ресурс, что так-же убивает скорость. Более того, почитав маны о libev (например тут) я нашел замечательный кусок:
Цитата: |
Early versions of winsocket's select only supported waiting for a maximum of 64 handles (probably owning to the fact that all windows kernels can only wait for 64 things at the same time internally; microsoft recommends spawning a chain of threads and wait for 63 handles and the previous thread in each. Great).
|
Да, сейчас уже есть IOCP и т.д., но все-равно... вдумчивое гугление скажет что IOCP все-равно не панацея. Погуглив еще я пришел к выводу (субъективному), что 100К клиентов на Windows не похендлишь. Оно тупо медленное.
Итак, огородились от Windows. Дальнейшее продумывание архитектуры сервера ограничило меня и от FreeBSD. Просто потому что у него нету CUDA. То есть драйверов для FreeBSD у nVidia с поддержкой CUDA нету.
Успокоившись и поняв что ОС будет Linux, а метод работы с ядром будет epoll - начал прикидывать архитектуру...
Основная мысль: CPU принимает UDP пакеты от игроков с их вводом и записывает это все в правильные участки памяти. По таймеру все это сгружается в GPU и там рассчитывается физика, коллижены, попадания и т.д. Результаты сгружаются в другую часть памяти и рассылаются клиентам и серверу игровой логики.
Собственно сейчас вопрос в том, как сделать наиболее эффективным 2 места:
1) Получить данные и положить в память
2) Послать данные.
Для начала первое.
Данные получаются через recvfrom по сигналу с epoll'а (вообще, скорее всего, будет libevent или libev, а может и нет ). Обойти recvfrom не получается, так как буфферезированного получения датаграмм еще ни у кого нету. Вроде-бы libevent 2.1 будет уметь, но и то не факт.
И тут опять получается интересная вещь... Если мы в цикле спамим recvfrom'ами, то потоки будут морозиться. И, как следствие, будем много переключений контекста. А это убийство производительности.
Самое эффективное, на мой взгляд, решение:
Создаем потоков ~= кол-ву ядер. Все эти потоки начинают спамить recvfrom, как только получили датаграмму - по отправителю нашли место, куда записать и заного спамим. По id из датаграммы это делать нельзя, так как подмена пакетов и левые люди мешают играть обычным людям. Проблемы будут только у NAT'еров, если за одним NAT'ом будет более 65К пользователей, что странно и вряд-ли будет.
Вопросы такие:
1) Это не воспаленный бред сумашедшего?
2) У кого-нибудь есть опыт написания таких серверов? Мне со стороны архитектуры хотя-бы...
3) Если у кого-то есть свежие мысли - выскажите.
добавлено спустя 34 минуты:
Пришла новая мысль...
Исходя из расчетов, проведенных в этой теме - можно на каждого клиента отвести свой порт. И сейчас мы это используем...
Проблема сервера в том, что при 100К клиентах надо искать соотношение IP/Port - Pointer, где IP/Port - коннект клиента, а Pointer - ссылка на его данные. Это может решаться или перебором или бинарным поиском и поддержанием списка упорядоченным или хешированием, двойным хешированием и т.д., а можно сделать финт ушами...
Если мы для каждого пользователя отдаем один порт на одном интерфейсе, то мы можем использовать номер этого порта в качестве id. А в данных прописать IP/Порт. Дело в том, что IP/Порт имеют размер 6 байтов, а Порт и N интерфейса - 2 байта порт + интерфейсов... Ну 4 максимум. То есть получается ~ 250К возможных значений. А на это можно тупо массив сделать. То есть получается прямое отображение. Конечно, потом еще надо будет проверить на IP/Порт, но это и так надо делать...
добавлено спустя 44 минуты:
Эм... А можно одним ударом прослушивать ВСЕ порты на определенном интерфейсе? ))
добавлено спустя 53 минуты:
Эм... В замешательстве... Как прослушать - нашел... либо libpcap либо
Код: |
socket(PF_PACKET,SOCK_PACKET,htons(ETH_P_ALL)); |
А как при этом посылать сообщения?
_________________ WARNING: By reading this post you accept that this post is genius.
Последний раз редактировалось: Варсик (07:48 16-01-2013), всего редактировалось 3 раз(а) |
|
|
VBKesha
817 EGP
      Рейтинг канала: 3(31) Репутация: 132 Сообщения: 3330 Откуда: обл.Брянская г. Трубчевск Зарегистрирован: 07.12.2005
 |
|
Варсик : |
Исходя из расчетов, проведенных в этой теме - можно на каждого клиента отвести свой порт. И сейчас мы это используем...
|
А что это вам даст? Какой смысл в куче портах на сервере?
Варсик : |
Проблема сервера в том, что при 100К клиентах надо искать соотношение IP/Port - Pointer, где IP/Port - коннект клиента, а Pointer - ссылка на его данные. Это может решаться или перебором или бинарным поиском и поддержанием списка упорядоченным или хешированием, двойным хешированием и т.д., а можно сделать финт ушами...
|
А что если при конекте присваивать ID пользователю, и заставлять его передавать его в каждом пакете, а на сервере держать связь между ID и IP:Port. Для 100К на ID достаточно 3х байт, и поиск имхо делать проще. Если сделать массив на все 3 байта длинной, и на 6+4(на указатель 4 байта хватит?) размерностью элемента то получается около 160 метров памяти что вроде как и жирно и ИМХО стразу решит все проблемы.
Варсик : |
А как при этом посылать сообщения?
|
Можно как минимум сделать ещё один сокет для отправки.
_________________ JumpGate. Quantar Optimus.
Jumpgate(TM) . . . welcome to your next life . . . There's nothing you can't do |
|
|
Варсик
545 EGP
    Рейтинг канала: 4(81) Репутация: 117 Сообщения: 4041 Откуда: Москва Зарегистрирован: 22.12.2002
 |
|
VBKesha : |
А что это вам даст? Какой смысл в куче портах на сервере?
|
О! Я сейчас расскажу... Если пользователю не присваивать свой порт, то надо делать поиск в таблице по IP/Порту. А если каждому свой, то по локальному порту можно сразу посчитать оффсет и проверить - тот-ли это пользователь. Нету поиска. Грубо говоря:
TablePointer + LocalPort * SizeOf(TableEntry). Это константное вычисление. А не поиск. И не надо держать хеши или упорядоченную таблицу.
VBKesha : |
А что если при конекте присваивать ID пользователю, и заставлять его передавать его в каждом пакете, а на сервере держать связь между ID и IP:Port.
|
Тот факт, что пакет может прилететь с левого коннекта, а так-же - это влияет на размер пакета. Я еще подумаю над тем, чтобы в пакет зерно меняющееся засунуть, чтобы обойти вариант, когда ты подделываешь пакеты.
VBKesha : |
Можно как минимум сделать ещё один сокет для отправки.
|
Нельзя. Сокет надо баиндить. А баинд задает порт. А конкретный порт - это проблема с NAT'ами (они создают временный туннель... То есть когда ты от себя посылаешь пакет на удаленный IP Порт, то они считают что лететь будет именно с этого порта, а не с другого. Так UDP NAT работает в большинстве случаев). А если баиндить 65К сокетов, то это слишком большая трата ресурсов.
_________________ WARNING: By reading this post you accept that this post is genius. |
|
|
VBKesha
817 EGP
      Рейтинг канала: 3(31) Репутация: 132 Сообщения: 3330 Откуда: обл.Брянская г. Трубчевск Зарегистрирован: 07.12.2005
 |
|
Варсик : |
Тот факт, что пакет может прилететь с левого коннекта, а так-же - это влияет на размер пакета. Я еще подумаю над тем, чтобы в пакет зерно меняющееся засунуть, чтобы обойти вариант, когда ты подделываешь пакеты.
|
Так в вашем способе всё остатся также только вместо ID выступает порт назначения? Да и 3 байта по моему вполне допустимая затрата.
Варсик : |
Нельзя. Сокет надо баиндить. А баинд задает порт. А конкретный порт - это проблема с NAT'ами
|
Ох вы что собрались сервер на 100к держать на сером IP за натом? Или я не понял и вы хотите слушать все порты на клиенте, тут я вообще тогда в замешательстве зачем это надо?
_________________ JumpGate. Quantar Optimus.
Jumpgate(TM) . . . welcome to your next life . . . There's nothing you can't do |
|
|
Варсик
545 EGP
    Рейтинг канала: 4(81) Репутация: 117 Сообщения: 4041 Откуда: Москва Зарегистрирован: 22.12.2002
 |
|
VBKesha : |
Так в вашем способе всё остатся также только вместо ID выступает порт назначения? Да и 3 байта по моему вполне допустимая затрата.
|
При посылке на сервер - да... Возможно вы правы... Я подумаю. Я как-то со стороны сервера все смотрю...
VBKesha : |
Ох вы что собрались сервер на 100к держать на сером IP за натом? Или я не понял и вы хотите слушать все порты на клиенте, тут я вообще тогда в замешательстве зачем это надо?
|
Нет... Вы немного не поняли... Когда клиент через NAT начинает гнать UDP нат считает что с сервера датаграмма прилетит с того-же порта, на которой улетела с локала. А так как у меня в текущей архитектуре каждому пользователь свой серверный порт, то надо делать 65К сокетов на каждый порт. А это ресурсы.
А надо это для того, чтобы не заниматься поиском связки IP/Порт в таблице пользователей. Опять-таки ID с клиента и меняющийся ключ в принципе должны спасти... Однако мне не нравится то, что это нагрузка на протокол. А он сейчас максимально сжатый.
_________________ WARNING: By reading this post you accept that this post is genius. |
|
|
Варсик
545 EGP
    Рейтинг канала: 4(81) Репутация: 117 Сообщения: 4041 Откуда: Москва Зарегистрирован: 22.12.2002
 |
|
VBKesha : |
Так в вашем способе всё остатся также только вместо ID выступает порт назначения? Да и 3 байта по моему вполне допустимая затрата.
|
Кстати... Спасибо за пинок... ID пользователя будет пересылаться с клиента на сервер. Заодно пошел в свой шит и увидел что там есть 4 байта свободных. Туда рандом и засунем.
_________________ WARNING: By reading this post you accept that this post is genius. |
|
|
|
|
|
Канал Игры Мечты: «Higload UDP сервер. Архитектура. Варианты решения.» |
|
К списку каналов | Наверх страницы |
Цитата не в тему: Глубокие мысли подобны колодцу: иные хотят напиться, иные же - плюнуть. (Pastor Schlagge)
|
» Higload UDP сервер. Архитектура. Варианты решения. | страница 1 |
|