聊聊websocket那些事


前端必备工具推荐网站(免费图床、API和ChatAI等实用工具):
http://luckycola.com.cn/

一、什么是websocket?

WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议。
它是 HTML5 中的一种新特性,能够实现 Web 应用程序和服务器之间的实时通信,比如在线聊天、游戏、数据可视化等。
相较于 HTTP 协议的请求-响应模式,使用 WebSocket 可以建立持久连接,允许服务器主动向客户端推送数据,避免了不必要的轮询请求,提高了实时性和效率。同时,WebSocket 的连接过程也比较简单,可以通过 JavaScript 中的 WebSocket API 进行创建和管理,并且可以和现有的 Web 技术如 HTML、CSS 和 JavaScript 无缝集成。
WebSocket 协议是基于握手协议(Handshake Protocol)的,它在建立连接时使用 HTTP/HTTPS 发送一个初始握手请求,然后服务器响应该请求,建立连接后就可以在连接上进行数据传输了。
聊聊websocket那些事_第1张图片

二、websocket原理

在实现websocket连线过程中,需要通过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” 。
在 WebSocket API,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket 协议中,为我们实现即时服务带来了两大好处:

1. Header:互相沟通的Header是很小的-大概只有 2 Bytes。
2. Server Push:服务器的推送,服务器不再被动的接收到浏览器的请求之后才返回数据,而是在有新数据时就主动推送给浏览器。

聊聊websocket那些事_第2张图片
聊聊websocket那些事_第3张图片
聊聊websocket那些事_第4张图片

三、websocket的特点和优势

1、特点:

1、WebSocket是一种在单个TCP连接上进行全双工通信的协议
2、WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
3、在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较高。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。
而 WebSocket 解决了 HTTP 的这几个难题。首先,当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通讯,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。
聊聊websocket那些事_第5张图片
聊聊websocket那些事_第6张图片

WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。
但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现。

2、优势:

3、劣势:

四、websocket与http异同点

1、异同点

聊聊websocket那些事_第7张图片
聊聊websocket那些事_第8张图片

2、心跳机制:

定义:为了保持NebSocket稳定的长连接,在连接建立之后,服务器和客户端之间通过心跳包来保持连接态,以防止连接因为长时间没有数据传输而被切断。
聊聊websocket那些事_第9张图片

let WS = connectWS();
let heartbeatStatus = 'waiting';

WS.addEventListener('open', () => {
    // 启动成功后开启心跳检测
    startHeartbeat()
})

WS.addEventListener('message', (event) => {
    const { data } = event;
    console.log('心跳应答了,要把状态改为已收到应答', data);
    if (data === '"heartbeat"') {
        heartbeatStatus = 'received';
    }
})

function startHeartbeat() {
    setTimeout(() => {
        // 将状态改为等待应答,并发送心跳包
        heartbeatStatus = 'waiting';
        WS.send('heartbeat');
        // 启动定时任务来检测刚才服务器有没有应答
        waitHeartbeat();
    }, 1500)
}

function waitHeartbeat() {
    setTimeout(() => {
        console.log('检测服务器有没有应答过心跳包,当前状态', heartbeatStatus);
        if (heartbeatStatus === 'waiting') {
            // 心跳应答超时
            WS.close();
        } else {
            // 启动下一轮心跳检测
            startHeartbeat();
        }
    }, 1500)
}

五、websocket的兼容性

聊聊websocket那些事_第10张图片

对于旧的浏览器该如何实现WebSocket的功能呢?下面就介绍一下几种常见的解决方案:

1. SockJS

SockJS是一个JavaScript库,它为浏览器提供了一个类似WebSocket的对象。
首先,它会优先使用原生的WebSocket;如果不支持,则使用streaming;如果streaming也不支持,则使用轮询(polling)。下面是支持的浏览器概览:
聊聊websocket那些事_第11张图片

既然模拟WebSocket双向通信,那么使用SockJS时,也要配合使用相应的服务器端的库,下面可以使用的服务器端库:

  • SockJS-node
  • SockJS-erlang
  • SockJS-tornado
  • SockJS-twisted
  • SockJS-ruby
  • SockJS-netty
  • SockJS-gevent (SockJS-gevent fork)
  • SockJS-go

2、使用例子:

前端

1、首先加载SockJS库

<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js">script>

2、初始化

var sock = new SockJS('https://mydomain.com/my_prefix');
 sock.onopen = function() {
     console.log('open');
     sock.send('test');
 };
 
 sock.onmessage = function(e) {
     console.log('message', e.data);
     sock.close();
 };
 
 sock.onclose = function() {
     console.log('close');
 };

后端-node

1、首先,安装sockjs-node:

npm install sockjs

2、初始化并且监听连接

var http = require('http');
var sockjs = require('sockjs');
 
var echo = sockjs.createServer({ sockjs_url: 'http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js' });
echo.on('connection', function(conn) {
    conn.on('data', function(message) {
        conn.write(message);
    });
    conn.on('close', function() {});
});
 
var server = http.createServer();
echo.installHandlers(server, {prefix:'/echo'});
server.listen(9999, '0.0.0.0');

六、客户端服务端双向通信几种方式

客户端和服务端的通信方式有很多种,大多数场景下都是由客户端主动发送数据给服务端,但在特定的场景下(如多人协作、在线游戏)客户端还需要和服务端保持实时通信,此时需要使用双向通信。
常见的双向通信方式包括 HTTP 短轮询(polling)、HTTP 长轮询(long-polling)、XHR Streaming、Server-Sent Events、Websocket 等。

1、HTTP 短轮询

客户端每隔特定的时间(比如 1s)便向服务端发起请求,获取最新的资源信息。该方式会造成较多的资源浪费,尤其当服务端内容更新频率低于轮询间隔时,就会造成服务端资源、客户端资源的浪费。除此之外,过于频繁的请求也会给服务端造成额外的压力,当服务端负载较高的时候,甚至可能导致雪崩等情况发生。
聊聊websocket那些事_第12张图片

2、HTTP 长轮询

解决了短轮询的一些问题,长轮询实现特点主要为当客户端向服务端发起请求后,服务端保持住连接,当数据更新响应之后才断开连接。然后客户端会重新建立连接,并继续等待新数据。此技术的主要问题在于,在重新连接过程中,页面上的数据可能会过时且不准确。
聊聊websocket那些事_第13张图片

3、XHR Streaming

可以维护客户端和服务端之间的连接。但使用 XHR Streaming 过程中,XMLHttpRequest对象的数量将不断增长,因此在使用过程中需要定期关闭连接,来清除缓冲区。
聊聊websocket那些事_第14张图片

iframe 流方式是在页面中插入一个隐藏的 iframe,利用其 src 属性在服务器和客户端之间创建一条长连接,服务器向 iframe 传输数据(通常是 HTML,内有负责插入信息的 java),来实时更新页面。

  • 优点:消息能够实时到达;浏览器兼容好
  • 缺点:服务器维护一个长连接会增加开销;IE、chrome、Firefox 会显示加载没有完成,图标会不停旋转。

4、WebSocket

它实现了浏览器与服务端全双工通信。前面我们提到,HTTP 短轮询、长轮询都会带来额外的资源浪费,因此 Websocket 在实现实时通信的同时,能更好地节省服务端资源和带宽。
Websocket 建立在 TCP 协议之上,握手阶段采用 HTTP 协议,但这个 HTTP 协议的请求头中,有以下的标识性内容。
Connection: Upgrade、Upgrade: websocket:表示这个连接将要被转换为 WebSocket 连接。
Sec-WebSocket-Key:向服务端提供所需的信息,以确认客户端有权请求升级到 WebSocket。
Sec-WebSocket-Protocol:指定一个或多个的 WebSocket 协议。
Sec-WebSocket-Version:指定 WebSocket 的协议版本。
如果服务端同意启动 WebSocket 连接,会在握手过程中的 HTTP 协议中返回包含Sec-WebSocket-Accept的响应消息,接下来客户端和服务端便建立 WebSocket 连接,并通过 WebSocket 协议传输数据。
聊聊websocket那些事_第15张图片

在海量并发和客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。
聊聊websocket那些事_第16张图片

七、websocket的应用

扫码登录实现
聊聊websocket那些事_第17张图片

八、ChatGPT即使通信-SSE

SSE(Server-Sent Events)是一种用于实现服务器主动向客户端推送数据的技术,也被称为“事件流”(Event Stream)。它基于 HTTP 协议,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。

1. 技术实现

SSE 基于 HTTP 协议,利用了其长连接特性,通过浏览器向服务器发送一个 HTTP 请求,建立一条持久化的连接。而 WebSocket 则是通过特殊的升级协议(HTTP/1.1 Upgrade 或者 HTTP/2)建立新的 TCP 连接,与传统 HTTP 连接不同。

2. 数据格式

SSE 可以传输文本和二进制格式的数据,但只支持单向数据流,即只能由服务器向客户端推送数据。WebSocket 支持双向数据流,客户端和服务器可以互相发送消息,并且没有消息大小限制。

适用于场景
chatGPT 返回的数据 就是使用的SSE 技术
实时数据大屏 如果只是需要展示 实时的数据可以使用SSE技术 而不是非要使用webSocket

前端调用

const sse = new EventSource('http://localhost:3000/api/sse' )

sse.addEventListener('open', (e) => {
    console.log(e.target)
})
//对应后端nodejs自定义的事件名lol
sse.addEventListener('lol', (e) => {
    console.log(e.data)
})

nodejs 后端操作

import express from 'express';
const app = express();
app.get('/api/sse', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream', //核心返回数据流
        'Connection': 'close'
    })
    const data = fs.readFileSync('./index.txt', 'utf8')
    const total = data.length;
    let current = 0;
    //mock sse 数据
    let time = setInterval(() => {
        console.log(current, total)
        if (current >= total) {
            console.log('end')
            clearInterval(time)
            return
        }
        //返回自定义事件名
        res.write(`event:lol\n`)
        /返回数据
        res.write(`data:${data.split('')[current]}\n\n`)
        current++
    }, 300)
})
app.listen(3000, () => {
    console.log('Listening on port 3000');
});

你可能感兴趣的:(websocket,网络协议,网络)