Готов поспорить, что каждый программист, который когда-либо играл в многопользовательскую онлайн-игру или впервые пользовался чатом, задавался вопросом, как возможно такое быстрое общение.

С сегодняшними технологиями это «чудо» стало проще, чем когда-либо. Давайте посмотрим, как это сделать.

Эта статья будет разделена на 2 части:

Здесь мы обсудим использование WebSocket в ощутимом контексте, а также создадим рабочий пример простого чат-приложения с использованием Socket.IO - реализации WebSocket для Node.js .

Используя знания, полученные в части 1, мы создадим многопользовательский сервер в Node.js с помощью Socket.IO. После этого мы создадим простую игру «камень-ножницы-бумага». Кроме того, мы рассмотрим концепции сватовства и игровых комнат.

WebSocket - это протокол связи, который обеспечивает связь TCP / IP между веб-клиентом (например, веб-браузером) и веб-сервером, обеспечивая постоянное соединение и двунаправленный обмен сообщениями между обеими сторонами без накладных расходов на HTTP-запрос.

Приведенная ниже диаграмма - хороший способ разобраться в концепции.

# 1 фаза

Сервер будет ожидать начального HTTP-вызова, поэтому может произойти рукопожатие. Некоторые необходимые проверки могут выполняться в соответствии с потребностями сервера (например, блокировка региона, регистрация пользователя, проверка учетных данных и т. Д.).

Если проверка прошла успешно, сервер отправляет ответ клиенту, сообщая ему, что соединение установлено, теперь клиент подключен и дальнейшее взаимодействие может происходить без использования протокола HTTP.

# 2-я фаза

Теперь между сервером и клиентом установлено постоянное соединение.

Доступна двунаправленная связь, и любая из сторон может отправлять сообщения друг другу, отправляя полезные данные.

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

# 3 фаза

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

Описание

В этом примере мы будем использовать сервер, использующий веб-фреймворк и реализацию WebSocket с именем.

Эта комбинация может не подходить для каждого реального приложения, но для этого примера подойдет.

Предпосылки

Установите, если у вас его еще нет. При этом также будет установлен npm.

Нам потребуется установить экспресс через терминал:

npm install --save express

Также установим Socket.IO через терминал:

npm install --save socket.io

В этом примере мы реализуем все функции на стороне сервера, необходимые для сервера чата.

- Подайте index.html, который мы создадим позже.

- Слушайте запросы на подключение к порту 4321.

- Прослушивание различных вызовов методов (например, set_username, message).

- Слушайте запросы на отключение.

Выполнение

Создайте chat_server.js со следующим кодом:

var exp = require('express')();
var http = require('http').Server(exp)
var io = require('socket.io')(http)
// Serve index.html
exp.get('/', function(req, res) {
 res.sendFile(__dirname + '/index.html');
});
// Set the listening port to 4321
http.listen(4321, function() {
 console.log('Server is listening on *:4321')
});
// Socket.IO implementation
io.on('connection', function(socket) {
 console.log('New user!')
socket.on('set_username', function(msg) {
  console.log('Username set:', msg.username)
  socket.Name = msg.username
  // Let everyone else know that a new user has entered the chat
  socket.broadcast.emit('message', {
   name: null,
   txt: socket.Name + ' has entered the chat!'
  })
 });
socket.on('message', function(msg) {
  console.log('New message:', socket.Name, msg.msg)
  // Emit the message to everyone except the user who sent it
  socket.broadcast.emit('message', {
   name: socket.Name,
   txt: msg.msg
  })
 });
socket.on('disconnect', function() {
  console.log('User disconnected...', socket.Name)
  // Let everyone else know that a new user has left the chat
  io.emit('message', {
   name: null,
   txt: socket.Name + ' has left the chat!'
  })
  socket.disconnect()
 });
});

См. Пример пера Андре Агияра (@andretaguiar) на CodePen.

Заходим в терминал и запускаем сервер:

node chat_server

У нас должен получиться следующий результат:

Server is listening on *:4321

Вот и все, что касается сервера. Теперь давайте создадим простую страницу чата для связи с нашим сервером.

Как и на сервере, нам нужно будет использовать некоторые внешние ресурсы, которые мы будем использовать, чтобы помочь нам создать наш интерфейс, Материализовать, чтобы украсить нашу страницу, и, конечно же, нам все еще нужно использовать Socket.IO для подключения к нашему серверу.

В этом примере мы реализуем все функции на стороне клиента, необходимые для использования созданного ранее чат-сервера.

  • Подключиться / повторно подключиться к localhost: 4321.
  • Установите сгенерированное имя пользователя («Пользователь-» + случайное число от 0 до 999).
  • Отправляйте сообщения.
  • Отключитесь от сервера.

Все ресурсы будут предоставлены сетями CDN.

Создайте index.html с помощью следующего кода или используйте результат кода для проверки чата:

<!DOCTYPE html>
<html>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=egde">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Cleverti chat</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" >
    <link href="https://fonts.googleapis.com/css?family=Quicksand" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
    <style>
        * { font-family: 'Quicksand', sans-serif; }
    </style>
</head>
<body>
    <div id="app" class="container">
        <h1 class="center-align">Cleverti chat</h1>
        <form class="col s12">
            <div class="row center-align">
                <div class="center-align input-field col s10">
                    <label class="active" for="message">Message</label>
                    <input id="message" v-model="message" value="" type="text">
                </div>
                <div class="center-align col s1">
                        <div class="btn btn-small light-green" onclick="send()">
                            <i class="material-icons">play_arrow</i>
                        </div>
                </div>
                <div class="center-align col s1">
                        <div class="btn btn-small red" onclick="disconnect()">
                            <i class="material-icons">cloud_off</i>
                        </div>
                </div>
            </div>
            <br/>
            <div class="row center-align" v-if="messages != null" v-for="message of messages">
                <div class="col s1"></div>
                <div class="right-align strong col s3">
                    <div v-if="message.name != null"><b> {{ message.name }} </b></div>
                </div>
                <div class="left-align col s7"> {{ message.txt }}</div>
                <div class="col s1"></div>
            </div>
        </form>
    </div>
    <script>
        var socket = null
function connect() {
            socket = io('http://localhost:4321')
            
            // Identity yourself before the server
            socket.emit('set_username', { username: app.username })
            app.messages = []
            app.messages.push( { name: null, txt: 'You are connected!' });
// This listener handles text messages sent by the server 
            socket.on('message', function(msg) {
                app.messages.push( { name: msg.name, txt: msg.txt });
            })
// This listener handles disconnection from the server 
            socket.on('disconnect', function() {
                app.messages.push( { name: null, txt: 'Connection to the server was terminated.' });
            })
        }
function disconnect() {
            if (socket.connected) {
                socket.disconnect();
                app.messages.push( { name: null, txt: 'You disconnected from the server.' });
            }
            else {
                app.messages.push( { name: null, txt: 'You are already disconnected from the server.' });
            }
        }
        
        function send() {
            if (!socket.connected) {
                connect()
            }
if (app.message.length > 0) {
                // Send message to the server
                socket.emit('message', { msg: app.message })
                app.messages.push( { name: 'Me', txt: app.message } );
                app.message = ''
            }
        }
var app = new Vue({
            el: '#app',
            data: {
                username: '',
                message: '',
                messages: []
            },
            methods: {
                connect: function() {
                    connect();
                },
                disconnect: function() {
                    disconnect();
                },
                send: function() {
                    send();
                }
            }
        });
// Generate a username between 0 and 999
        app.username = 'User-' + Math.floor((Math.random() * 999))
// Lets connect!
        connect()
// This lets us press Enter and not refreshing the page by disabling submit action
        document.getElementById("message").addEventListener("keypress", function(e) {
            if (e.keyCode == 13) {
                e.preventDefault();
                send();
            }
        })
    </script>
</body>
</html>

См. Перо qGERvQ Андре Агияра (@andretaguiar) на CodePen.

Теперь, когда вы знаете, что такое упражнение, пора заставить его работать. Вы также можете получить весь код здесь.

Увидимся во второй части - Давай поиграем вместе!

Автор Андре Агияр | Старший разработчик в Cleverti

Первоначально опубликовано на https://www.cleverti.com.