学习websocket,原来这么简单
阅读原文时间:2023年08月21日阅读:2

简单介绍 websocket

WebSocket是一种在TCP连接上进行全双工通信的协议。
WebSocket通信协议于2011年被IETF定为标准。
然后WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,
允许服务端主动向客户端推送数据。
在WebSocket API中,浏览器和服务器只需要完成一次握手。
两者之间就直接可以创建持久性的连接,并进行双向数据传输。

我们为什么要使用 websocket

我们都知道 HTTP 协议有一个缺陷。
那就是通信只能由客户端发起。服务端没法主动发起。
有些场景我们需要让客户端实时知道消息。
虽然可以使用轮询来解决这个问题。
但是非常浪费资源,因为要不停连接,断开后又继续连接。
这个时候我们就需要使用 websocket

websocket 应用场景

1.弹幕媒体聊天 [客服推送]
2.协同编辑 [wps]
3.基于位置的应用 [外卖]
4.体育实况更新
5.股票帮金报价实时更新

websocket 和HTTP请求的不同点:

1.WebSocket是双向通信协议,HTTP是单向的
WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。而HTTP是单向的

2.连接方式不同
WebSocket是需要浏览器和服务器握手进行建立连接的。
http是浏览器发起向服务器的连接,服务器预先并不知道这个连接。

3.协议开头不同
websocket:websocket的协议是以 ws/wss 开头。
http 对应的是 http/https

4.连接长度不同
websocket是持久连接。http 是短连接。

简单实现一下 WebSocket

node创建WebSocket
我们需要先下载 npm install ws
ws是一个简单易用、快速且经过全面测试的WebSocket客户端和服务器实现。
我们就直接使用就好了


const express = require('express')
const app = express()
const port = 3000
// 托管静态资源-express.static()
app.use(express.static('public'))
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

// 引入 ws,并且解构使用我们需要的 WebSocketServer
const { WebSocketServer } = require('ws');
// 创建WebSocketServer的实例wss,并指定端口号
const wss = new WebSocketServer({ port: 8080 });
// 通过实例wss进行监听
wss.on('connection', function connection(ws) {
  ws.on('error', console.error);
  // 接收客户端发送过来的消息
  ws.on('message', function message(data) {
    console.log('客户端发送过来的消息', data);
  });
  ws.send('服务端发送的消息');
});
客户端 WebSocket 的创建
我们需要创建 WebSocket 的实例同时指定端口号。
然后通过 onopen  onmessage onerror 来判断ws的状态事件
下面我们就来简单写一下


<body>
    <h1>欢迎 学习WebSocket </h1>
</body>
<script>
// 创建 WebSocket并指定一个地址
let ws = new WebSocket('ws://localhost:8080')
// 是否链接成功
ws.onopen = (() => {
  console.log("链接成功")
})
// 监听服务端发送过来的消息
ws.onmessage = ((messObj) => {
  console.log('发送过来的消息', messObj)
})
// 连接失败
ws.onerror = (() => {
  console.log('连接失败')
})
</script>

客户端发送消息给服务端

我们需要写一个简单的html界面,
获取到 文本域中的内容。
然后通过 ws.send(textareaValue) 给服务端发送消息。
服务端在
ws.on('message', function message(data) {
  console.log(data)
})
这个方法中接收客户端的消息
data就是客户端发送过来的消息
如果是二进制数据,我们可以使用 data.toString()进行转换


<body>
    <h1>欢迎 学习WebSocket </h1>
    <textarea rows="10" cols="30" id="textarea" placeholder="请输入内容"></textarea>
    <button onclick="sendHandler()">点击按钮</button>
</body>

// 创建 WebSocket并指定一个地址
let ws = new WebSocket('ws://localhost:8080')

let textarea=document.getElementById('textarea')
function sendHandler(){
  const textareaValue = textarea.value;
  // 通过ws.send给客户端发送消息
  ws.send(textareaValue)
}

WebSocket 实现广播

我们要做一个广播。
就有点类似在QQ群中发送一条消息,
在这个群中的其他用户可以接收到这条消息。
WebSocket 连接成功后,wss.clients来获取当前服务端连接了多少个客户端。
wss.clients是一个Set类型的。我们通过 forEach 来进行遍历。
我们通过 client.readyState 的状态是否是 WebSocket.OPEN
然后调用 client.send(data, { binary:false  })就数据发送出去


// 引入 ws,并且解构使用我们需要的 WebSocketServer
const { WebSocketServer, WebSocket } = require('ws');
// import { WebSocketServer } from 'ws';
// 创建WebSocketServer的实例wss,并指定端口号
const wss = new WebSocketServer({ port: 8080 });
// 通过实例wss进行监听
wss.on('connection', function connection(ws) {
  ws.on('error', console.error);
  // 客户端
  ws.on('message', function message(data) {
    console.log('received: %s', data);
    // 服务器广播
    // 广播到所有连接的WebSocket客户端,包括其自身。
    console.log('连接了多少个',wss.clients.size)
    wss.clients.forEach(function each(client) {
      // 判断当前连接服务端的所有客户端的ws。
      if (client.readyState === WebSocket.OPEN) {
        // binary表示数据是否是二进制。binary:false表示不是二进制
        client.send(data, { binary:false  });
      }
    });
  });
  ws.send('服务端发送的消息');
});

发现自己也被广播了

假设不同的浏览器代码一个用户。
通过上面的图片,我们发现谷歌浏览器发送一条消息。
虽然火狐浏览器接收到了。但是谷歌发送的消息自己也接收到了。
这样明显是不正确的。因此。我们处理一下。
自己发送的消息不需要广播自己的。
我们使用 client!==ws 来进行判断


// 引入 ws,并且解构使用我们需要的 WebSocketServer
const { WebSocketServer, WebSocket } = require('ws');
// import { WebSocketServer } from 'ws';
// 创建WebSocketServer的实例wss,并指定端口号
const wss = new WebSocketServer({ port: 8080 });
// 通过实例wss进行监听
wss.on('connection', function connection(ws) {
  ws.on('error', console.error);
  // 客户端
  ws.on('message', function message(data) {
    console.log('received: %s', data);
    // 服务器广播
    // 广播到所有连接的WebSocket客户端,包括其自身。
    console.log('连接了多少个',wss.clients.size)
    wss.clients.forEach(function each(client) {
      // 判断当前连接服务端的所有客户端的ws用户。
      // client!==ws 表示不是当前发送的用户[不要自己广播自己]
      if (client!==ws && client.readyState === WebSocket.OPEN) {
        // binary表示数据是否是二进制。binary:false表示不是二进制
        client.send(data, { binary:false  });
      }
    });
  });
  // 第一次连接成功后发送的消息。
  ws.send('');
});

实现关闭页面关闭 WebSocket

当我们离开连接 WebSocket的页面离开后,
我们应该关闭当前连接。
可是现在我们点击去user页面的时候,并没有关闭连接。
这显然是不太好的。所以我们需要优化一下。
当用户离开,我们需要关闭连接


// 关闭当前窗口,按下 F5 或点击以下链接触发 onbeforeunload 事件
document.body.onbeforeunload = function () {
  ws.close()
  return 'close'
}

//离开当前页面会触发
window.onunload = function (e) {
  var e = window.event || e;
  console.log('===>',e)
  // 离开页面,关闭WebSocket
  ws.close()
}

携带token

我们在连接 WebSocket 的时候,
没有鉴权,也就是说无法判断当前这个用户的信息。
这样是不安全的。
因此我们在连接 WebSocket 需要携带token.
通过这样的形式携带  ws://localhost:8080?token=' + 'tokne_zahngsan'


// 创建 WebSocket并指定一个地址,并且携带一个token
let ws = new WebSocket('ws://localhost:8080?token=' + 'tokne_zahngsan')
//省略其他代码


wss.on('connection', function connection(ws,req) {
  // 获取前端携带的token
  console.log('req', req.url.slice(8))
})