Node Up and Running / Node В начале... (рабочее название): --- Строим Chat Server
| Английский оригинал | Перевод на русский | |
|---|---|---|
Сожалеем, но текст оригинала доступен только зарегистрированным пользователям. |
В мире который становится все более рперативным, нет ничего более оперативного чем чат. С чего же начать? Давайте начнм с TCP-based чат сервера который будет конектиться с Telnet. Это не только самое просто с чего можно начать, это еще и 100% будет написано на Node. jchouse 02.08.12 в 11:34 |
|
|
Первое, что надо сделать - это подключить библиотеки TCP из Node и создать новый TCP-сервер (смотри пр. 2.1). Randl 18.07.12 в 14:29 |
||
|
Пример 2.1 Создание нового TCP-сервера Randl 18.07.12 в 14:30 |
||
|
Во-первых, мы подключаем модуль net. Он содержит все необходимое для работы с TCP в Node. Теперь мы можем создать TCP сервер вызвав метод net.createServer(). Теперь, когда у нас есть сервер, мы хотим чтобы он что-то выполнял. Поэтому мы добавим слушатель событий при помощи метода on(). Когда произойдет событие подключения, слушатель событий вызовет функцию, которую мы назначили. Событие подключения происходит когда новый клиент подключается к серверу. spsmirnov 28.07.12 в 09:43 |
||
|
Событие подключения передает нам ссылку на TCP сокет нашего новго клиента, когда он вызывает нашу функцию обратного вызова. Мы назвали эту ссылку клиент. При вызове client.write (), мы можем отправлять сообщения на вновь подключенный клиент. Для начала скажем, "Привет!", а затем "Прощай", и вызываем метод client.end(), который закроет подключение. Это просто, и это начало нашего чат сервера. Наконец, нам нужно вызвать listen() чтобы Node знал какой порт слушать. Давай попробуем это. jchouse 02.08.12 в 11:49 |
||
|
Мы можем проверить наш новый сервер подлючением к Telnet, которая установлена на большинство операционных систем. Прежде всего, нам нужно запустить сервер зпуском Node c файлом. Затем нужно полдключить Telnet к localhost порту 9000, который мы указали в нашей Node программе. См. пр. 2.2. jchouse 02.08.12 в 12:09 |
||
|
Пример 2-2. Подключение к TCP серверу Node с помощью Telnet'а spsmirnov 28.07.12 в 09:38 |
||
|
Пока мы сделали сервер к которому может подключиться клиент и отправили ему сообщение перед тем как отключить его, но врядли это чат сервер. Давайте добавим еще несколько фишек. Во-первых, мы должны убедиться что мы можем получать сообщения от клиента, как показано в Примере 2.3 jchouse 02.08.12 в 12:16 |
||
|
Пример 2-3. Прослушивание каждого подключения spsmirnov 28.07.12 в 09:36 Пример 2.3 Листинг каждого подключения jchouse 02.08.12 в 11:36 |
+2 | |
|
Здесь мы добавляем другой обработчик событий -- client.on(). Обратите внимание, как мы добавляем обработчик событий в область функций обратного вызова. Это означает что мы рабоатем с клиентом направленному по этому пути. Обработчик мы добавляем по событию выхова данных. Это событие вызывается каждый раз когда клиент отправляет что-либо на сервер. Хотя мы теряем client.end(). Потому что, если мы закроем соединение с клиентом, как мы быдем обрабатывать новые данные? Теперь когда бы мы не отправили данные на сервер они будут выводиться в консоль. Попробуйте это в примере 2.4. jchouse 03.08.12 в 07:53 |
||
|
Пример 2.4 Отправка данных на сервер из Telnet jchouse 07.08.12 в 12:47 |
||
|
Что же здесь произошло? Мы запустили сервер и присоединились к нему Telnet. Сервер сказал "Hi" и мы ответили “Hello, yourself”. В этой точке, Node выплюнул кучу бреда, который вы никогда ранее не видели. Потому что JavaScript по хорошему не умееет работать с двоичными данными, Node также. Он вызывает Буфер, и он позволяет серверу представить двоичные данные. Node не знает какой тип данных отправил Telnet, поэтому Node просто хранит эти данные6 пока мы не укажаем какой нибудь вид кодировки. jchouse 07.08.12 в 12:55 |
||
|
Последовательность букв и цифр на самом деле предствлены в шестнацатиричной системе (см. Буфер в Главе 4). Каждый байт предствляет собой букву или цифру в строке “Hello, yourself”. Мы можем использовать метод toString() для перевода данных Буфера в обычную строку если нужно, или мы можем пробросить его как есть потому что TCP и Telnet понимают двоичный код. jchouse 07.08.12 в 21:29 |
||
|
Теперь, когда мы можем получать сообщения от клиентов, и мы должны позволить им отправлять сообщения друг другу. Для этого, нам нужна возможность соощения их между собой. Это хорошо что мы можем вызвать client.write(), но это работает для одного клиента в одну единицу времени. Нам же нужны ссылки на других клиентов. Мы можем сделать это через создание списка клиентов. Всякий раз как мы получаем нового клиента мы добавляем его в наш список и используем этот список для коммуникации между нашими клиентами (см. пример 2.5). jchouse 07.08.12 в 21:47 |
||
|
Пример 2.5. Общение между клиентами. jchouse 07.08.12 в 21:47 |
||
|
Теперь когда мы запускаем его а примере 2.6, мы видим соединение нескольких клиентов к серверу и отправку их сообщений между собой. jchouse 07.08.12 в 21:49 |
||
|
Пример 2.6 Обмен сообщениями между клиентами. jchouse 07.08.12 в 21:50 |
||
|
В этот раз сервер не фиксирует все полученные сообщения, всмето этого мы их заворачиваем через масив обратно к клиентам. Обратите внимане, когда Telnet клиент в терминале 2 отправляет сообщение, оно отправлется в Telnet в терминале 3, а также обратно в Telnet терминал 2. Это потому что, когда мы отправляем сообщение, мы не проверяем кто был отправителем, мы просто отправляем его всему масиву клиентов. Также, глядя в Telnet не понятно какие из сообщений были ваши, а какие вы поличли. Можно поработать над этим. В примере 2.7, давайте создадим функцию для отправки сообщения всем клиентам и испоьзуем чтобы разобраться с этим вопросом. jchouse 10.08.12 в 08:51 |
||
|
Пример 2.7 Отправка сообщений jchouse 10.08.12 в 09:26 |
||
|
Прежде всего, мы добавляем к обработчику события соединения команду для добавления свойства имени каждому клиенту. Обратите внимание что мы можем декорировать объект клиента дополнительными свойствами. Это потому что мы замыкаем каждого клиента к специфическому запросу. Используемые свойства клиента используются для создания имени, и client.remoteAddress это IP адрес клиента который соединяется. client.remotePort это TCP порт клиента обращающегося к серверу с запросом. Когда несколько клиентов соединяются с одного IP, у них получается разный remotePort. Когда у нас стоит задача приветствия клиента мы можем сделать это используя уникальное имя этого клиента. jchouse 10.08.12 в 13:49 |
||
|
Мы извлекаем цикл клиентских записей из данных обработчика событий. Теперь у нас есть функция вещания и, используя ее, мы можем отправлять сообщения всем подключенным клиентам. Однако, мы все равно передаем сообщение клиенту отправившего его. Мы также включаем в отправку имя клиента, когда отправляем сообщение всем остальным. Это более лучшаяя версия сервера, смотреть пример 2.8. jchouse 10.08.12 в 15:18 |
||
|
Пример 2.8 Пример улучшенной версии чат-сервера. jchouse 13.08.12 в 18:28 |
||
|
Это более дружелюбный и удобный сервис. Но все еще не совершенный, но мы работаем над этим. Отметим, что номера портов будут отличаться, когда вы запустите этот пример у себя. Разные операционные системы используют разные диапазоны портов, кроме того это будет зависит от уже используемых портов а также от случайного фактора. Возможно вы уже столкнулись с тем что наш сервис имеет фатальный изъян! Если один из клиентов отсоедениться мы получим результат представленный в примере 2.9. jchouse 13.08.12 в 18:37 |
||
|
Пример 2.9. Сбой сервера при отключении клиента. jchouse 13.08.12 в 18:38 |
||
|
Мы запустили сервер и подсоединили несколько клиентов, но когда клиент в Консоле 2 отсоединился, мы получили проблемы. В следующий раз мы будем использовать broadcast(), в этом случай когда Консоль 3 отправляет сообщение сервер пытается написать отключенным клиентам. Когда клиент из Консоли 2 отключается, его сокет перестает быть записываемым и читаемым. Когда мы пытаемся вызвать write() в сокете когда он закрыт, мы получаем исключение в процессе Node. Это приводит к отключению всех оставшихся клиентов. Очевидно, что от этого наш сервер становится неприемлимо хрупким. jchouse 13.08.12 в 18:44 |
||
|
Мы можем исправить это двумя путями. Первый, мы должны убедиться, что каогда клиент отключается, мы удаляем его из массива клиентов, поэтому он перестает получать сообщения от метода write(). Это позволяет сборщику мусора V8 исправить в памяти объект socet. Второй, мы должны поставить больше защиты когда пишем в socet. Мы должны убедиться, что между последним разом когда socet был записан и нынешним временем, ничего не помешает нам вызвать write(). К счастью в Node есть легкие способы очуществить оба варината. Первый показан в примере 2.10. jchouse 14.08.12 в 08:15 |
||
|
Пример 2.10. Делаем чат сервер более устойчивым. jchouse 14.08.12 в 09:00 |
||
