Готовый перевод Node Up and Running / Node В начале... (рабочее название): --- Строим Chat Server
В мире который становится все более рперативным, нет ничего более оперативного чем чат. С чего же начать? Давайте начнм с TCP-based чат сервера который будет конектиться с Telnet. Это не только самое просто с чего можно начать, это еще и 100% будет написано на Node.
Первое, что надо сделать - это подключить библиотеки TCP из Node и создать новый TCP-сервер (смотри пр. 2.1).
Пример 2.1 Создание нового TCP-сервера
Во-первых, мы подключаем модуль net. Он содержит все необходимое для работы с TCP в Node. Теперь мы можем создать TCP сервер вызвав метод net.createServer(). Теперь, когда у нас есть сервер, мы хотим чтобы он что-то выполнял. Поэтому мы добавим слушатель событий при помощи метода on(). Когда произойдет событие подключения, слушатель событий вызовет функцию, которую мы назначили. Событие подключения происходит когда новый клиент подключается к серверу.
Событие подключения передает нам ссылку на TCP сокет нашего новго клиента, когда он вызывает нашу функцию обратного вызова. Мы назвали эту ссылку клиент. При вызове client.write (), мы можем отправлять сообщения на вновь подключенный клиент. Для начала скажем, "Привет!", а затем "Прощай", и вызываем метод client.end(), который закроет подключение. Это просто, и это начало нашего чат сервера. Наконец, нам нужно вызвать listen() чтобы Node знал какой порт слушать. Давай попробуем это.
Мы можем проверить наш новый сервер подлючением к Telnet, которая установлена на большинство операционных систем. Прежде всего, нам нужно запустить сервер зпуском Node c файлом. Затем нужно полдключить Telnet к localhost порту 9000, который мы указали в нашей Node программе. См. пр. 2.2.
Пример 2-2. Подключение к TCP серверу Node с помощью Telnet'а
Пока мы сделали сервер к которому может подключиться клиент и отправили ему сообщение перед тем как отключить его, но врядли это чат сервер. Давайте добавим еще несколько фишек. Во-первых, мы должны убедиться что мы можем получать сообщения от клиента, как показано в Примере 2.3
Пример 2.3 Листинг каждого подключения
Здесь мы добавляем другой обработчик событий -- client.on(). Обратите внимание, как мы добавляем обработчик событий в область функций обратного вызова. Это означает что мы рабоатем с клиентом направленному по этому пути. Обработчик мы добавляем по событию выхова данных. Это событие вызывается каждый раз когда клиент отправляет что-либо на сервер. Хотя мы теряем client.end(). Потому что, если мы закроем соединение с клиентом, как мы быдем обрабатывать новые данные? Теперь когда бы мы не отправили данные на сервер они будут выводиться в консоль. Попробуйте это в примере 2.4.
Пример 2.4 Отправка данных на сервер из Telnet
Что же здесь произошло? Мы запустили сервер и присоединились к нему Telnet. Сервер сказал "Hi" и мы ответили “Hello, yourself”. В этой точке, Node выплюнул кучу бреда, который вы никогда ранее не видели. Потому что JavaScript по хорошему не умееет работать с двоичными данными, Node также. Он вызывает Буфер, и он позволяет серверу представить двоичные данные. Node не знает какой тип данных отправил Telnet, поэтому Node просто хранит эти данные6 пока мы не укажаем какой нибудь вид кодировки.
Последовательность букв и цифр на самом деле предствлены в шестнацатиричной системе (см. Буфер в Главе 4). Каждый байт предствляет собой букву или цифру в строке “Hello, yourself”. Мы можем использовать метод toString() для перевода данных Буфера в обычную строку если нужно, или мы можем пробросить его как есть потому что TCP и Telnet понимают двоичный код.
Теперь, когда мы можем получать сообщения от клиентов, и мы должны позволить им отправлять сообщения друг другу. Для этого, нам нужна возможность соощения их между собой. Это хорошо что мы можем вызвать client.write(), но это работает для одного клиента в одну единицу времени. Нам же нужны ссылки на других клиентов. Мы можем сделать это через создание списка клиентов. Всякий раз как мы получаем нового клиента мы добавляем его в наш список и используем этот список для коммуникации между нашими клиентами (см. пример 2.5).
Пример 2.5. Общение между клиентами.
Теперь когда мы запускаем его а примере 2.6, мы видим соединение нескольких клиентов к серверу и отправку их сообщений между собой.
Пример 2.6 Обмен сообщениями между клиентами.
В этот раз сервер не фиксирует все полученные сообщения, всмето этого мы их заворачиваем через масив обратно к клиентам. Обратите внимане, когда Telnet клиент в терминале 2 отправляет сообщение, оно отправлется в Telnet в терминале 3, а также обратно в Telnet терминал 2. Это потому что, когда мы отправляем сообщение, мы не проверяем кто был отправителем, мы просто отправляем его всему масиву клиентов. Также, глядя в Telnet не понятно какие из сообщений были ваши, а какие вы поличли. Можно поработать над этим. В примере 2.7, давайте создадим функцию для отправки сообщения всем клиентам и испоьзуем чтобы разобраться с этим вопросом.
Пример 2.7 Отправка сообщений
Прежде всего, мы добавляем к обработчику события соединения команду для добавления свойства имени каждому клиенту. Обратите внимание что мы можем декорировать объект клиента дополнительными свойствами. Это потому что мы замыкаем каждого клиента к специфическому запросу. Используемые свойства клиента используются для создания имени, и client.remoteAddress это IP адрес клиента который соединяется. client.remotePort это TCP порт клиента обращающегося к серверу с запросом. Когда несколько клиентов соединяются с одного IP, у них получается разный remotePort. Когда у нас стоит задача приветствия клиента мы можем сделать это используя уникальное имя этого клиента.
Мы извлекаем цикл клиентских записей из данных обработчика событий. Теперь у нас есть функция вещания и, используя ее, мы можем отправлять сообщения всем подключенным клиентам. Однако, мы все равно передаем сообщение клиенту отправившего его. Мы также включаем в отправку имя клиента, когда отправляем сообщение всем остальным. Это более лучшаяя версия сервера, смотреть пример 2.8.
Пример 2.8 Пример улучшенной версии чат-сервера.
Это более дружелюбный и удобный сервис. Но все еще не совершенный, но мы работаем над этим. Отметим, что номера портов будут отличаться, когда вы запустите этот пример у себя. Разные операционные системы используют разные диапазоны портов, кроме того это будет зависит от уже используемых портов а также от случайного фактора. Возможно вы уже столкнулись с тем что наш сервис имеет фатальный изъян! Если один из клиентов отсоедениться мы получим результат представленный в примере 2.9.
Пример 2.9. Сбой сервера при отключении клиента.
Мы запустили сервер и подсоединили несколько клиентов, но когда клиент в Консоле 2 отсоединился, мы получили проблемы. В следующий раз мы будем использовать broadcast(), в этом случай когда Консоль 3 отправляет сообщение сервер пытается написать отключенным клиентам. Когда клиент из Консоли 2 отключается, его сокет перестает быть записываемым и читаемым. Когда мы пытаемся вызвать write() в сокете когда он закрыт, мы получаем исключение в процессе Node. Это приводит к отключению всех оставшихся клиентов. Очевидно, что от этого наш сервер становится неприемлимо хрупким.
Мы можем исправить это двумя путями. Первый, мы должны убедиться, что каогда клиент отключается, мы удаляем его из массива клиентов, поэтому он перестает получать сообщения от метода write(). Это позволяет сборщику мусора V8 исправить в памяти объект socet. Второй, мы должны поставить больше защиты когда пишем в socet. Мы должны убедиться, что между последним разом когда socet был записан и нынешним временем, ничего не помешает нам вызвать write(). К счастью в Node есть легкие способы очуществить оба варината. Первый показан в примере 2.10.
Пример 2.10. Делаем чат сервер более устойчивым.
Переведено на Нотабеноиде
http://notabenoid.org/book/30771/105666
Переводчики: jchouse, Randl, spsmirnov
Настройки
Готово:
