# 基于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 效果

## 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