Connection: Upgrade 是与Upgrade: websocket 进行配合使用的,它的作用是告诉服务器,不要把这个请求当作普通的 HTTP 请求来处理,而是要关注 “Upgrade” 请求头中的内容,按照要求进行协议的升级。
服务器收到客户端的升级请求后,如果支持 Web Socket 协议,就会返回一个响应来完成握手过程。响应状态码通常是 101,表示 协议切换。响应头也会包含和客户端请求对应的 Upgrade 和 Connection 字段,确认连接升级。Sec - WebSocket - Accept ,它是根据客户端请求中的 Sec - WebSocket - Key 生成的,用于安全验证。通过这个握手过程,双方就建立了一个稳定的 Web Socket 连接这样,这个连接是基于 TCP的,为后续的双向通信做好了准备。
我们从下图中可以看出,Web socket全双工通道的特点,客户端可以向服务端主动发送消息,服务端也可以推送数据到服务端。具体的数据格式,我们可以通过抓包软件来进行抓包看下。
在整个通信过程中,Web Socket 协议要求客户端和服务器都要维护连接的状态。连接状态包括连接是否打开、正在关闭或者已经关闭等。客户端和服务器通过心跳机制(发送周期性的小数据包来检测对方是否还在线)或者其他自定义的连接检测方法来确保连接的稳定性。
当需要关闭 WebSocket 连接时,无论是客户端还是服务器都可以发起关闭请求。关闭请求也是通过发送一个特定的帧(关闭帧)来实现的。对方在收到关闭帧后,会进行一些必要的清理工作,如释放资源等,然后关闭连接。
<div id="app"> <input v-model="message" placeholder="输入要发送的消息" /> <button @click="sendMessage">发送消息</button> <div> <h3>收到的回复记录:</h3> <ul> <li v-for="(msg, index) in receivedMessages" :key="msg">{{ msg }}</li> </ul> </div> </div> <script> const app = new Vue({ el: "#app", data() { return { message: "", receivedMessages: [], // 新增数组用于存储所有收到的消息 socket: null, }; }, mounted() { // 创建WebSocket连接,这里的地址要和后端服务器监听的地址对应,这里假设后端在本地3000端口 this.socket = new WebSocket("ws://localhost:3000"); // 连接成功时触发的事件 this.socket.addEventListener("open", () => { console.log("已连接到WebSocket服务器"); }); // 接收服务器发送消息的事件 this.socket.addEventListener("message", (event) => { const receivedMsg = event.data; this.receivedMessages.push(receivedMsg); // 将收到的消息添加到数组中 }); // 堆代码 duidaima.com // 连接关闭时触发的事件 this.socket.addEventListener("close", () => { console.log("与WebSocket服务器的连接已关闭"); }); }, methods: { sendMessage() { if (this.socket.readyState === WebSocket.OPEN) { this.socket.send(this.message); this.message = ""; } else { console.log("WebSocket连接未就绪,无法发送消息"); } }, }, }); </script>模拟服务端发送请求
const WebSocket = require('ws'); // 创建WebSocket服务器实例,监听在3000端口,你可以根据需求修改端口号 const wss = new WebSocket.Server({ port: 3000 }); // 用于存储已连接的客户端WebSocket实例,方便后续向所有客户端发送消息等操作 const clients = []; // 当有客户端连接时触发的事件 wss.on('connection', (ws) => { console.log('客户端已连接'); clients.push(ws); // 接收客户端发送的消息 ws.on('message', (message) => { console.log(`收到客户端消息: ${message}`); // 这里简单地将收到的消息加上一个后缀后再发回客户端 const responseMessage = `你发送的消息是:${message}`; ws.send(responseMessage); }); // 当客户端关闭连接时触发的事件 ws.on('close', () => { console.log('客户端已断开连接'); const index = clients.indexOf(ws); if (index > -1) { clients.splice(index, 1); } }); }); // 【注意: 模拟一个定时任务,每隔1秒向所有已连接的客户端发送一条消息,你可以编写自己的业务代码】 setInterval(() => { const messageToSend = '这是服务端主动发送的消息,当前时间:' + new Date().toLocaleString(); clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(messageToSend); } }); }, 1000); console.log('WebSocket服务器已启动,正在监听3000端口...');实现效果
const express = require('express'); const app = express(); const port = 3000; // 设置响应头,表明这是一个SSE流 app.get('/events', (req, res) => { res.setHeader('Content-Type','text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); // 【注意:模拟定时发送数据(实际应用中需要根据真实业务逻辑触发发送)!!!】 const interval = setInterval(() => { const data = `data: { "time": "${new Date().toLocaleString()}"}\n\n`; res.write(data); }, 3000); // 当客户端关闭连接时,清除定时器 req.on('close', () => { clearInterval(interval); }); }); app.listen(port, () => { console.log(`服务器运行在 http://localhost:${port}`); }); 模拟客户端代码 mounted() { const source = new EventSource('http://localhost:3000/events'); source.onmessage = (event) => { const data = JSON.parse(event.data); // data 就是发送的消息 this.message = data.message; }; source.onerror = (error) => { console.log('SSE连接出错:', error); }; }实现效果