# 基于Websocket实现简单聊天室 **Repository Path**: mxyNichol/ChatRoom_WebSocket ## Basic Information - **Project Name**: 基于Websocket实现简单聊天室 - **Description**: 基于Websocket实现简单聊天室(基于node和socket.io) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2022-05-03 - **Last Updated**: 2024-04-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Websocket ## 1. 什么是websocket ``` WebSocket 是一种网络通信协议 ``` ## 2. 为什么需要websocket ``` Websocket 协议,是为了弥补HTTP协议【单向通信】的缺点。 详细来讲,HTTP只支持由客户端发起通信,一旦服务端的数据发生变化,是不可能主动向客户端推送消息的。 一般而言,此时都会使用【轮询 + HTTP】的解决方案,即每隔一段时间就建立一次HTTP连接,来帮助客户端追加更新。但效率非常低,非常浪费资源。 ``` ``` websocket 则支持服务器推送技术。 即,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,实现真正的双向平等对话。 客户端和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 ``` ## 3. websocket简介 ### websocket 特点 ``` (1)实时通信:全双工协议,延迟更少,效率更高。 (2)与HTTP协议有良好的兼容性:默认端口也是80 和 443,并且握手阶段采用HTTP协议,可以通过各种HTTP代理服务器,不容易被屏蔽。 (3)控制开销:用于协议控制的数据包头部相对较小,数据格式比较简洁,性能开销比较小,通信高效。 (4)二进制传输:可以发送文本,也可以发送二进制数据。 (5)没有同源限制:可以实现客户端和服务器的任意通信。 (6)实现简单:建立在TCP协议之上,服务器端的实现比较简单。 ``` ### webSocket 与 HTTP 的异同 ``` 相同点: (1)都是基于TCP的应用层协议。 (2)都使用Request/Response模型进行连接的建立。 (3)在连接的建立过程中对错误的处理方式相同,在这个阶段WebSocket可能返回和HTTP相同的返回码。 ``` ``` 不同点: (1)HTTP协议基于Request/Response,只能做单向传输,是半双工通信,而WebSocket是全双工通信 - 半双工通信:单向流动, 服务器不主动推送数据给客户端 - 全双工通信:服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。 (2)http是无状态的,所以请求得到响应以后就关闭了,无状态的好处是服务器不需要存储相关会话信息。缺点是每次http请求和响应都会发送关于请求的冗余信息;而WebSocket 只需要建立一次Request/Response消息对,之后都是TCP连接,避免了需要多次建立Request/Response消息对而产生的冗余头部信息。节省了大量流量和服务器资源。 (3)WebSocket在建立握手连接时,数据是通过HTTP协议传输的,但在建立连接之后,真正的数据传输阶段是不需要HTTP协议参与的。而http需要需要三次握手。 (4)WebSocket传输的数据是二进制流,是以帧为单位的,HTTP传输的是明文传输,是字符串传输。 ``` ## 4. 客户端的简单应用 ### 事件:onopen、onmessages、onclose、onerror ```js const ws = new Websocket("wss://echo.websocket.org"); // onopen:用于执行连接成功后的回调 ws.onopen = function(evt){ consoel.log("===========连接开始=========="); ws.send("Hello WebSocket!"); } // onmessage:用于指定收到服务器数据后的回调 ws.onmessage = function(evt){ console.log("收到数据:" + evt.data); ws.close(); } // onopen:用于执行连接断开后的回调 ws.onclose = function(evt){ consoloe.log("==========连接关闭=========="); } ws.onerror = function(event) { consoloe.error("==========连接有误=========="); }; ``` 以上 `onopen、onmessage、onclose`如果需要指定多个回调,就可以使用监听形式 ```js // 以onopen为例 ws.addEventListener("open",(event)=>{ const code = event.code; const reason = event.reason; const wasClean = event.wasClean; }) ``` ### 方法:send() | close() ``` send() : 在连接成功后关闭前,发送消息(onopen后和onclose前才可发送消息)。 close() : 关闭连接 ``` `webSocket` 实例对象的`send()`,用于向服务器发送数据。 ```js ws.send("") ``` ### webSocket.readyState ``` Websocket.CONNECTING: 值为0,表示正在连接 Websocket.OPEN:值为1,表示连接成功 Websocket.CLOSING:值为2,表示连接关闭 Websocket.CLOSED:值为3,表示连接已经关闭,或者打开连接失败 ``` ```js switch (ws.readyState) { case WebSocket.CONNECTING: // do something break; case WebSocket.OPEN: // do something break; case WebSocket.CLOSING: // do something break; case WebSocket.CLOSED: // do something break; default: // this never happens break; } ``` ### webSocket.bufferedAmount bufferedAmount 是一个数值,表示还有多少字节的二进制数据没有发送,常用于判断发送数据是否结束 ```js var data = new ArrayBuffer(10000000); socket.send(data); if (socket.bufferedAmount === 0) { // 发送完毕 } else { // 发送还没结束 } // 限制客户端向服务器发送数据的频率 const ws = new WebSocket("ws://echo.websocket.org/updates"); ws.onopen = function(){ setInterval(()=>{ if(ws.bufferedAmount < 10240){ ws.send(getApplicationState()); } },1000); } ``` ## 5. 服务端的应用 ``` 常用的 Node 实现有以下三种。 - WebSockets - Socket.IO - WebSocket-Node ``` ## 6. 简单聊天室的实现——node ### 6.1 服务端 1. 创建一个空文件夹, `npm init -y` 初始化 2. `npm install websocket` 3. 创建`server.js` ```js // 因为websocket 是基于 http 协议的,因此 http 和 websocket 模块都需要被引入 const http = require("http"); const websocket = require("websocket").server; // 创建http实例,监听8080端口,作为第一次握手链接使用 const httpServer = http.createServer().listen(8080,()=>{ console.log("Hello localhost:8080!"); }) // 基于 http 协议实例化 websocket const websocketServer = new websocket({ // 配置依赖的握手 http 服务器 httpServer: httpServer, autoAcceptConnections:false }) // 创建通信线池:不用数据库的话,一般用数组来模拟一个虚拟线池 const connecttionArr = []; // 用npm包中的API on()监听事件 websocketServer.on('request', function(request) { // 获取连接实例,只要连接没断开,连接就一直需要用到 const connection = request.accept() // 每次接收一个链接,将它存放在数组里面 connecttionArr.push(connection) // 监听客户端发送的消息 connection.on('message', function(message) { console.log(message); // 消息转换编码 // 循环连接线池,取出所有的链接,广播到各个客户端 for(let i =0; i 输入姓名:
输入消息:
``` - 脚本配置 ```js // 用户名 const uName = document.getElementById('uName') // 文本框内容 const context = document.getElementById('context') // 点击按钮 const btn = document.getElementById('btn') // 要显示聊天室的区域 const charRoom = document.getElementById('charRoom') // 实例化 websocket const websocket = new WebSocket('ws://localhost:8080') // 建立链接 websocket.onopen = function() { console.log(websocket.readyState); } btn.onclick = ()=>{ // 将用户名和要发送的内容放在一个对象中,一起传送给后端 const info = { uName : uName.value, context: context.value } // 清空文本框的内容 uName.value = ""; context.value = ""; // 通过 websockte 发送消息 websocket.send(JSON.stringify(info)); } // 监听服务器传送来的消息 websocket.onmessage = function(event){ const chatInfo = JSON.parse(event.data); charRoom.innerHTML += ` ${chatInfo.uName}: ${chatInfo.context}
` } ``` ### 6.3 效果 ![请添加图片描述](D:/孟鑫岩/pictureBackUp/4faaed0bc89d4f38ad1844d02b5f7d1ctplv-k3u1fbpfcp-zoom-in-crop-mark1304000.awebp) ## 7 简单聊天室的实现——socket.io ### 7.1 服务端 1. 创建一个空文件夹, `npm init -y` 初始化 2. `npm install socket.io` 3. 创建`server.js` ```js // socket.io 框架 // 引入 http 模块的 createServer 方法 const { createServer } = require('http') // 引入 socket.io 的 Server 模块 const { Server } = require('socket.io') // 实例化 httpServer const httpServer = createServer(); // 初始化 socket.io const io = new Server(httpServer, { cors: { origin: '*', methods: ['GET', 'POST'] } }) // 使用 socket.io 来建立连接 io.on('connection', socket => { socket.on('sendMsg', data => { io.emit('pushMsg', data) console.log(data); }) }) // 创建服务器,进行监听 httpServer.listen(8080, function() { console.log('http://localhost:8080'); }) ``` ### 7.2 客户端 客户端需要引入`socket.io`框架 - 基本页面配置 ```html Document 输入姓名:
输入消息:
``` - 脚本配置 ```js // 用户名 const uName = document.getElementById('uName') // 文本框内容 const context = document.getElementById('context') // 点击按钮 const btn = document.getElementById('btn') // 要显示聊天室的区域 const charRoom = document.getElementById('charRoom') // 使用 socket.io 框架与服务器相连接 const socket = io.connect('http://localhost:8080') // 点击发送消息的事件 btn.onclick = function() { // 将用户名和要发送的内容放在一个对象中,一起传送给后端 const values = { uName: uName.value, context: context.value } // 清空文本框的内容 uName.value = '' context.value = '' // 通过 socket.io 框架向服务器发送数据 socket.emit('sendMsg', values) } // 使用 socket.on 方法监听服务端发送过来的数据 socket.on('pushMsg', data => { // 添加到页面上 charRoom.innerHTML += ` ${data.uName}: ${data.context}
` }) ```