Comments 41
Классно. Читал предыдущие части, когда еще не был на Хабре.
А какие у вас проблемы возникли с Идеей? Я использую xsbt 0.10.1 для сборки и идея плагин к нему, а в идею установил sbt консоль — хотя и не предел мечтаний но работать вполне себе можно.
А какие у вас проблемы возникли с Идеей? Я использую xsbt 0.10.1 для сборки и идея плагин к нему, а в идею установил sbt консоль — хотя и не предел мечтаний но работать вполне себе можно.
Да в общем то больших проблем нет… просто после нетбинса она как гента после убунты… )
В нетбинсе например создал проект, написал код, нажал «Скомпилить» и в папочке dist лежит jar файл, и тут же папочке libs аккуратно сложены все используемые библиотеки…
В идее чтобы получить jar файл надо артефакты какие-то создавать… В общем все руками создавать…
Да и вообще не логичная она какая-то для меня… Автообновляться не умеет, только сообщает что есть обновления и их надо руками скачивать и устанавливать…
Если доку по нетбинсу я вообще ни разу в жизни не открывал, там все как-то логично названо и по менюшкам раскидано. То в идее у меня сайт с докой постоянно был открыт. Те же артифакты ну нифига не логично названы… хотя это для меня все было не логично… может для других программеров это как раз и логично…
В нетбинсе например создал проект, написал код, нажал «Скомпилить» и в папочке dist лежит jar файл, и тут же папочке libs аккуратно сложены все используемые библиотеки…
В идее чтобы получить jar файл надо артефакты какие-то создавать… В общем все руками создавать…
Да и вообще не логичная она какая-то для меня… Автообновляться не умеет, только сообщает что есть обновления и их надо руками скачивать и устанавливать…
Если доку по нетбинсу я вообще ни разу в жизни не открывал, там все как-то логично названо и по менюшкам раскидано. То в идее у меня сайт с докой постоянно был открыт. Те же артифакты ну нифига не логично названы… хотя это для меня все было не логично… может для других программеров это как раз и логично…
sbt не работает с fsc. А без него нет инкрементивной компиляции. А без нее и свет не мил на скале)
В идее в два счета настраивается fsc и тогда процесс билда действительно становиться гораздо приятнее)
В идее в два счета настраивается fsc и тогда процесс билда действительно становиться гораздо приятнее)
Вы не правы. Fsc это всего лишь сервер, который держит внутри разогретые библотеки компилятора. Sbt точно так же не завершает процесс и держит эти библиотеки разогретыми, а также анализирует и при вызове compile перекомпилирует только изменённый файлы.
Не прав? В чем именно?
Я вроде и не говорил, что fsc что-то большее чем сервер. Однако он работает сам по себе. А вот sbt напротив — приходиться закрывать, чтобы перезапустить запущенное приложение. Понятно что он компилирует только измененные, однако не говорите ерунды — это совсем не тоже, что fsc.
И при чем тут разогретость? Этот термин больше применим к jvm машине. fsc же их просто держит в памяти и разогретость либ здесь ну никак не требуется. Достаточно того факта, что они уже находятся в памяти.
Поработайте с fsc и почувствуйте разницу. Sbt на сегодняшний день годиться разве что для автоматической подкачки недостающих либ.
Я вроде и не говорил, что fsc что-то большее чем сервер. Однако он работает сам по себе. А вот sbt напротив — приходиться закрывать, чтобы перезапустить запущенное приложение. Понятно что он компилирует только измененные, однако не говорите ерунды — это совсем не тоже, что fsc.
И при чем тут разогретость? Этот термин больше применим к jvm машине. fsc же их просто держит в памяти и разогретость либ здесь ну никак не требуется. Достаточно того факта, что они уже находятся в памяти.
Поработайте с fsc и почувствуйте разницу. Sbt на сегодняшний день годиться разве что для автоматической подкачки недостающих либ.
А вот sbt напротив — приходиться закрывать, чтобы перезапустить запущенное приложение.
Зачем каждый раз завершать sbt, чтобы перезапустить приложение?
Под разогретостью я имею ввиду, что класслоадеру после первой компиляции в том же процессе не нужно бегать и загружать библотеки компилятора по новой.
Спасибо за статью. Скажите, а правильно ли я понял, что описанный протокол никак не защищен от читеров и что сменить его в будущих версиях программы будет проблематично?
Начинающим важно понять суть обмена данными. А защита это уже следующий этап.
Да и опытным разработчикам зацикливаться на суперзащите не стоит. Лучше тратить время на что-то другое, более полезное, иначе программа будет долго находится в стадии разработки. А универсального решения нет. Это постоянно борьба между защитой и способами ее обхода.
Да и опытным разработчикам зацикливаться на суперзащите не стоит. Лучше тратить время на что-то другое, более полезное, иначе программа будет долго находится в стадии разработки. А универсального решения нет. Это постоянно борьба между защитой и способами ее обхода.
Я понимаю, что игра демонстрационная. Но на мой взгляд все же не помешало бы делать оговорку, что в реальных программах следовало бы добавить в протокол номер версии этого протокола, а также пару лишних проверок, чтобы игрок не мог прыгнуть с координаты (10,10) на (100500,100500).
1. SocketChannel.read() в общем не гарантирует Вам, что будет прочитанно именно 16 байт. Хотя это и маловероятно, но зависит от низлежащих сетевых протоколов, и полагнаться на это не стоит. Теоретически сетевой пакет может быть раздроблен на два. Поэтому надо читать в цикле, пока ByteBuffer не будет заполнен.
2. Точно также SocketChannel.write() не гарантирует, что все будет записано — зависит от состояния буфера. В общем случае пакеты нужно писать циклически и удалять, когда remaining=0. Если буфер заполнен, селектор автоматически не будет возвращать writeable-ключи до тех пор, пока данные из буфера не прососутся по каналу.
3. Несмотря на то, что buffer — это val, он будет создаваться каждый раз, как мы что-то читаем. Лучше его создать наверху один раз, используя при этом ByteBuffer.allocateDirect()
2. Точно также SocketChannel.write() не гарантирует, что все будет записано — зависит от состояния буфера. В общем случае пакеты нужно писать циклически и удалять, когда remaining=0. Если буфер заполнен, селектор автоматически не будет возвращать writeable-ключи до тех пор, пока данные из буфера не прососутся по каналу.
3. Несмотря на то, что buffer — это val, он будет создаваться каждый раз, как мы что-то читаем. Лучше его создать наверху один раз, используя при этом ByteBuffer.allocateDirect()
… хабр обрезал коментарий…
4. Не совсем уверен, что корректно делать Select в одном треде, а обрабатывать SelectionKey в другом (ClientHandler). Мне кажется keys, они валидны только до следующей операции select, посему обрабатываться должны в том же треде.
5. Ваш сервер не обрабатывает отсоединение клиентов. Именно тут начнутся танцы с бубном. Наперед скажу, что не гарантируется, что для каждого отвалившегося клиента селектор вернет key.isValid()=false. Поэтому дисконнект нужно делать по IOException.
Кроме того, есть клиенты, которые при потере связи не пошлют TCP CLOSE-WAIT, и на сервере они останутся висеть бесконечно долго. Timeout возникнет только, если в буфере есть что послать клиенту. Поэтому чтобы такого не произошло, нужно пинговать клиента.
4. Не совсем уверен, что корректно делать Select в одном треде, а обрабатывать SelectionKey в другом (ClientHandler). Мне кажется keys, они валидны только до следующей операции select, посему обрабатываться должны в том же треде.
5. Ваш сервер не обрабатывает отсоединение клиентов. Именно тут начнутся танцы с бубном. Наперед скажу, что не гарантируется, что для каждого отвалившегося клиента селектор вернет key.isValid()=false. Поэтому дисконнект нужно делать по IOException.
Кроме того, есть клиенты, которые при потере связи не пошлют TCP CLOSE-WAIT, и на сервере они останутся висеть бесконечно долго. Timeout возникнет только, если в буфере есть что послать клиенту. Поэтому чтобы такого не произошло, нужно пинговать клиента.
1,2,3,5 — Вы абсолютно правы. Но цель была показать общие принципы. Если сразу написать все как положено, то кода будет намного больше и новички могут просто не разобраться и будут тупо копипастить… Статьи, как я уже говорил, будут идти по принципу от простого к сложному и в следующих статьях будет оптимизация сервера. Добавится контроль сессий, поясню про Nagle алгоритм и т.д.
4 — Вполне корректная ситуация. keys в общем-то нужны только для того чтобы понять в каком канале и какие произошли события. А актор умеет накапливать сообщения. Поэтому проблем с обработкой в разных тредах не будет. Кстати довольно популярный SmartFoxServer именно так работает. Только у него не акторы (он на java) а 3 пула тредов фиксированного размера. На accept, read и write. На тесте у меня при 6000 сообщений в сек и 100 клиентах ни одно сообщение не потерялось…
4 — Вполне корректная ситуация. keys в общем-то нужны только для того чтобы понять в каком канале и какие произошли события. А актор умеет накапливать сообщения. Поэтому проблем с обработкой в разных тредах не будет. Кстати довольно популярный SmartFoxServer именно так работает. Только у него не акторы (он на java) а 3 пула тредов фиксированного размера. На accept, read и write. На тесте у меня при 6000 сообщений в сек и 100 клиентах ни одно сообщение не потерялось…
У меня игровой сервер. Держит каждый день 2500 юзеров, в целом 100 сообщений в секунду. Пиковая нагрузка была 5000+, сервер выдерживал без проблем. Я делаю все I/O в одном треде. Только после того как буфер с сообщением будет полностью прочитан, я передаю его пулу обработчиков. Как показывает практика, сама работа с I/O некритична и не жрет процессорное время, т.к. занимается исключительно тупой переброской данных между JVM и низлежащей ОС. Пытался одно время распараллелить I/O, вешая каналы на разные треды и селекторы, но под линуксом обнаружился очень странный эффект: он не давал открыть больше одного селектора на процесс, хотя под виндами все работало.
JVM под линухом построен не так как под windows.
Например тот же NIO под линухом сделан через epoll а в винде через winsocket. Они по разному работают. Возможно почитав про epoll будет понятно в чем причина.
Например тот же NIO под линухом сделан через epoll а в винде через winsocket. Они по разному работают. Возможно почитав про epoll будет понятно в чем причина.
И кстати… можно по подробнее узнать про ваш сервер. Просто 100 сообщений на 2500 юзеров маловато будет… игра пошаговая наверно?
www.buho21.com
Сообщений конечно же не 100, это я ошибся. 700 партий где-то по секунде-полторы на ход, то есть 500-700 сообщений в секунду. Обратный треффик больше: сообщение приходится реплицировать нескольким клиентам. В случае общего чата каждое сообщение или изменение графа отправляется всем пользователям.
Сервер разделен на 4 зала. Коннектор — отдельный процесс, который обслуживает все соединения с клиентами во всех игровых залах. Своего рода диспетчер пакетов и соединений. Интерфейс с серверами ввиде очереди сообщений через обычный Socket с блокирующим I/O. Сейчас, при 2000 пользователях он показывается в top с 7% CPU. Основное время тратится на шифровку/дешифровку пакетов.
Сообщений конечно же не 100, это я ошибся. 700 партий где-то по секунде-полторы на ход, то есть 500-700 сообщений в секунду. Обратный треффик больше: сообщение приходится реплицировать нескольким клиентам. В случае общего чата каждое сообщение или изменение графа отправляется всем пользователям.
Сервер разделен на 4 зала. Коннектор — отдельный процесс, который обслуживает все соединения с клиентами во всех игровых залах. Своего рода диспетчер пакетов и соединений. Интерфейс с серверами ввиде очереди сообщений через обычный Socket с блокирующим I/O. Сейчас, при 2000 пользователях он показывается в top с 7% CPU. Основное время тратится на шифровку/дешифровку пакетов.
Неужели после всех этих плясок не хочется использовать www.jboss.org/netty
Если вы читали статьи, то я везде говорю, что в реальных проектах лучше использовать проверенные временем решения. Для написания своих велосипедов должны быть веские причины.
Но есть один ньюанс… когда пытаешься нарисовать свой маленький велосипедик происходит процесс просветления и понимания… а это бесценно… ибо после этого абсолютно сознательно приходишь к использованию готовых библиотек типа netty.
Но есть один ньюанс… когда пытаешься нарисовать свой маленький велосипедик происходит процесс просветления и понимания… а это бесценно… ибо после этого абсолютно сознательно приходишь к использованию готовых библиотек типа netty.
Вобщем-то это был вопрос к всем комментаторам выше. Я чётко понимаю, что пьеса не об этом :)
Дело в том, что товарищ Throwable так чётко обсуждает NIO, как будто про Netty слыхом не слыхивал.
Дело в том, что товарищ Throwable так чётко обсуждает NIO, как будто про Netty слыхом не слыхивал.
На самом деле есть свои «за», но очень много «против». Но это уже тема отдельного разговора — что лучше использовать: готовые решения или свои собственные. Недостаток подобных библиотек в том, что они пытаются быть универсальными и покрыть как можно большее число потенциальных задач. Тогда как реальная задача требует всего одного-единственного решения, зато хорошо контролируемого и легко адаптируемого.
Навскидку я не уверен, что Netty мне обеспечит:
— контроль длины и валидности входящих сообщений
— лимитирование коннекшнов с одного IP (против DoS)
— blacklist IP и сеток
— heartbeat, и автоматическое определение времени ping-а
— flooding-контроль
— remote-контроль валидности клиентского кода
— отсоединение неактивных клиентов
— если внезапно понадобится еще что — допишу
Другой недостаток библиотек — это синдром «черного ящика», когда не ясно как чужой код ведет себя при определенных условиях. Требует экспериментальных проверок, а иногда уже на production выползают очень интересные «фичи». Ну и естественно зависимость от производителя…
И все-таки причиной создания «велосипеда» было то, что он был написан, когда появился NIO (1.4.1). Netty и подобных фреймворков и в помине не было. Раньше были танцы с бубном, когда надо было обойти blocking io.
P.S. Про netty кстати не знал, спасибо. Хотя уверен, это не единственное решение (напр. Apache Camel).
Навскидку я не уверен, что Netty мне обеспечит:
— контроль длины и валидности входящих сообщений
— лимитирование коннекшнов с одного IP (против DoS)
— blacklist IP и сеток
— heartbeat, и автоматическое определение времени ping-а
— flooding-контроль
— remote-контроль валидности клиентского кода
— отсоединение неактивных клиентов
— если внезапно понадобится еще что — допишу
Другой недостаток библиотек — это синдром «черного ящика», когда не ясно как чужой код ведет себя при определенных условиях. Требует экспериментальных проверок, а иногда уже на production выползают очень интересные «фичи». Ну и естественно зависимость от производителя…
И все-таки причиной создания «велосипеда» было то, что он был написан, когда появился NIO (1.4.1). Netty и подобных фреймворков и в помине не было. Раньше были танцы с бубном, когда надо было обойти blocking io.
P.S. Про netty кстати не знал, спасибо. Хотя уверен, это не единственное решение (напр. Apache Camel).
Спасибо, не знал про эту вещь.
Так TSP server или все же TCP?
(Не силён в Scala)
Подскажите, в addPlayerMsg мы защитили session с помощью lock.
Но, как мне кажется, в GameServer:Loop тоже нужны:
1)Синхронизация: sessions += channel -> actor
2)Барьер: val actor = sessions.get(channel).get.asInstanceOf[ClientHandler] — иначе поток может не увидеть изменений в sessions сделаных другим потоком.
Я прав? или в Scala есть какие-то дополнительные гарантии?
Подскажите, в addPlayerMsg мы защитили session с помощью lock.
Но, как мне кажется, в GameServer:Loop тоже нужны:
1)Синхронизация: sessions += channel -> actor
2)Барьер: val actor = sessions.get(channel).get.asInstanceOf[ClientHandler] — иначе поток может не увидеть изменений в sessions сделаных другим потоком.
Я прав? или в Scala есть какие-то дополнительные гарантии?
Вы правы. Пофиксю в исходниках. А Scala сама по себе не дает гарантий. Она дает лишь инструменты для решения.
Спасибо, буду ждать статью про разные методы синхронизации в играх: что такое интерполяция, эталонные состояния, etc.
Demo!!!
спасибо за пост
вставьте в 1ых двух статьях ссылки на эту часть
вставьте в 1ых двух статьях ссылки на эту часть
О как!
Прикольно увидеть скриншот из своей игры на хабре, хоть и не понятно зачем он тут :)
Прикольно увидеть скриншот из своей игры на хабре, хоть и не понятно зачем он тут :)
Sign up to leave a comment.
Пьеса «Разработка многопользовательской сетевой игры.» Часть 3: Клиент-серверное взаимодействие