• 网络-WebSocket




    前言

    本文主要记录WebSocket的简单介绍和使用,完成群聊的demo


    一、WebSocket简介

    WebSocket是一种通信协议,它通过单个TCP连接提供全双工的通信通道。
    它允许客户端和服务器之间进行实时的双向通信。
    与传统的HTTP请求不同,WebSocket建立了客户端和服务器之间的持久连接,从而实现了高效和低延迟的通信。

    要建立WebSocket连接,客户端向服务器发送WebSocket握手请求,服务器则以WebSocket握手响应进行回应。
    一旦连接建立,客户端和服务器都可以异步地发送消息。

    WebSocket采用基于消息的通信模型,消息以帧的形式通过WebSocket连接发送。
    这些帧可以是二进制或文本形式,具体取决于应用程序的需求。
    该协议还支持消息分片、用于保持连接的ping/pong消息以及用于优雅终止连接的关闭消息等功能。
    WebSocket得到了大多数现代Web浏览器的支持,并且可以在服务器端应用程序中使用提供WebSocket功能的库或框架

    应用场景

    • 实时性要求高,如:聊天、游戏、数据可视化
    • 需要频繁交换数据,如:实时协作工具、文件管理器
    • 需要推送服务的应用,如:实时数据监控、通知系统
    • 跨平台应用,如:桌面应用、移动端应用

    它提供了比长轮询或服务器推送事件(SSE)更高效的替代方案。

    原理

    • 握手阶段:客户端发送一个HTTP请求给服务器,请求升级为WebSocket协议。在请求头中包含一些特殊的字段,如UpgradeConnection,表明客户端希望升级到WebSocket协议,这里也可以使用这里也可以使用WebSocket API进行通讯API进行通讯,实际也是发送GET请求带特殊请求头。服务器收到请求后,如果支持WebSocket协议,则返回一个HTTP响应,状态码为101 Switching Protocols,表示升级成功。

    101

    • 建立连接:一旦握手成功,客户端和服务器之间建立了一个持久的双向连接。这个连接是基于TCP的,可以实现全双工通信,即客户端和服务器可以同时发送和接收消息。
      数据传输:客户端和服务器可以通过WebSocket连接发送和接收消息。消息以帧的形式进行传输,可以是二进制数据或文本数据。帧包含了一些控制信息,如消息类型、消息长度等。客户端和服务器可以异步地发送和接收消息,实现实时的双向通信。
    • 保持连接:WebSocket连接是持久的,不会像传统的HTTP请求那样每次都需要重新建立连接。为了保持连接的活跃状态,客户端和服务器可以定期发送心跳消息,以确保连接不会断开。
    • 关闭连接:当客户端或服务器希望关闭WebSocket连接时,可以发送一个关闭帧。接收到关闭帧的一方会响应一个关闭帧,然后双方的连接会被正常关闭。

    二、使用

    • 创建ws对象
    const ws = new WebSocket('ws://localhost:8080');
    
    • 1
    • 监听连接成功
    ws.onopen = function () {
                console.log('已连接')
            }
    
    • 1
    • 2
    • 3
    • 发送消息
    ws.send('发送的消息')
    
    • 1
    • 接收消息
     ws.onmessage = function (e) {
                console.log(e.message)
            }
    
    • 1
    • 2
    • 3
    • 断开连接
    ws.close()
    
    • 1
    • 监听断开连接
    ws.onclose = function() {
                console.log('WebSocket连接已关闭');
            };
    
    • 1
    • 2
    • 3
    • 监听连接错误
    ws.onerror = function(error) {
                console.error('WebSocket连接发生错误:', error);
            };
    
    • 1
    • 2
    • 3
    • 事件监听器
      WS是可以添加事件监听器的,如open、message、close、error等与属性效果是一致的。
     ws.addEventListener('open',function (event) {
                console.log('连接成功')
                isConnected = true;
            })
    
    • 1
    • 2
    • 3
    • 4
    ws.addEventListener('message',(e)=>{
                let li = document.createElement('li')
    
                let data = JSON.parse(e.data)
                if (data.type == 2){
                    li.innerText = data.message
                    list.appendChild(li)
                }else {
                    console.log(data.message)
                }
            })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    心跳监测

    socket连接在长时间不使用、网络波动、弱网状况下有可能断开连接,需要进行心跳监测来保证连接存活。在断开连接时,实现重连,在进行心跳消息发送时,客户端需要将心跳监测信息过滤掉。

     let heartInreaval = null
        const heartCheck = () =>{
            // 等于open 发送心跳
            if (socket.readyState === ws.OPEN){
                socket.send(JSON.stringify({
                    type:state.HEART,
                    message:'心跳检测'
                }))
            }else {
                clearInterval(heartInreaval)
            }
        }
        setInterval(heartCheck,5000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    广播消息

    在后端直接使用send发送消息只能针对点对点的,不能将消息发送给全部连接,需要使用clients属性遍历发送,达到广播的效果

     // 广播消息
            wss.clients.forEach((client)=>{
                client.send(JSON.stringify({
                    type:state.MESSAGE,
                    message:e.toString()
                }))
            })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    三、群聊demo

    前端:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <div>
        <ul id="list"></ul>
        <input type="text" id="input">
        <button id="send">发送</button>
        <button id="stop">断开连接</button>
    </div>
    </body>
    <script>
        // 连接状态
        let isConnected = false;
        let input = document.getElementById('input')
        let btn = document.getElementById('send')
        let list = document.getElementById('list')
        let stop = document.getElementById('stop')
        function connectWebSocket() {
            // 创建ws实例
            const ws = new WebSocket('ws://localhost:8080');
            // 监听连接成功
            ws.addEventListener('open',function (event) {
                console.log('连接成功')
                isConnected = true;
            })
            btn.addEventListener('click',function () {
                if (input.value){
                    // send发送消息
                    ws.send(input.value)
                    input.value = ''
                }
            })
            ws.addEventListener('message',(e)=>{
                let li = document.createElement('li')
    
                let data = JSON.parse(e.data)
                if (data.type == 2){
                    li.innerText = data.message
                    list.appendChild(li)
                }else {
                    console.log(data.message)
                }
            })
            //断开连接
            stop.addEventListener('click',()=>{
                ws.close()
            })
            // ws.onopen = function () {
            //     console.log('已连接')
            // ws.send('发送的消息')
            // ws.onmessage = function (e) {
            //     console.log(e.message)
            // }
            // }
            ws.onclose = function() {
                console.log('WebSocket连接已关闭');
                isConnected = false;
                // 进行重连
                reconnect();
            };
    
            // 监听WebSocket连接发生错误事件
            ws.onerror = function(error) {
                console.error('WebSocket连接发生错误:', error);
                isConnected = false;
                // 进行重连
                reconnect();
            };
        }
        // 重连函数
        function reconnect() {
            if (!isConnected) {
                console.log('尝试重新连接...');
                setTimeout(connectWebSocket, 5000);
            }
        }
        connectWebSocket()
    
    </script>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    后端:这里使用Node

    //安装 ws
    const ws =require('ws')
    // 创建socket服务
    const wss = new ws.Server({port:8080},()=>{
        console.log('socket服务启动成功')
    })
    
    //心跳枚举
    const state = {
        HEART:1,
        MESSAGE:2
    }
    
    // 监听客户端连接
    wss.on('connection',(socket)=>{
        //监听客户端消息
        console.log('客户端连接成功')
        socket.on('message',(e)=>{
            console.log(e.toString())
            // 广播消息
            wss.clients.forEach((client)=>{
                client.send(JSON.stringify({
                    type:state.MESSAGE,
                    message:e.toString()
                }))
            })
        })
        let heartInreaval = null
        const heartCheck = () =>{
            // 等于open 发送心跳
            if (socket.readyState === ws.OPEN){
                socket.send(JSON.stringify({
                    type:state.HEART,
                    message:'心跳检测'
                }))
            }else {
                clearInterval(heartInreaval)
            }
        }
        setInterval(heartCheck,5000)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    群聊


    总结

    WebSocket通过握手阶段建立持久的双向连接,然后通过帧的方式传输数据,实现实时的双向通信。它相比传统的HTTP请求具有更低的延迟和更高的效率,适用于需要实时更新的Web应用程序。

  • 相关阅读:
    34 IDEA 架包,字体...
    关于vantUI的导航组件tab标签页在ios和安卓中运用遇到的坑
    Vue模板语法集(上)
    一个电子小说阅读系统源码,thinkphp开发的小说系统系统
    vscode搭建springboot开发环境
    【电商运营】不知道怎么做网站优化?这里有你需要知道的一切!
    【生物信息学】基因差异分析Deg(数据读取、数据处理、差异分析、结果可视化)
    Runable和Callable的区别?首先要搞清楚Thread以及FutureTask!
    搭建自己的pypi服务器
    [附源码]计算机毕业设计JAVA影院售票系统
  • 原文地址:https://blog.csdn.net/smznbhh/article/details/133722723