Некоторое время я сомневался стоит ли писать статью подобного типа. Во-первых, флеш-программистам она будет не очень полезна, т.к. она больше про яву, а опытные ява-программисты, мне кажется, знают все то, о чем я собираюсь написать. Но тем не менее это опыт, которым я хочу поделиться. Надеюсь, кому-то это будет полезным. Также, в этой части будет только теоретическое изложение советов и не будет самого кода сервера. Данная статья является лишь теоретической и не содержит в себе код, но тем не менее содержит много букв.
Я флеш-программист, и мой опыт работы с явой исчисляется где-то месяцем. Эта статья типа «написание Java Socket сервера глазами флеш-программиста».
Предыстория.
До того, как мы написали свой сервер в нашем проекте Doom Forge Game мы использовали готовое решение для мультиплеерных игр — Smart Fox Server. Сервер сделан хорошо, в нем много возможностей, но нам пришлось столкнуться с некоторыми проблемами в его работе, которые решались тяжелым общением с безинициативной службой поддержки и решение проблемы могло занимать не меньше месяца (если проблема вообще решалась). Так что опыт использования Smart Fox Server у меня был печальный.
Из ActionScript3 в Java
На самом деле, перейти из флеша на яву было очень просто. В java и флеше есть несколько отличий, которые запомнились лично мне (конечно, это не все отличия), но в общем они очень схожи.
Отличие 1. Переменные. Java — сначала тип, потом имя.
AS3:
var str:String = “Hello”;
Java:
String str = “Hello”;
Отличие 2. В Java есть перегрузка методов, а во флеше нет.
В Java можно сделать так (и это круто):
<br />
public class MyClass{<br />
public void find(int id){…};<br />
public void find(String name){…};<br />
}<br />
В AS3 приходится делать приблизительно так:
<br />
public class MyClass{<br />
public function findById(int id):void{…};<br />
public function findByName(String name):void{…};<br />
}<br />
Отличие 3. В Java нельзя сделать ссылку на функцию, а в AS3 можно.
В AS3 мы пишем так (и это круто):
<br />
public function connect():void{<br />
target.addEventListener(Event.CONNECT, handleConnect)<br />
}<br />
public function handleConnect(event:Event):void {<br />
…<br />
}<br />
В java приходится для каждого слушателя создавать отдельный класс:
<br /> IRequestHandler handler = new Handler();<br /> addRequestHandler(Request.PUBLIC_MESSAGE, handler);<br />
А dispatcher, зная интерфейс хенжлера, знает какую функцию нужно вызвать и что в неё передать:
<br />
public interface IRequestHandler {<br />
void handleRequest(String command, Object data, Session session);<br />
}<br />
Отличие 4. В Java можно указать какое исключение может сгенерировать метод:
void handleRequest(String command, Object data, Session session) throws Exception;
И нам прийдется обязательно обработать это исключение. И это круто. В AS3 мыможем либо прочитать документацию/asdoc либо только догадываться о наличии исключения.
Отличие 5. Многопоточность.
Конечно, вписывать это отличие в один ряд с предыдущими отличиями равносильно списку дел
«1) подписать контракт слияния компаний. 2) купить корм для рыбок».
На самом деле, это Огромное отличие. Суть его очень проста. Это выполнение кода в несколько параллельных потоков. Представьте, дорогие флешеры, что у вас в памяти не одна несколько запущенных флешек, только отисовуют они одну флешку и память у них общая. Т.е. в тот момент, пока в одном потоке вы перебираете список соперников, другой поток в это время добавляет или удаляет что-то из этого списка.
Если ещё проще – это как несколько людей в одной комнате(где люди это потоки, а предметы – это данные): пока Лена считает яблоки на столе, Ира в это время съедает парочку, и к моменту когда Лена досчитает яблоки, полученное значение не будет соответствовать реальности. Есть масса методов избежать проблем в подобных ситуациях — запретить Ире есть яблоки, пока они считаются; сделать так, чтобы яблоки, которые считает Лена и яблоки, которые есть Ира – это были разные яблоки и так далее. Также это может поразить кучу других проблем, к примеру deadlock: Ира ждет пока Лена досчитает яблоки, а Лена ждет пока Ира наестся в конце-то концов — и получается, что они блокируют друг-друга и никто ничего не делают. Но это очень обширная тема и её нужно изучать отдельно.
Начнем.
Итак, нам нужно сделать socket-сервер для чата. Первым делом я начал искать в просторах интернета и натолкнулся на ряд старей:
http://www.broculos.net/en/article/how-make-multi-client-flash-java-server
http://habrahabr.ru/blogs/java/69136/
— Супер! Как все просто! Это то что нужно, — подумал я и внедрил это в жизнь. Вроде все предельно просто. Эти статьи полезно изучить для того чтобы знать основы. Делаем. Все работает, работает отлично… до тех пор пока вы живете в мире без ошибок сети, без зла и насилия, и у вас только 2 подключенных пользователея. Как я понял позже, это пример сфирического коня в вакууме…
Что взять в дорогу. Мой выбор библиотек.
NIO
Первое, что я понял для себя – не стоит напрямую работать с сокетами. Если использовать готовое решение для работы с сокетами можно избежать кучи проблем с многопоточностью, deadlock, избавит от необходимости писать уже давно написанные вещи. Я выбрал Netty по рекомендации одного хорошего java-программиста, но не берусь утверждать, что это самый лучший вариант.
Логирование
Для логирования я использовал готовое и распространенное решение - loj4g.
База данных
Работа с базой данных является тривиальной задачей для явы. Хотелось бы лишь посоветовать использовать пул соединений. Я использовал DBCP.
Протокол
Для простоты будем использовать JSON формат.
Статистика сервера
RRD4j — это простая библиотека для работы с rrd-базой.
Суть работы с сокетами — элементарное объяснение (вдруг кто не знает).
Итак, что такое сокеты. Я думаю, тут можно применить метафору телефонных разговоров. Клиент «звонит» по номеру (ip) серверу. Между ними устанавливается связь и они могут обмениваться информацией. Но все что они могут передавать друг-другу это звук (В случае компьютера — текст, а точнее нолики и единички). По «телефону» может говорить как клиент так и сервер и каждый будет слышать другого. Если кто-то положит трубку или связь оборвется где-то на линии – столько не кричи — твой собеседник тебя не услышит. Сокеты работают точно также.
В чем отличие от http-протокола? На самом деле, когда вы открываете веб-страницу ваш браузер создает сокет-соединение и посылает серверу команду «дай мне такую-то страницу» (Делает он это в http формате). Сервер в ответ посылает текст страницы и по окончании закрывает соединение («кладет трубку»). Что увидеть это воочию можно открыть telnet, подсоединиться к любому сайту и послать комманду «GET /» . Именно так и работает ваш браузер.