Для реализации приложения мгновенного чата один на один важным моментом является то, что сообщения могут передаваться в режиме реального времени.Одним из решений является использование известного протокола Websocket.В этой статье мы используем фреймворк Socket.io в Node. .js для реализации.
Предварительный просмотр эффекта
Давайте посмотрим на окончательный эффект, которого мы достигли, а именно:
Вы также можете ввести следующие два URL-адреса в браузере, чтобы испытать:
http://120.27.239.212:30010/?sender=赵敏&receiver=聂小倩
http://120.27.239.212:30010/?sender=聂小倩&receiver=赵敏
Технический отбор
- Внешний интерфейс: HTML + CSS + JS также использует Boostrap для реализации макета страницы и рендеринга некоторых стилей.
- Серверная часть: Node.js + Express + Socket.io.
Интерфейсная реализация
Макет HTML-страницы
HTML-разметка страницы чата несложная и примерно разделена на 3 слоя следующим образом:
- заголовок чата: информация заголовка интерфейса чата.
- chat-content: используется для отображения общего содержимого чата. То, что вы видите сейчас, это просто пустой div. После отправки или получения информации чата он будет манипулировать DOM, чтобы вставить содержимое сообщения в тело чата.
- chat-bottom: внизу показано окно ввода контента и кнопка отправки нашего окна чата.
<div class="container">
<div class="chat-header row">
<span class="col-xs-2 chat-header-left glyphicon glyphicon-menu-left"></span>
<span class="col-xs-8 chat-header-center" id="chatHeaderCenter"></span>
<span class="col-xs-2 chat-header-right glyphicon glyphicon-option-horizontal"></span>
</div>
<div class="chat-content" id="chatContent"></div>
<div class="chat-bottom row">
<span class="col-xs-10 col-md-11 input-text"><input type="text" class="form-control " id="inputText" placeholder="请输入要发送的内容..."></span>
<span class="col-xs-2 col-md-1 span-submit">
<input class="btn btn-default btn-primary input-submit" id="sendBtn" data-dismiss="alert" type="submit" value="发送">
</span>
</div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="./js/chat.js"></script>
Socket.io Client
Сначала клиент создает объект сокета, и первым параметром io() является URL-адрес связанного сервера, который по умолчанию является window.location.
И у клиента, и у сервера Socket есть две функции on() и emit(), которые также являются основными. Благодаря этим двум функциям можно легко реализовать двустороннюю связь между клиентом и сервером.
- emit: инициировать событие, первый параметр — это имя события, второй параметр — это данные, которые должны быть отправлены на другой конец, а третий параметр — это функция обратного вызова, используемая для подтверждения получения информации другой стороной, которая может быть игнорируется.
- on: Зарегистрируйте событие для прослушивания событий, вызванных emit.
// js/chat.js
const socket = io();
socket.on('connect', () => {
socket.emit('online', query.sender);
});
socket.on('reply_private_chat', replyPrivateMessage);
...
Чтобы отправить сообщение на стороне клиента, нужно прослушать событие onclick или событие возврата каретки кнопки отправки, выполнить некоторую обработку сообщения и отправить его на сервер через socket.emit, а сервер передает его на другой клиент.
Более подробный код front-end части здесь не приводится, вы можете клонировать его на Github, чтобы посмотреть самостоятельно, в конце статьи есть адрес примера кода.
const chatHeaderCenter = document.getElementById('chatHeaderCenter');
const inputText = document.getElementById('inputText');
const sendBtn = document.getElementById('sendBtn');
chatHeaderCenter.innerText = query.receiver;
sendBtn.onclick = sendMsg;
inputText.onkeydown = sendMsgByEnter;
function sendMsg() {
const value = inputText.value;
if (!value) return alert('Message is required!');
const message = { sender: query.sender, receiver: query.receiver, text: value };
socket.emit('private_chat', message, data => {
renderMessage(data, true);
});
inputText.value = '';
}
...
бэкэнд реализация
Создайте сервис с помощью Express
Используйте Express для создания нашей серверной службы, создайте app.js, который прослушивает порт 30010, и загрузите нашу клиентскую страницу.
// app.js
const express = require('express');
const app = express();
const path = require('path');
const server = require('http').createServer(app);
const PORT = 30010;
app.use(express.static(path.join(__dirname, '../', 'public')));
server.listen(PORT, () => console.log(`Server is listening on ${PORT}`));
Представляем Socket.io
Выше мы построили простой экспресс-сервис, теперь мы представляем наш собственный io.js.
// app.js
require('./io.js')(server);
Создайте io.js и передайте объект сервера при загрузке socket.io. В это время вы получите объект ввода-вывода на стороне сервера и синхронно зарегистрируете событие подключения. Если появится новый клиент, он будет запущен. функции обратного вызова соединения относится к текущему соединению между клиентом и сервером.
Также есть события online, private_chat, разъединения, некоторые из которых предоставляются системой, а некоторые настраиваются нами, которые будут представлены позже.
const _ = require('underscore');
const moment = require('moment');
const userData = require('./users.json');
const USER_STATUS = ['ONLINE', 'OFFLINE'];
const users = {};
module.exports = server => {
const io = require('socket.io')(server);
io.on('connection', socket => {
socket.on('online', ...)
socket.on('private_chat', ...);
socket.on('disconnect', ...);
});
}
Онлайн-уведомление
on('online') - это наше пользовательское событие, которое запускается клиентом после того, как он выходит в сеть, чтобы сообщить нам информацию о пользователе текущего клиента, сохранить socket.id, чтобы установить отношение сопоставления между пользователем и socket.id для последующих приватных чатов. Здесь socket.id будет меняться каждый раз, когда клиент отключает тяжелую цепочку.
socket.on('online', username => {
socket.username = username;
users[username] = {
socketId: socket.id,
status: USER_STATUS[0]
};
})
Получать отправленные сообщения в приватном чате
on('private_chat') также является нашим настраиваемым событием. После получения сообщения, отправленного клиентом, сообщение обрабатывается, чтобы определить, находится ли получатель в сети. Если соответствующий сокет найден в сети через socket.id, сообщение отправляется в получатель.Если пользователь Если вы не в сети, вы можете выполнить некоторую автономную обработку push-сообщений. Ключевым моментом переадресации приватного чата здесь является socket.to().emit().
socket.on('private_chat', (params, fn) => {
const receiver = users[params.receiver];
params.createTime = moment().format('YYYY-MM-DD HH:mm:ss');
const senderData = _.findWhere(userData, { username: params.sender });
params.senderPhoto = (senderData || {}).photo;
if (!params.senderPhoto) {
const senderLen = params.sender.length;
params.senderPhotoNickname = params.sender.substr(senderLen - 2)
}
fn(params);
if (receiver && receiver.status === USER_STATUS[0]) {
socket.to(users[params.receiver].socketId).emit('reply_private_chat', params);
} else {
console.log(`${params.receiver} 不在线`);
// 可以在做些离线消息推送处理
}
});
disconnect
Запускается при разрыве соединения. Reason указывает причину, по которой клиент или сервер отключили соединение. В этом случае мы также изменим причину неработающей ссылки.
socket.on('disconnect', reason => {
if (users[socket.username]) users[socket.username].status = USER_STATUS[1];
});
Код и развертывание
Я упаковал приведенный выше пример как образ Docker.Если вам интересно, вы можете выполнить следующую команду, чтобы вытащить его, развернуть и запустить самостоятельно.
docker pull docker.io/qufei1993/private-chat-socketio
Пример кода:
- Github: GitHub.com/fee1993/о…
Демонстрационный онлайн-опыт:
http://120.27.239.212:30010/?sender=赵敏&receiver=聂小倩
http://120.27.239.212:30010/?sender=聂小倩&receiver=赵敏
Суммировать
Socket.io упакован очень хорошо. Использование его для разработки приложения мгновенного чата требует от нас доступа к нашей собственной бизнес-логике. Эта статья — лишь верхушка айсберга системы чата. Нам еще многое предстоит сделать. Заинтересованные друзья добро пожаловать, чтобы обратить внимание, и будет продолжать делиться некоторыми другими функциями в будущем.