Пишем простой чат с помощью websockets и nodejs

Прошелся по мануалу с официального сайта socket.io, воспроизвел шаги, кое-что поправил, так как не заводилось, перевел, делюсь. Картинки брал оттуда же.

Введение

Создание чата с помощью традиционного набора LAMP (Linux, Apache, MySQL, PHP) всегда было непростой задачей. Приходилось использовать long polling, для своевременного получения информации, добавлять timestamps, и все это работало не так быстро, как хотелось бы.

Сокеты всегда были наиболее удобным решением для систем реального времени, в том числе и чатов, и позволяли приложениям одновременно обмениваться информацией в обе стороны.

В клиент-серверной архитектуре чата сервер отправляет (push) сообщения клиентам. Когда бы вы не написали сообщение, вы рассчитываете на то, что сервер разошлет его всем подключенным клиентам.

Фреймворк для работы с веб-страницами

Первой нашей целью будет создание простой HTML страницы, которая представляет пользователю форму ввода и отправленные сообщения. Мы собираемся использовать фреймворк  express (NodeJS) для отображения таких страниц. Если у вас еще не установлен NodeJS, установите его.

Теперь, давайте создадим отдельную директорию, в моем случае я назвал её chat-example, и в ней файл-манифест под названием package.json .

В нем будут содержаться следующие строки:

{
"name": "socket-chat-example",
"version": "0.0.1",
"description": "my first socket.io app",
"dependencies": {}
}

В дальнейшем, для установки всех зависимостей, которые нам понадобятся, мы будем использовать команду npm install –save

Первым делом установим фреймворк:

npm install --save <a href="mailto:express@4.10.2">express@4.10.2</a>

Теперь, после установки, мы можем создать файл index.js, в котором будет происходить основная жизнь нашего проекта. Его содержимое на данный момент:

var app = require('express')();
var http = require('http').Server(app);

app.get('/', function(req, res){
  res.send('<h1>Hello world</h1>');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

Что означает следующее:

  1. Фреймворк Express инициализирует переменную app таким образом, что она становится обработчиком функции, который может быть передан HTTP серверу (строка 2).
  2. Мы определяем обработчик роутинга для адреса /.  Эта функция будет вызвана, когда пользователь запросит корневую директорию нашего сайта (главную страницу).
  3. Мы запускаем веб-сервер на порту 3000.

Если вы запустите node index.js вы должны увидеть следующее:

И, если в этот момент вы откроете сайт (//localhost:3000), то должны увидеть такую картину:



Работа с HTML

До сих пор мы использовали res.send в нашем index.js, в котором передавали строку. Такой код станет невыносимо запутанным, если мы поместим туда весь HTML код, который хотим. Гораздо лучше будет создать отдельный файл, например index,html, и работать с ним.

Давайте переработаем наш обработчик роутинга и воспользуемся функцией sendFile (регистр имеет значение).

app.get('/', function(req, res){
    res.sendFile(__dirname + '/index.html');
});

А в index.html мы запишем следующее:

<!doctype html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      * { margin: 0; padding: 0; box-sizing: border-box; }
      body { font: 13px Helvetica, Arial; }
      form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
      form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
      form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages li { padding: 5px 10px; }
      #messages li:nth-child(odd) { background: #eee; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

Если вы завершите процесс (нажав Control+C и запустив node index.js заново), и обновите страницу, вы должны увидеть такую форму:



Подключаем Socket.IO

Socket.IO состоит из двух частей:

  • Серверная часть, которая расположена на HTTP сервере, где развернут NodeJS: socket.io
  • Клиентская часть, которая загружается на стороне пользователя: socket.io-client

Сейчас, для нужд разработки, серверный socket.io настраивает клиента за нас, так что нам нужно установить всего один модуль:

npm install --save socket.io

После того, как модуль и все его зависимости установятся, давайте изменим index.js: (прим: Я поправил строку 5 на res.sendFile(__dirname + ‘/index.html’); так как в примере на сайте использовалась устаревшая форма. С ней у меня не работало)

var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res){
  res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket){
  console.log('a user connected');
});

http.listen(3000, function(){
  console.log('listening on *:3000');
});

Обратите внимание, что я инициализирую новый экземпляр socket.io передавая объект http (the HTTP server). После этого я добавляю слушателя для события connection, который будет вызываться при подключении новых входящих сокетов, и писать об этом в лог.

Теперь давайте добавим сниппет в index.html перед тэгом  :

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

Это единственное, что требуется для загрузки клиентской части socket.io, мы создаем глобальную переменную, после чего подключаемся.

Обратите внимание, что я не указываю никакого адреса (URL) когда подключаю библиотеку и вызываю io(), так как по умолчанию он пытается загрузить библиотеку с того сервера, который вернул ему эту страницу.

Если теперь вы перезапустите сервер вы увидите в консоли надпись

“a user connected”.

Откройте несколько вкладок, и вы увидите несколько записей:

Каждый сокет также генерирует специальное событие disconnect (обратите внимание, функция создается внутри функции-обработчикаconnection):

io.on('connection', function(socket){
  console.log('a user connected');
  socket.on('disconnect', function(){
    console.log('user disconnected');
  });
});

Теперь, если вы обновите страницу несколько раз, вы увидите, что это работает:



Распространение событий

Основная идея, заложенная в Socket.IO состоит в том, что вы можете посылать и получать любые события по вашему выбору, содержащие любые данные, которые вам могут понадобиться. Подойдет любой объект, который может быть преобразован в JSON, а так же поддерживаются бинарные данные.

Давайте добавим следующий функционал: когда пользователь отправляет сообщение, сервер получает событие  chat message. Для этого давайте изменим javascript в нашем index.html:

<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="//code.jquery.com/jquery-1.11.1.js"></script>
<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
</script>

А в index.js добавим обработчик события chat message:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    console.log('message: ' + msg);
  });
});

Теперь, после перезапуска сервера, вы можете отправлять сообщения с веб-формы и читать их в консоли (видео - https://i.cloudup.com/transcoded/zboNrGSsai.mp4).



Broadcasting

Следующая наша цель это заставить сервер рассылать сообщения всем подписанным клиентам.

Для такой рассылки Socket.IO предоставляет нам функцию io.emit:

io.emit('some event', { for: 'everyone' });

Если мы хотим отправить сообщение всем, кроме определенного сокета, вы можете воспользоваться флагом broadcast:

io.on('connection', function(socket){
  socket.broadcast.emit('hi');
});

Но мы, в рамках этого приложения, просто отправим сообщением всем, включая отправителя:

io.on('connection', function(socket){
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});

А на стороне клиента мы будем отслеживать событие chat messageи, при получении, его содержимое мы добавим на страницу. Окончательный код JavaScript в нашем index,html будет выглядеть так:

<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
  socket.on('chat message', function(msg){
    $('#messages').append($('<li>').text(msg));
  });
</script>

И мы получили работающее приложение, уместившись в 20 строк кода!

Видео: https://i.cloudup.com/transcoded/J4xwRU9DRn.mp4

 

 

Категория: Программирование
Последнее изменение:


Крипто-кошельки для помощи и благодарности проекту:

Bitcoin адрес проекта: [[address]]

Перевод на сумму [[value]] BTC получен. Спасибо!.
[[error]]

Ethereum адрес проекта: [[address]]



Комментарии
Пожалуйста, авторизуйтесь, что бы оставить свой комментарий