Как сделать чат на сокетах

Обновлено: 07.07.2024

В статье я расскажу, как можно создать чат-приложение в режиме реального времени с помощью Vue.js, Node.js, Express и SOCKET.IO.

Вот скриншот того, что должно получиться:

PS: Я не специалист по фронтенду, так что не смейтесь над дизайном. Данный макет используется только в качестве примера.

Установка

Для работы нам понадобятся Node.js и NPM. Если у вас еще нет установленного Node.JS, то качайте его отсюда .

  • Вам потребуются базовые знания JavaScript.
  • Пригодятся небольшие знания по Vue.js (но это не принципиально).

Если все готово, то можно начинать.

Создайте директорию для приложения и откройте ее в любимом редакторе. Я пользуюсь Visual Studio Code.

Если хотите, можете работать в терминале.

mkdir ChatApp && cd ChatApp && code

Давайте инициализируем директорию через NPM.

Если вам предлагают ввести какую-то информацию, смело ее вводите или нажимайте Enter для настроек по умолчанию. Такая информация используется для настройки пакета .json файла.

Установка зависимостей

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

npm install express --save

npm install --save socket.io

После установки всех зависимостей запустите

Так установятся все необходимые пакеты.

Фронтенд с Vue.js (разметка)

Интерфейс приложения мы сделаем в Vue.js . Его нужно установить в нашу директорию и добавить bootstrap 4.3.1 .

Создадим файл index.html.

Для включения в проекты Vue.js и bootstrap скопируем CDN и добавим в раздел со скриптами файла index.html .

После успешной установки Vue и bootstrap перейдем к созданию разметки.

Для подключения Socket.IO сервера к клиенту добавим клиентские JavaScript библиотеки.

Это будет наш файл Vue и bootstrap (HTML) для фронтенда. Можете скопировать код целиком, чтобы не отставать.

Лучший способ научиться чему-то — повторять за другими.

Можно разграничить функционал, вынеся JavaScript-код из общей разметки. Это решать вам. Но я для удобства этого делать не буду.

Установка сервера

Создадим server.js . Внутри файла создадим и настроим Express-сервер для работы с Socket.IO.

Это базовая конфигурация, необходимая для установки Socket.IO в бэкенде.

Как работает Socket.IO

Socket.IO — это JavaScript-библиотека чата в режиме реального времени. Документацию можно почитать здесь (она не относится к тематике данной статьи). Я объясню лишь то, что нам потребуется для статьи.

В Socket.IO есть много флагов или методов, которые можно вызывать для выполнения различных действий (например, отправка событий, прослушивание событий и т.д.).

Socket.ON(): принимает в качестве параметров название события и обратный вызов. Прослушивает события, отправленные на сервер и с него. Обратный вызов нужен для получения данных, связанных с событием.

Socket.EMIT(): отправляет/посылает событие с данными или без них для прослушивания сокетов (включая себя).

Socket.Broadcast.Emit() : отправляет событие всем подключенным клиентам (кроме себя). Эти методы пригодятся для отправки событий с сервера через чат-приложение.

Настройка кода клиентской части

Откройте index.html. В нижней части файла добавьте следующий код в тег script.

Давайте перейдем к объекту methods . Мы видим метод Send() . Он хранит информацию о чате в массиве message и отправляет события чата на сервер с помощью флага Socket.emit() .

На серверной стороне мы получаем событие с флагом Socket.on() и через флаг Socket.broadcast.emit()пересылаем его другим подключенным клиентам.

В хуке Vue CREATED мы прослушиваем все события, отправленные сервером. Сюда же включено событие chat-message, которое мы пересылали с сервера ранее.

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

Аналогия

Именно так все и происходит, когда пользователи выполняют какие-то действия в ChatApp.

Подведем итог всему происходящему во фронтенде.

1. Хук Methods. Именно здесь в Vue.js вы создаете все методы/функции для компонента. И при каждом вызове метода в разметке/шаблоне Vue обращается к этому хуку.

У нас есть только два объявленных метода:

ii. addUser(). Метод отправляет на сервер событие joined и устанавливает значение переменной ready как true, показывая, что пользователь успешно присоединился к чату.

2. Хук Created. Данный хук вызывается при загрузке компонента Vue.js. Это отличное место для прослушивания всех событий с сервера.

Мы прослушиваем 6 событий с сервера и отправляем 1 событие на сервер.
>, 5000);
>);

В коде выше видно, чем занимается каждое событие.

В статье описывается, как разработать и запустить простейший чат на вебсокетах.

Чат на вебсокетах

Сервер

Существует много готовых продуктов для запуска websocket-сервера. Наш сервер мы запустим используя библиотеку Workerman, ключевое преимущество которой – это полное отсутствие зависимостей: скачал, запустил – работает. Также в Workerman имеется поддержка таймеров прямо из коробки, они нам пригодятся. И конечно же! Библиотека написана на PHP, а все любят PHP ;-)

Клиент

Для разработки приложения-клиента я выбрал фреймворк Qt только потому, что он мне нравится. Qt – кроссплатформенная библиотека (фреймворк), позволяющая создавать программы практически под любую платформу: Windows, Linux, Android, OS X, iOS и др. Разумеется, клиент для полученного сервера можно написать на чем угодно, хоть на HTML+JS, который будет работать прямо из браузера.

1. Настройка VDS-сервера

Сразу после приобретения VDS и установки операционной системы (выбрал свежую версию Ubuntu 18.04 на тарифе «Master») подключаемся к нему. На сервер можно зайти через консоль из панели управления VDS, но это не самый удобный вариант. Предпочтительнее подключаться по SSH.

Таймвэб

Если разные способы подключения по SSH из Windows, например:
1. Воспользоваться программой Putty;
2. Воспользоваться терминалом Cygwin;
3. Воспользоваться терминалом Ubuntu из WSL (я выбрал этот способ).

В Linux намного проще, клиент для подключения по SSH, как правило, установлен во всех дистрибутивах по умолчанию, поэтому просто открываем терминал.

Независимо от выбранного способа, команда для подключения будет одна:

где – это IP-адрес вашего сервера, который можно найти в панели управления VDS (блок «Список используемых IP-адресов»).

<VDS_IP_ADDRESS></p>
<p>

Установка Workerman

Чтобы скачать Workerman, сначала устанавливаем composer:

Теперь скачиваем Workerman в папку /usr/local/workerman:

И создаём php-файл, в котором будем писать код сервера чата:

Далее открываем файл ChatWorker.php для редактирования. Это можно сделать разными способами. Самый хардкорный и олдскульный вариант - редактировать прямо в терминале, воспользовавшись консольными редакторами nano, mcedit, vim и др.

Если работаете в Linux, то из рабочего окружения KDE можно подключиться через файловый менеджер Dolphin по протоколу SFTP и открыть файл в любом редакторе или даже в IDE (например, в KDevelop).

Если работаете в Windows, то можете скачать Notepad++ с плагином NppFTP, либо что-то более продвинутое, вроде Sublime / Atom / Visual Studio Code, и так же подключиться по протоколу SFTP.

2. Сервер

Наш простейший чат будет поддерживать следующие действия:

Инициализация WebSocket-сервера

И это всё. При запуске этого php-скрипта WebSocket-сервер будет запущен на порту 27800 и к нему уже можно будет подключиться.

Но обратите внимание: можно указать любой другой свободный порт, главное не забыть открыть его на VDS-сервере командой:

где – выбранный вами порт для чата.

Запускаем WebSocket-сервер командой:

php ChatWorker.php start

Для проверки соединения и дальнейшей отладки можно воспользоваться плагином Simple WebSocket Client для браузера Google Chrome.

Simple WebSocket Client

Окно плагина Simple WebSocket Client

В поле Server Location -> URL: вводим адрес сервера, начиная с названия протокола: ws:// и нажимаем кнопку Open.

Инициализировать сервер было легко, но надо ведь ещё обработать события!

Подключение и авторизация пользователя

Авторизация пользователя будет происходить во время подключения клиента к серверу с передачей параметров. Параметры подключения передаются в адресной строке как в обычном URL-адресе (QUERY STRING), а на сервере их можно прочитать из переменной $_GET.

Предполагается, что при подключении приложение-клиент должно передать все необходимые сведения о пользователе:
- отображаемое имя (ник): параметр userName;
- цвет ника: параметр userColor;
- пол: параметр gender.

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

При подключении клиента к websocket-серверу, вызывается функция Worker::onConnect, в которую передается указатель на созданный объект соединения TcpConnection.

После подключения обрабатываются все параметры подключения (а нас как раз и интересует то, с какими параметрами осуществлялось подключение) и затем вызывается функция установки соединения TcpConnection::onWebSocketConnect – в которой будет доступна обработанная переменная $_GET - отсюда будем извлекать сведения о подключившемся клиенте.

Обратите внимание!

Вызов функцииWorker::runAll() запускает цепь обработки событий Workerman. Код, написанный после вызова этой функции, не будет выполнен. Помните об этом при внесении дальнейших изменений.

Сохраним файл на сервере и перезапустим Workerman. Остановить предыдущий запуск можно клавишами CTRL+C, а затем снова запустить той же командой:

Обратите внимание!

Перезапускать WebSocket-сервер нужно после каждого внесения изменений в любой из php-скриптов вашего проекта.

Пробуем подключиться с передачей параметров: ws://:27800?userName=anonymous

И вот, наконец, мы добрались до Hello World!

Hello World!

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

Первое (Authorized) отправляется только подключившемуся пользователю, чтобы сообщить ему, с какими данными он был подключен.

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

Отключение пользователя

Когда клиент закрывает соединение с сервером, вызывается функция Worker::onClose, тут обработаем выход пользователя из чата:

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

Пинг пользователей

При вызове метода Worker::runAll запускаются все объявленные «работники» (их может быть несколько), а при их запуске вызывается функция Worker::onWorkerStart – здесь добавим код таймера для пинга пользователей.

Примечание

Протокол WebSocket имеет встроенную реализацию ping/pong из коробки, но мы напишем собственную, в которой сможем выполнять дополнительные действия. Однако клиент будет дополнительно оповещать сервер о наличии подключения, используя встроенную реализацию.

После тестирования websocket-сервер можно запустить в режиме службы, для этого нужно добавить параметр -d:

Перезапуск выполняется командой restart:

3. Клиент

Режим приватного чата активируется двойным кликом по имени в списке пользователей, а закрывается этот режим специальной кнопкой [x], которая по умолчанию скрыта.

Клиент чата

Прототип окна чата

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

Диалог авторизации в клиенте

В рамках этой статьи рассмотрим лишь основные моменты.

Установка соединения с сервером

В проекте реализован метод Widget::connectToServer, он открывает диалог авторизации. Если диалог будет закрыт (result != AuthDialog::Accepted), то приложение закроется вместе с ним.

Если же ввести все авторизационные данные, то будет осуществлена попытка подключения к серверу.

Оповещение пользователя

На сервере имеется паттерн обработки такого текста (функция $worker->onMessage), который заменяет фигурные скобки на теги «<b>» и «</b>», выделяя текст жирным шрифтом.

По такой же схеме можно реализовать полноценную поддержку markdown, вставку смайликов и картинок.

Расширяя функционал сервера и клиента можно также добавить:

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

При помощи Socket.io и Node JS можно создавать потрясающие программы. В ходе статьи мы расскажем как создать веб чат используя сокеты и локальный сервер.


Мы реализуем программу при помощи платформы Socket.io, а также Node JS и Express. Socket.IO — JavaScript-библиотека, выполняющая роль программы для обмена данными между клиентами и сервером. Все обновления происходят в режиме реального времени, что позволяет создавать функционал на подобии веб чата, онлайн игры или других мультиплеерных сервисов.

Благодаря сокетам мы можем отправлять данные на один сервер, а далее все клиенты (пользователи), подключенные к этому серверу, будут видеть всю информацию и взаимодействовать с ней. На основе Socket.IO можно создавать как небольшие чат программы, так и полноценные мультиплеерные игры, запускаемые через браузер.

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

Для установки Node перейдите на их официальный сайт и выполните установку программы на ваш компьютер.

Создайте папку и откройте её через командную строку или терминал. В командной строке пропишите команду npm init . У вас спросят различные настройки, введите их:


Далее выполните установку npm install express и npm install socket.io . Это два пакета, которые понадобятся для разработки программы.

Далее создайте два файла: index.js , а также index.html . Оба эти файла представлены ниже вместе с комментариями.

HTML код программы:

JavaScript код программы:

Для более подробного пояснения можете также посмотреть видео урок:


Полезные ссылки из видео:

Больше интересных новостей

10 популярных языков программирования

10 популярных языков программирования

Нужна ли физика программисту?

Нужна ли физика программисту?

10 привычек, которые делают разработчика эффективным

10 привычек, которые делают разработчика эффективным

7 профессиональных советов программисту

7 профессиональных советов программисту



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

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

Почему сокеты?

Вероятно, вам интересно, зачем нам нужно идти на более низкие уровни чем URLSession? Если вам это не интересно, тогда читайте дальше и притворитесь, что интересно…

1. Запрос JSON у сервера.

2. Получение и использование запрошенного JSON в callback’е или функции делегата.

Но что если вы хотите, чтобы сервер вам сообщал о каком-нибудь событии? Данный случай не совсем подходит для HTTP. Конечно, вы можете сделать это непрерывно отправляя запросы серверу и проверяя состояние (принцип polling), или можете применить немного хитрости и использовать long-polling, но эти способы неправильны и имеют свои подводные камни. В конце концов, зачем ограничивать себя парадигмой "запрос-ответ", тем более, что она не подходит для решения задачи?

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

Поехали

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

Запуск сервера

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

Чтобы запустить сервер откройте терминал, перейдите в директорию проекта и введите следующую команду (понадобится ввести пароль):

После ввода пароля вы должны увидеть: Listening on 127.0.0.1:80. Ваш сервер готов!

Можете пропустить следующую часть.

Если хотите скомпилировать сервер самостоятельно, нужно установить Go через Homebrew.

Если у вас не установлен Homebrew, установите его. Для этого откройте терминал, и выполните следующую команду:

Затем используйте команду чтобы установить Go:

После завершения переместитесь в папку с проектом и соберите сервер при помощи команды build:

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

Рассмотрим стартовый проект

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

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

Создаем чат

Поскольку у вас уже есть ChatRoomViewController, имеет смысл создать класс ChatRoom.

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

1. Открывал соединение с сервером чата.

2. Представлял пользователю возможность войти в чат, указав имя.

4. Закрывал соединение по окончанию сессии.

Теперь вы знаете, чего хотите, нажмите cmd + n для создания нового файла. Выберите Cocoa Touch Class и назовите его ChatRoom (subclass NSObject).

Создание входных и выходных потоков данных

Далее вставьте в файл следующий код:

Здесь вы определяете класс ChatRoom и объявляете его свойства, которые нужны для соединения.

2. Следующее, у вас есть переменная для хранения имени текущего пользователя.

Затем, откройте ChatRoomViewController.swift и добавьте новое свойство на самом верху.

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

Открытие соединения

Откройте файл ChatRoom.swift и добавьте следующий метод:

Вот что в нем происходит:

1. Здесь определяются два неинициализированных потока данных сокета для чтения и записи, которые не имеют автоматического управления памятью.

2. Затем вы объединяете ваши потоки чтения и записи сокета и подключаете их к сокету сервера, в нашем случае это порт 80.

Функция принимает четыре параметра. Первый типа allocator для инициализации потоков. Вам необходимо стараться использовать kCFAllocatorDefault всегда, когда это возможно, хотя есть и другие варианты, в зависимости от того чего вы хотите добиться.

Второй параметр это имя сервера, в данном случае вы подключаетесь к локальной машине, но если у вас есть IP адрес удаленного сервера, вы можете использовать его. Затем вы определяете, что соединение будет остановлено на порту 80, на этот порт сервер принимает данные.

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

Теперь, когда потоки инициализированы, вы можете сохранить ссылки на них добавив код:

Вызов функции takeRetainedValue() на объекте без управления памятью позволяет одновременно захватить сохраненную ссылку и удалить несбалансированную ссылку, чтобы позже избежать утечки памяти. Теперь у вас есть возможность использовать входной и выходной потоки, когда они понадобятся.

Затем, чтобы приложение правильно реагировало на сетевые события, эти потоки необходимо добавить в цикл выполнения. Добавьте эти две строки в конец setupNetworkCommunication.

Еще чуть-чуть и врата флуда будут открыты! Для того, чтобы вечеринка началась добавьте следующий код (и снова в конец setupNetworkCommunication ):

И это все что нужно. Чтобы закончить откройте ChatRoomViewController.swift и добавьте строку кода в метод viewWillAppear(_:)

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

Протокол соединения

Чтобы войти в чат и назвать свое имя.

А также можете сказать:

Это логично и просто.

Но также и очень небезопасно, так что не применяйте это в реальных проектах.

Теперь понятно чего ожидает сервер, можно написать метод в класс ChatRoom который принимает пользователя в чат. Единственный параметр, который нужен - это имя пользователя.

Добавьте следующий метод в конец метода настроек:

Теперь, когда ваш метод готов, перейдите в ChatRoomViewController.swift и добавьте вызов в конец viewWillAppear(_:).

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


Подождите, я могу обьяснить, откройте терминал. Прямо под Listening on 127.0.0.1:80 вы увидите Luke has joined или что-то подобное, в зависимости от того, какое имя вы ввели.

Далее необходимо сделать чат делегатом входящего потока. Откройте ChatRoom.swift и добавьте расширение в конец файла:

Так как чат теперь подписан на протокол StreamDelegate можно объявить делегат.

Добавьте следующую команду в setupNetworkCommunication() перед вызовом schedule(_:forMode:)

Следующее, добавьте stream(_:handle:) в расширение:

1. Настройка буфера, из которого происходит чтение входящих байтов.

2. Чтение до тех пор, пока не будут прочитаны все байты.

3. Каждый раз при вызове read(_:maxLength:) прочитываются данные из потока и передаются в буфер.

4. Если вызов команды чтения возвращает отрицательное значение, выдается ошибка.

Этот метод нужно вызывать в случае, когда входной поток имеет доступные байты, поэтому перейдите в Stream.Event.hasBytesAvailable условного оператора switch внутри stream(_:handle:) и вызовите метод над которым вы работали под командой print.

На данный момент у вас есть шикарный буфер наполненный байтами! Прежде чем завершить метод, вам нужно будет написать еще один помощник, чтобы превратить буфер в объект Message.

Поместите нижеприведенное определение метода под readAvailableBytes(_:):

Создание протокола ChatRoomDelegate

Перейдите в начало ChatRoom.swift и добавьте простое определение протокола:

Затем добавьте слабое опциональное свойство чтобы хранить ссылку на того, кто станет делегатом ChatRoom.

Теперь вы можете вернуться и закончить readAvailableBytes(_:) добавив следующее внутри if-let.

Чтобы закончить работу, вернитесь в ChatRoomViewController.swift и добавьте следующее расширение подписывающее его к этому протоколу, прямо под расширением MessageInputDelegate.

Теперь перейдите и подпишите ViewController быть делегатом chatRoom, добавив следующую строку сразу после вызова super в viewWillAppear(_:).

Еще раз создайте и запустите, а затем введите свое имя в текстовое поле и нажмите enter.

Вернитесь в ChatRoom.swift и добавьте следующий метод в нижнюю часть определения класса.

Запустите проект и попробуйте.

Если вы хотите, чтобы кто-то поболтал в ответ, перейдите в новое окно терминала и введите:

Это позволит вам подключиться к серверу TCP командной строкой. Теперь вы можете выдавать те же команды, которые приложение использует в чате.

Поздравляю, вы успешно написали приложение клиент чата!

Уборка после себя

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

Чтобы сделать это добавьте следующий метод после объявления sendMessage(_:)

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

Чтобы закончить работу, добавьте этот вызов метода в Stream.Event.endEncountered инструкции switch.

Затем вернитесь в ChatRoomViewController.swift и добавьте этот же вызов во viewWillDisappear(_:).

И это все, вы закончили!

А что дальше?

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

Читайте также: