新手小白研究一下websocket

WebSocket是双向的,在客户端-服务器通信的场景中使用的全双工协议,与HTTP不同,它以ws://或wss://开头。它是一个有状态协议,这意味着客户端和服务器之间的连接将保持活动状态,直到被任何一方(客户端或服务器)终止。在通过客户端和服务器中的任何一方关闭连接之后,连接将从两端终止。

如果其中任何一方(客户端服务器)宕掉或主动关闭连接,则双方均将关闭连接。套接字的工作方式与HTTP的工作方式略有不同,状态代码101表示WebSocket中的交换协议。

原理

当客户端要和服务端建立 WebSocket 连接时,在客户端和服务器的握手过程中,客户端首先会向服务端发送一个 HTTP 请求,包含一个 Upgrade 请求头来告知服务端客户端想要建立一个 WebSocket 连接。

Upgrade: websocketConnection: UpgradeSec-WebSocket-Key: ************==Sec-WebSocket-Version: **

Server正确接收后,会返回一个响应头:

Upgrade:websocketConnnection: UpgradeSec-WebSocket-Accept: ******

和TCP、HTTP协议的关系

WebSocket是基于TCP的独立的协议。和HTTP的唯一关联就是HTTP服务器需要发送一个“Upgrade”请求,即101 Switching Protocol到HTTP服务器,然后由服务器进行协议转换。

优点

较少的控制开销,在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于 HTTP 请求每次都要携带完整的头部,此项开销显著减少了。

更强的实时性,由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于HTTP请求需要等待客户端发起请求服务端才能响应,延迟明显更少;

长连接,保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。

双向通信、更好的二进制支持。与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易被屏蔽,能通过各种 HTTP 代理服务器。

示例

后端使用node.js来hostwebsocket服务,使用ws这个三方库:当然java和.net也都有很好的库支持,这里不做介绍。

Server端代码如下,记得装依赖包:

- express

- ws

use strict;const express = require(express);const path = require(path);const { createServer } = require(http);const WebSocket = require(ws);const app = express();app.use(express.static(path.join(__dirname, /public)));const server = createServer(app);const wss = new WebSocket.Server({ server });// httpapp.get(/api/word, function (req, res) {res.send({ message: hello - http })})// wswss.on(connection, function (ws) {ws.send(JSON.stringify({ message: hello - websocket }), function () {});ws.on(message, function (data, isBinary) {wss.clients.forEach(function each(client) {if (client !== ws && client.readyState === WebSocket.OPEN) {client.send(data, { binary: isBinary });}});});ws.on(open, function() {console.log(connected);ws.send({ message: hello - websocket - open });});ws.on(close, function () {console.log(closed);});});server.listen(8080, function () {console.log(Listening on :8080);});

Client端代码如下:

<!doctype html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width"></head><body><button id="connect">建立连接</button><input id="message-text" type="text"><button id="send">发送</button><button id="close">关闭</button><div id="message-list"></div></body><script type="text/javascript">let ws;document.getElementById("connect").onclick = function () {ws = new WebSocket(ws://localhost:8080);ws.onopen = function(evt) {console.log(建立连接,状态: + ws.readyState);};ws.onmessage = function(evt) {const data = JSON.parse(evt.data)console.log("状态:" + ws.readyState + ";服务端返回数据:", data);const list = document.getElementById("message-list");list.innerHTML = `${list.innerHTML} <p>${data.message}</p>`;};ws.onerror = function(evt) {console.log(发生错误,状态: + ws.readyState);};ws.onclose = function(evt) {console.log("连接关闭,状态:", ws.readyState);}; }document.getElementById("send").onclick = function () {const val = document.getElementById("message-text").valueconst data = {message: val}const list = document.getElementById("message-list");list.innerHTML = `${list.innerHTML} <p>${data.message}</p>`;ws.send(JSON.stringify(data));}document.getElementById("close").onclick = function () {ws.close();}</script></html>

效果如下:

打开两个浏览器,edge和chrome,两边都要先建立连接,然后就可以通信了。当一方关闭连接,另一方就收不到消息了。