简单使用Nodejs+Socket.io2.0+boostrap4.0实现聊天室功能
简单使用Nodejs+Socket.io2.0+boostrap4.0实现聊天室功能
前言
这篇文章实话说我有点虚,因为平时都不怎么研究这一块的,然后涉及到的知识点超多,我只能到处看看资料总结一下相关信息,所以在此我只想说句:
本文章内容只代表个人立场,有错必改!
原本打算一次性总结,后来越扯越多超过字数限制了,就干脆做成http系列文章了,不定时更新原有内容(发现哪里出错的话),不定时新增系列文章,请见谅!
因为之前写得太臃肿又不够详细,最近刚好复习到这一块的内容,所以决定把这些文章都拆分成更加细致一点,补充详细内容,优化排版布局,目前来看还是应该的,因为自身时间问题和平台编译的问题迟迟未改,只好等都改完之后才发出来。
网络协议基础 — TCP、UDP、SOCKET与参考模型
Http协议系列 — 协议原理构成与连接管理
Http协议系列 — cookie,Session,缓存机制
Http协议系列 — Http2、特殊字符编码与常见问题简答
Http协议系列—-进阶Https基础
WebSocket协议入门基础
简单使用Nodejs+Socket.io2.0+boostrap4.0实现聊天室功能
修改
2018/08/10 转简体字,重新排版,内容不变
以下全部代码可以看sockit-chat-demo,个人环境为
日期: 2018/06/25
Chrome极速版: 版本号 2.0.6.38
nodejs: v10.2.0
boostrap4
socket.io2
界面实现
我们直接使用boostrap4实现一个简单界面效果,不要再细节花费多余的功夫.组件里有个List group可以将就拿来实现,另外底部固定一个输入框完成.因为不需要用到插件,只引入样式即可
Bootstrap4
user.html
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title></title> <link rel="stylesheet" href="../bootstrap.min.css"> <style media="screen"> html, body { height: 100%; } .submitWrappre { position: fixed; left: 0; right: 0; bottom: 0; border: 1px solid #999; } </style> </head> <body> <div class="list-group"> <a class="list-group-item list-group-item-action flex-column align-items-start active"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">List group item heading</h5> <small>3 days ago</small> </div> <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p> <small>Donec id elit non mi porta.</small> </a> //为了间隔开 <a class="list-group-item list-group-item-action flex-column align-items-start"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">List group item heading</h5> <small class="text-muted">3 days ago</small> </div> <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p> <small class="text-muted">Donec id elit non mi porta.</small> </a> </div> //为了间隔开 <form class="submitWrappre"> <div class="form-group"> <textarea class="form-control" id="exampleFormControlTextarea1" rows="2"></textarea> <button type="button" class="btn btn-primary btn-sm" style="float:right">Small button</button> </div> </form> </body> </html>
以上代码可以看sockit-chat-demo的lesson1.
Socket.io
Socket.IO enables real-time bidirectional event-based communication.
It works on every platform, browser or device, focusing equally on reliability and speed.
SOCKET.IO 2.0
socket.io具有实时双向基于事件通讯,能兼容不同平台,浏览器或设备,同样关注可靠性和速度.
我们可以直接在浏览器和服务器都用socket.io库使用websocket通信.使用方法很简单,文档已经很详细了,就不复述了.
同时在同目录下新建一个server.js脚本,我们用nodejs创建一个服务器,先安装依赖socket.io和path.
yarn add socket.io path //OR npm i -D socket.io path
path.join方法将多个参数值字符串结合成一个绝对路径字符串,我们需要利用它简单判断请求资源路径
req.url === '/' ? './user.html' : (path.join(__dirname, '../', req.url)); // ./user.html同层html // C:\project\sockit-chat\xx其他上层资源
我们创建一个服务器并且监听80端口,简单判断请求资源在当前目录下相对路径返回给前端.
var app = require('http').createServer(handler); var fs = require('fs'); var path = require("path"); //为了间隔开 app.listen(80); //为了间隔开 function handler(req, res) { var _path = req.url === '/' ? './user.html' : (path.join(__dirname, '../', req.url)); //为了间隔开 fs.readFile(_path, function(err, data) { if (err) { res.writeHead(500); return res.end('Error loading index.html'); } res.writeHead(200); res.end(data); }); }
然后引入socket.io库并且监听my other event事件,同时触发news事件传播信息给前端.
var io = require('socket.io')(app); io.on('connection', function(socket) { socket.emit('news', {hello: 'world'}); socket.on('my other event', function(data) { console.log(data); }); });
前端写法一样
user.html
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.dev.js" charset="utf-8"></script> <script type="text/javascript"> var socket = io('http://localhost'); socket.on('news', function(data) { console.log(data); socket.emit('my other event', { my: 'data' }); }); </script>
以上代码可以看sockit-chat-demo的lesson2.
在当前目录下打开命令行,输入node server,启动服务器之后浏览器打开http://localhost/(默认端口80可省略).看到命令行打印{ my: 'data' },浏览器控制台打印Object {hello: "world"}即为成功.
界面响应
我们现在已经有了界面和通信实现了,现在先把界面响应实现出来.
1, 列表简化,把不需要的结构删除,只保留用户身份,时间,内容即可.
2, 输入框填写内容,点击按键触发通信同时输入框清空.
3, 不管是发送还是接收都要在列表末尾添加进去内容,通过.active区分用户身份.
<div class="list-group"> <a class="list-group-item list-group-item-action flex-column align-items-start active"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">Me</h5> <small>3 days ago</small> </div> <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p> </a> //为了间隔开 <a class="list-group-item list-group-item-action flex-column align-items-start"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">Other</h5> <small class="text-muted">3 days ago</small> </div> <p class="mb-1">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p> </a> </div>
var socket = io('http://localhost'), $content = document.getElementById('exampleFormControlTextarea1'), $send = document.getElementById('send'); //为了间隔开 $send.onclick = function() { socket.emit('user_send', { content: $content.value }); $content.value = ''; }
这里我用了ES6的模板写法,如果你们浏览器不支持就乖乖用原生写法吧
/** * [addMsg description] * @param {[bol]} identity [身份,1本人,0非本人] * @param {[type]} val [description] */ function addMsg(identity, val) { var node = document.createElement("a"), str = `<a class="list-group-item list-group-item-action flex-column align-items-start ${identity ? 'active' : ''}"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">${identity ? 'Me' : 'Other'}</h5> <small class="text-muted">${new Date().toLocaleString()}</small> </div> <p class="mb-1">${val}</p> </a>`; node.innerHTML = str; $list.appendChild(node); }
以上代码可以看sockit-chat-demo的lesson3.
聊天实现
1, 先把列表界面清除.只保留容器;
2, 定义前端发送给服务器的事件名称为user_send,保存在对象content属性;
3, 定义服务器发送给前端的事件名称为user_receive,保存在对象content属性;
user.html
socket.on('user_receive', function(data) { console.log('user_receive: ' + data.content); addMsg(0, data.content); }); //为了间隔开 $send.onclick = function() { socket.emit('user_send', { content: $content.value }); addMsg(1, $content.value); $content.value = ''; }
server.js
每个连接都有一个单独的socket对象,我们创建一个user_list保存每个socket对象,当我们接收到一条信息就遍历发送给其他的socket对象达到聊天效果.
const user_list = []; io.on('connection', (socket) => { if (!user_list.includes(socket)) user_list.push(socket); socket.on('user_send', (data) => { user_list.map(item => { if (item !== socket) item.emit('user_receive', {content: data.content}) }) }); });
以上代码可以看sockit-chat-demo的lesson4.
在当前目录下打开命令行,输入node server,启动服务器之后浏览器打开多个http://localhost/页面,然后随便在输入框填写东西,你就会发现其他页面也会自动接收相应数据了.
最终完成代码
user.html
<!DOCTYPE html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <title></title> <link rel="stylesheet" href="../bootstrap.min.css"> <style media="screen"> html, body { height: 100%; } .submitWrappre { position: fixed; left: 0; right: 0; bottom: 0; border: 1px solid #999; } </style> </head> <body> <div class="list-group"></div> //为了间隔开 <form class="submitWrappre"> <div class="form-group"> <textarea class="form-control" id="exampleFormControlTextarea1" rows="2"></textarea> <button type="button" class="btn btn-primary btn-sm" style="float:right" id="send">Small button</button> </div> </form> //为了间隔开 <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.dev.js" charset="utf-8"></script> <script type="text/javascript"> var socket = io('http://localhost'), $content = document.getElementById('exampleFormControlTextarea1'), $send = document.getElementById('send'), $list = document.getElementsByClassName('list-group')[0]; //为了间隔开 socket.on('user_receive', function(data) { console.log('user_receive: ' + data.content); addMsg(0, data.content); }); //为了间隔开 $send.onclick = function() { socket.emit('user_send', { content: $content.value }); addMsg(1, $content.value); $content.value = ''; } //为了间隔开 /** * [addMsg description] * @param {[bol]} identity [身份,1本人,0非本人] * @param {[type]} val [description] */ function addMsg(identity, val) { var node = document.createElement("a"), str = `<a class="list-group-item list-group-item-action flex-column align-items-start ${identity "code" >const app = require('http').createServer(handler), io = require('socket.io')(app), fs = require('fs'), path = require("path"); //为了间隔开 app.listen(80); //为了间隔开 function handler(req, res) { const _path = req.url === '/' ? './user.html' : (path.join(__dirname, '../', req.url)) fs.readFile(_path, (err, data) => { if (err) { res.writeHead(500); return res.end('Error loading index.html'); } res.writeHead(200); res.end(data); }); } //为了间隔开 const user_list = []; io.on('connection', (socket) => { if (!user_list.includes(socket)) user_list.push(socket); socket.on('user_send', (data) => { user_list.map(item => { if (item !== socket) item.emit('user_receive', {content: data.content}) }) }); });
后话
主要思路已经交代完了,全文下来其实还是很简单的,特别是socket.io封装好了API,我们只需要傻瓜式使用即可,实际开发我们还有很多步骤需要做的,例如:
- 注册登录唯一账号密码并且需要验证;
- 登录数据保存数据库,nodejs需要连接数据库,支持多种数据库使用;
- 针对特定账号发送信息;
- 支持表情图;
- 微信的撤回删除等操作;
总之可以优化的地方很多,你们可以自行研究.
用户评论