[Из песочницы] Разработка простого чата на Socket.IO [2016] \ Node.js
Установка модуля
Итак, давайте приступать?! Для начала нам нужно собственно установить наш Socket.Io, как его установить можете прочитать здесь, а для тех, кто не понял, поясню: есть клиент и сервер, на сервер нужно поставить сам модуль через npm в папку проекта (для этого надо заранее открыть её в консоли, а дальше устанавливать):
npm install socket.io
После того как установили модуль, в папке с проектом (куда ставили) появится папка «node-modules»
Теперь мы должны установить сам .js файл модуля в папку с проектом (.js файл будет использоваться на клиенте), ставить отсюда => *тык*. Вы скачаете архив, и оттуда вы должны достать файл «socket.io.js» и перекинуть в папку с проектом.
Для тех кто не понял, структура папки проекта с чатом должна быть такова на данный момент:
chat: (Сама папка) |node-modules |--socket.io (Внутри node-modules) |socket.io.js
После того как вы скачали .js файл и установили модуль — можно приступать к следующему этапу.
Установка дополнительных модулей
Вам понадобятся также такие модули как:
1. express # Для создания сервера
2. log4js # Отличный логгер
3. http # Сам http-сервер
express:
npm install express
log4js:
npm install log4js
http:
npm install http
После всех этих процедур, папка «node-modules» должна пополниться (*логично вроде*). Теперь можно приступать к разработке нашего чата!
Разработка чата
Теперь мы можем приступать у разработке, но перед этим, надо создать .js-файл в директории проекта «chat.js» в чем мы и будем писать наш чат. Для начала открываем наш chat.js и подключаем все модули таким образом:
var express = require('express'); // Подключаем express
var app = express();
var server = require('http').Server(app); // Подключаем http через app
var io = require('socket.io')(server); // Подключаем socket.io и указываем на сервер
var log4js = require('log4js'); // Подключаем наш логгер
var logger = log4js.getLogger(); // Подключаем с модуля log4js сам логгер
Это все что нам нужно подключить в начале. Дальше создаем переменную куда укажем наш порт для прослушивания (для сервера):
var port = 3000; // Можно любой другой порт
После этого можно прологгировать старт скрипта таким образом:
logger.debug('Script has been started...'); // Логгируем.
Дальше ставим на «прослушку» для сервера порт таким образом:
server.listen(port); // Теперь мы можем подключиться к нашему серверу через localhost:3000 при запущенном скрипте
Теперь мы должны сделать так, чтобы клиенту который подключается к нашему серверу «localhost:3000», получал пакет файлов таких как index.html; main.css; socket.io; main.js; чтобы отображалась сама страница с нашим чатом.
Это делается просто, создаем папку «public» в корне проекта, и закидываем туда наш «socket.io.js», а дальше создаем там такие файлы как index.html, main.js, main.css(Не будем пользоваться, это для наглядности). Наша структура проекта должна быть примерно такая:
chat: |node-modules: |-socket.io |-express |-log4js |-http |public: |-index.html |-socket.io.js |-main.css |-main.js |chat.js
Дальше делаем такой «финт» который будет отправлять клиенту содержимое этой папки при подключении:
chat.js:
app.use(express.static(__dirname + '/public')); // Отправляет "статические" файлы из папки public при коннекте // __dirname - путь по которому лежит chat.js
Отлично, теперь при подключении на localhost:3000 (при запущенном скрипте) у нас будет открываться «index.html», только страница будет пустая из-за того что у нас «index.html» пустой :)
Накидаем эскиз чата в «index.html» (без стилей). Можете просто копировать код в «index.html»
chat
Прекрасно! Теперь если мы запустим скрипт и откроем localhost:3000, мы увидим наш *кривой* эскиз) Также вы можете увидеть, что я подключил jQuery, благодаря ему мы будем извлекать текст из поля «удобно».
Как мы можем понять, сейчас наш чат не работает, по сколько не настроены «евенты» в скрипте. Сейчас мы будем всё разбирать. Для начала мы должны подключиться с клиента к серверу (socket.io) в «main.js»
var port = 3000; // Указываем порт на котором у на стоит сокет
var socket = io.connect('http://localhost:' + port); // Тут мы объявляем "socket" (дальше мы будем с ним работать) и подключаемся сразу к серверу через порт
Теперь когда мы написали подключение socket.io к серверу через порт, самое время обработать это событие на сервере.
io.connect (port) — Создает событие 'connection', нам надо сделать его обработчик на сервере, иначе подключение будет *пустое*, а нам надо еще присвоить никнейм тому кто приконнектился, а это делается в обработчике, просто читаем дальше.
Создаём обработчик в скрипте «chat.js»
io.on('connection', function (socket) { // Создаем обработчик события 'connection' которое создает io.connect(port); с аргументом socket
var name = 'U' + (socket.id).toString().substr(1,4); // Создаем никнейм нашему клиенту. В начале буква 'U' дальше берем 3 символа ID (сокета) после первого символа, и все это клеим с помощью '+'
socket.broadcast.emit('newUser', name); // Отсылает событие 'newUser' всем подключенным, кроме текущего. На клиенте навешаем обработчик на 'newUser' (Отправляет клиентам событие о подключении нового юзера)
socket.emit('userName', name); // Отправляем текущему клиенту событие 'userName' с его ником (name) (Отправляем клиенту его юзернейм)
logger.info(name + ' connected to chat!'); // Логгирование
});
Итак, для тех, кто не понял… io.on ('event', function (arg){}) — создаёт прослушку на событие 'event' с аргументом arg; передавать аргументы будем чуть позже) socket.id — ID подключения этого сокета (сокет — клиент), socket.broadcast.emit ('newUser', name); — отправка события 'newUser' всем кроме текущего сокета, с переменной name (текущего сокета). socket.emit ('userName', name); — отправляет событие 'userName' только текущему сокету c переменной name.
Для тех, кто не понял — сервер и клиент могут отправлять и принимать события одинаково:
socket.emit ('event') — отправляет на сервер\клиент
socket.on — прослушивает события на клиенте
io.on — прослушивает события на сервере
Теперь создаём в «main.js» прослушки на 'newUser', 'userName'…
socket.on('userName', function(userName){ // Создаем прослушку 'userName' и принимаем переменную name в виде аргумента 'userName'
console.log('You\'r username is => ' + userName); // Логгирование в консоль браузера
$('textarea').val($('textarea').val() + 'You\'r username => ' + userName + '\n'); // Выводим в поле для текста оповещение для подключенного с его ником
});
socket.on('newUser', function(userName){ // Думаю тут понятно уже =)
console.log('New user has been connected to chat | ' + userName); // Логгирование
$('textarea').val($('textarea').val() + userName + ' connected!\n'); // Это событие было отправлено всем кроме только подключенного, по этому мы пишем другим юзерам в поле что 'подключен новый юзер' с его ником
});
$('textarea').val ('текст'); — изменение текста в текстовом поле. \n — переход на новую строку
Теперь когда мы откроем localhost:3000 мы увидим встречный текст в поле с нашим ником! Прекрасно? Я знаю ^^ Но согласитесь, это еще не чат. Теперь нам надо сделать так, чтобы при нажатии на кнопку «Отправить» <= index.html отправлялся текст на сервер. Для этого дописываем в «main.js» обработчик на кнопку 'button' через jQuery.
$(document).on('click', 'button', function(){ // Прослушка кнопки на клик
var message = $('input').val(); // Все что в поле для ввода записываем в переменную
socket.emit('message', message); // Отправляем событие 'message' на сервер c самим текстом (message)- как переменная
$('input').val(null); // Заполняем поле для ввода 'пустотой'
});
После этого мы можем спокойно делать обработчик события 'message' на самом сервере.
chat.js:
io.on('connection', function (socket) {
var name = 'U' + (socket.id).toString().substr(1,4);
socket.broadcast.emit('newUser', name);
logger.info(name + ' connected to chat!');
socket.emit('userName', name);
// Обработчик ниже // Мы его сделали внутри коннекта
socket.on('message', function(msg){ // Обработчик на событие 'message' и аргументом (msg) из переменной message
logger.warn('-----------'); // Logging
logger.warn('User: ' + name + ' | Message: ' + msg);
logger.warn('====> Sending message to other chaters...');
io.sockets.emit('messageToClients', msg, name); // Отправляем всем сокетам событие 'messageToClients' и отправляем туда же два аргумента (текст, имя юзера)
});
});
Отлично! Теперь когда мы будем отправлять текст со страницы, он у нас будет логгироваться в консоли node, так же мы всем клиентам отправили событие 'messageToClients', сейчас мы будем на клиенте делать обработчик этого события…
main.js:
socket.on('messageToClients', function(msg, name){
console.log(name + ' | => ' + msg); // Логгирование в консоль браузера
$('textarea').val($('textarea').val() + name + ' : '+ msg +'\n'); // Добавляем в поле для текста сообщение типа (Ник : текст)
});
Наш чат готов! Теперь вы можете его модернизировать как угодно.
Этот пост был создан потому, что топики, которые на Хабре по созданию чата, были запощены 5 лет назад, а за это время много чего произошло и я решил создать свой топик) Тем не менее, вот топик, по которому я вдохновлялся — ссылочка. Может я где-то ошибся, поправляйте! Все люди ошибаются.
Спасибо всем за внимание!
Комментарии (7)
15 августа 2016 в 10:53
0↑
↓
Никогда не забывайте про package.json. Чтобы потом не гадать какие зависимости необходимы вашему серверу.
И ставить модули поэтому надо с ключом --save, например «npm install express --save», тогда этот модуль сразу будет прописан в зависимостях.15 августа 2016 в 11:20 (комментарий был изменён)
0↑
↓
npm install http
Что? Это https://npmjs.com/http, зачем он вам? Это пустой пакет, с одной версией
0.0.0
, опубликованной три года назад.
В Node.js есть встроенный модульhttp
, скорее всего, вы хотели его. Ваша ссылка, кстати, на https://npmjs.com/http-server ведёт почему-то.И да, как выше сказали — прописывайте зависимости в
package.json
.node-modules
node_modules
Вы скачаете архив, и оттуда вы должны достать файл «socket.io.js» и перекинуть в папку с проектом.
Нет-нет-нет-нет. Не надо так делать. Используйте менеджеры зависимостей, например
npm
илиbower
— клиентские библиотеки тоже тяните оттуда, тогда они и у вас в репозитории валяться не будут, и обновлять проще, и сразу будет поднятно, какая версия чего.https://code.jquery.com/jquery-3.1.0.min.js
Для изменения значения
textarea
? Серьёзно? И нет, не надо так тоже делать — если оно действительно нужно, поставьте зависимостью, не стоит со стороннего CDN тянуть. Помним про videojs.И, самое главное — зачем
socket.io
? Почему нельзя было ограничиться штатными вебсокетами?15 августа 2016 в 11:29
+1↑
↓
Совсем забыл:
за это время много чего произошло
Например, появился ECMAScript 2015 с const/let и стрелочными функциями. ESLint круто линтит код. IE9 и встроенный андроид браузер 4.3 (не путать с хромом на андроиде 4.3) умерли, штатные вебсокеты поддерживается всем. Использования jQuery для тривиальных операций не особо-то и нужно уже давно. =).
И да — не в обиду автору, а улучшения ситуации для.
В целом такие статьи с простыми примерами нужны, да.
15 августа 2016 в 11:24
0↑
↓
Не так давно реализовали чат на веб-сокетах с (слава богу) деградацией до поллинга. Какой же это геморой, никому не пожелаю, особенно на нагруженном проекте.15 августа 2016 в 11:36
0↑
↓
Подключать jquery ради того чтобы «удобно» извлекать текст из поля? Тогда сразу агнуляр на прослушку самого поля ставьте! Мастхэв!15 августа 2016 в 11:36
0↑
↓
http://socket.io/get-started/chat/15 августа 2016 в 11:43
0↑
↓
Пару месяцев назад понадобилась тоже создать чат на socket.io для прохождения собеседования (оно было успешно пройдено). Только задача состояла в том, чтобы создать кластерный чат с сохранением последних 10 сообщений в MongoDB. Если кому интересно, вот ссылочка на репозиторий.В сокетах есть один подводный камень, который всплывает при работе на кластерах (или многопоточных приложениях). Сокеты ломаются, при использовании протокола long polling, поэтому при создании многопоточного приложения используйте websocket. Так как протокол websocket не всеми старыми браузерами поддерживается, то по умолчанию в socket.io выбран протокол long polling.
Если интересно, то могу написать более подробно в отдельном посте.