Node.js + Socket.io 实现一对一即时聊天
bigegpt 2024-12-16 11:09 5 浏览
作者:五月君
转发链接:https://mp.weixin.qq.com/s/TLKkRwftewa0uMvIRlOf5g
前言
实现一对一即时聊天应用,重要的一点就是消息能够实时的传递,一种方案就是熟知的使用 Websocket 协议,本文中我们使用 Node.js 中的一个框架 Socket.io 来实现。
效果预览
先看下,我们实现的最终效果,如下所示:
你也可以在浏览器分别输入以下两个 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-header:聊天界面头部信息。
- 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
客户端首先创建一个 socket 对象,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 发送到服务端,由服务端转接到另一客户端。
前端部分更多细节代码,这里不再列举,可在 Github 上 Clone 下来自行查看,文末有代码示例地址。
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
上面我们已经搭建了一个简单的 Express 服务,现在引入我们自定义的 io.js。
// app.js
require('./io.js')(server);
创建 io.js 在加载 socket.io 时传入 server 对象,这时会拿到一个服务端的 io 对象,同步的注册 connection 事件,如果有新的客户端进来会被触发,connection 回调函数的 socket 是指当前客户端与服务端建立的链接。
还有 online、private_chat、disconnect 这些事件有些是系统提供的,有些是我们自定义的,下文还会在介绍。
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 找到对应的 socket 向接收方推送消息,如果用户不在线,可以做些离线消息推送处理。这里私聊转发关键的一点是 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: https://github.com/qufei1993/Examples
Demo 在线体验:
- http://120.27.239.212:30010/?sender=赵敏&receiver=聂小倩
- http://120.27.239.212:30010/?sender=聂小倩&receiver=赵敏
总结
Socket.io 已经封装的很好了,使用它开发一个即时聊天应用更多工作需要我们去接入自己的业务逻辑,本文也只是一个聊天系统的冰山一角,还有很多需要去做,感兴趣的朋友欢迎关注,后续会持续分享一些其它功能。
推荐JavaScript学习相关文章
《用 80 行 Javascript 代码构建自己的语音助手 》
《纯JavaScript实现HTML5 Canvas 6种特效滤镜 》
《手写一个Promise/A+ 完美通过官方872个测试用例》
《Node.js 实现抢票小工具&短信通知提醒(上)「干货」》
《Node.js 实现抢票小工具&短信通知提醒(下)「干货」》
《学习 jQuery 源码整体架构,打造属于自己的 js 类库》
作者:五月君
转发链接:https://mp.weixin.qq.com/s/TLKkRwftewa0uMvIRlOf5g
相关推荐
- 了解Linux目录,那你就了解了一半的Linux系统
-
大到公司或者社群再小到个人要利用Linux来开发产品的人实在是多如牛毛,每个人都用自己的标准来配置文件或者设置目录,那么未来的Linux则就是一团乱麻,也对管理造成许多麻烦。后来,就有所谓的FHS(F...
- Linux命令,这些操作要注意!(linux命令?)
-
刚玩Linux的人总觉得自己在演黑客电影,直到手滑输错命令把公司服务器删库,这才发现命令行根本不是随便乱用的,而是“生死簿”。今天直接上干货,告诉你哪些命令用好了封神!喜欢的一键三连,谢谢观众老爷!!...
- Linux 命令速查手册:这 30 个高频指令,拯救 90% 的运维小白!
-
在Linux系统的世界里,命令行是强大的武器。对于运维小白而言,掌握一些高频使用的Linux命令,能极大提升工作效率,轻松应对各种系统管理任务。今天,就为大家奉上精心整理的30个Linu...
- linux必学的60个命令(linux必学的20个命令)
-
以下是Linux必学的20个基础命令:1.cd:切换目录2.ls:列出文件和目录3.mkdir:创建目录4.rm:删除文件或目录5.cp:复制文件或目录6.mv:移动/重命名文件或目录7....
- 提高工作效率的--Linux常用命令,能够决解95%以上的问题
-
点击上方关注,第一时间接受干货转发,点赞,收藏,不如一次关注评论区第一条注意查看回复:Linux命令获取linux常用命令大全pdf+Linux命令行大全pdf为什么要学习Linux命令?1、因为Li...
- 15 个实用 Linux 命令(linux命令用法及举例)
-
Linux命令行是系统管理员、开发者和技术爱好者的强大工具。掌握实用命令不仅能提高效率,还能解锁Linux系统的无限潜力,本文将深入介绍15个实用Linux命令。ls-列出目录内容l...
- Linux 常用命令集合(linux常用命令全集)
-
系统信息arch显示机器的处理器架构(1)uname-m显示机器的处理器架构(2)uname-r显示正在使用的内核版本dmidecode-q显示硬件系统部件-(SMBIOS/DM...
- Linux的常用命令就是记不住,怎么办?
-
1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...
- Linux常用文件操作命令(linux常用文件操作命令有哪些)
-
ls命令在Linux维护工作中,经常使用ls这个命令,这是最基本的命令,来写几条常用的ls命令。先来查看一下使用的ls版本#ls--versionls(GNUcoreutils)8.4...
- Linux 常用命令(linux常用命令)
-
日志排查类操作命令查看日志cat/var/log/messages、tail-fxxx.log搜索关键词grep"error"xxx.log多条件过滤`grep-E...
- 简单粗暴收藏版:Linux常用命令大汇总
-
号主:老杨丨11年资深网络工程师,更多网工提升干货,请关注公众号:网络工程师俱乐部下午好,我的网工朋友在Linux系统中,命令行界面(CLI)是管理员和开发人员最常用的工具之一。通过命令行,用户可...
- 「Linux」linux常用基本命令(linux常用基本命令和用法)
-
Linux中许多常用命令是必须掌握的,这里将我学linux入门时学的一些常用的基本命令分享给大家一下,希望可以帮助你们。总结送免费学习资料(包含视频、技术学习路线图谱、文档等)1、显示日期的指令:d...
- Linux的常用命令就是记不住,怎么办?于是推出了这套教程
-
1.帮助命令1.1help命令#语法格式:命令--help#作用:查看某个命令的帮助信息#示例:#ls--help查看ls命令的帮助信息#netst...
- Linux的30个常用命令汇总,运维大神必掌握技能!
-
以下是Linux系统中最常用的30个命令,精简版覆盖日常操作核心需求,适合快速掌握:一、文件/目录操作1.`ls`-列出目录内容`ls-l`(详细信息)|`ls-a`(显示隐藏文件)...
- Linux/Unix 系统中非常常用的命令
-
Linux/Unix系统中非常常用的命令,它们是进行文件操作、文本处理、权限管理等任务的基础。下面是对这些命令的简要说明:**文件操作类:*****`ls`(list):**列出目录内容,显...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)