SSE介绍

什么是SSE

Server-Sent Events(SSE)是一种用于实现服务器向客户端实时推送数据的Web技术。与传统的轮询和长轮询相比,SSE提供了更高效和实时的数据推送机制。
SSE基于HTTP协议,允许服务器将数据以事件流(Event Stream)的形式发送给客户端。客户端通过建立持久的HTTP连接,并监听事件流,可以实时接收服务器推送的数据。

SSE的主要特点

  1. 简单易用:SSE使用基于文本的数据格式,如纯文本、JSON等,使得数据的发送和解析都相对简单。
  2. 单向通信:SSE支持服务器向客户端的单向通信,服务器可以主动推送数据给客户端,而客户端只能接收数据。

SSE的简单实现

node

// 引入相关模块
const http = require('http');

// 创建 HTTP 服务器
const server = http.createServer((req, res) => {

    // 设置跨域响应头
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    res.status = 200;
    res.respond = false;

    let sentText = '这一段很长的文本,主要用来测试sse技术的实施,这一段很长的文本,主要用来测试sse技术的实施'

    let count = 0;
    const interval = setInterval(() => {
        const data = { message: sentText[count] };
        const eventData = `data: ${JSON.stringify(data)}\n\n`;
        res.write(eventData);
        if (count >= sentText.length) {
            count = 0;
            clearInterval(interval);
            res.end();
        }
        count++;
    }, 500);

    // 监听连接关闭事件,清除定时器
    res.on('close', () => {
        count = 0;
        clearInterval(interval);
    });
})
    // 启动服务器
server.listen(3000, () => {
    console.log('Server is listening on port 3000');
});

html

const button = document.getElementsByClassName('send')[0];
const input = document.getElementsByTagName('textarea')[0];
const closeButton = document.getElementsByClassName('close')[0];
button.onclick = function () {
    onOpen();
}

closeButton.onclick = function () {
    eventSource.close();
    input.value += `已经关闭连接`;
}
const onOpen = () => {
    eventSource = new EventSource('http://localhost:3000');
    eventSource.onmessage = function (e) {
        if (!(JSON.parse(e.data).message)) {
            eventSource.close();
            return;
        }
        input.value += `${JSON.parse(e.data).message}`;
    };
    eventSource.onopen = () => {
        input.value += `SSE连接成功,准备接收数据...`;
    }
};

WebSocket

WebSocket的介绍

按照传统的定义,WebSocket是一种双工协议,主要用于客户端-服务器通信通道。它本质上是双向的,这意味着通信在客户端与服务器之间来回发生。使用 WebSocket 开发的连接只要任何参与方中断连接就会持续存在。一旦一方断开连接,另一方将无法进行通信,因为连接会在其前面自动断开。WebSocket需要HTTP的支持来发起连接。说到它的实用性,当涉及到数据的无缝流和各种不同步流量时,它是现代 Web 应用程序开发的支柱。

WebSocket的特点

  1. 提供全双工通信,还可以在TCP之上启用消息流;
  2. WebSocket协议规范将ws(WebSocket)和wss(WebSocket Secure)定义为两个新的统一资源标识符(URI)方案,分别对应明文和加密连接。
  3. WebSocket 的默认端口也选择了 80 和 443,因为现在互联网上的防火墙屏蔽了绝大多数的端口,只对 HTTP 的 80、443 端口“放行”,所以 WebSocket 就可以“伪装”成 HTTP 协议,比较容易地“穿透”防火墙,与服务器建立连接。
  4. WebSocket 更侧重于“实时通信”,而 HTTP/2 更侧重于提高传输效率。
  5. WebSocket有比较好的兼容性,除了IE浏览器以外基本都兼容。
//判断浏览器的兼容性
if (window.WebSocket){
console.log("This browser supports WebSocket!");
} else {
console.log("This browser does not support WebSocket.");
}

WebSocket的简单实现

node

// 引入相关模块
const http = require('http');
const wss = require('nodejs-websocket')

// 创建 HTTP 服务器
const server = wss.createServer(connection => {
    //处理客户端发送过来的消息	
    console.log("服务已经启动");
    connection.on("text", function (data) {

        connection.sendText("服务器端返回数据:" + data)

        setTimeout(()=>{
            connection.sendText("服务器端返回数据:" + '服务端给前端发送了一段文本')
        },10000)
        //监听关闭
        connection.on("close", function (code, reason) {
            console.log("Connection closed")
        })
        //监听异常
        connection.on("error", (err) => {
            console.log('服务异常关闭...')
        })
    })
});

// 启动服务器
server.listen(3000, () => {
    console.log('Server is listening on port 3000');
});

html

<body>
    <input type="text" id="input" onchange="change()">
    <div class="reserve"></div>
    <button onclick="onCancel()">关闭</button>
    <button onclick="onCreateWss()">重启</button>

</body>
<script>
    const input = document.getElementById('input');
    const reserve = document.getElementsByClassName('reserve')[0];

    let ws = null;

    const change = function () {
        // 向服务器发送消息
        ws.send(input.value);
    }

    const onCancel = function () {
        ws.close();
    }


    const onStart = function () {
        ws.onopen = function (e) {
            console.log("连接服务器成功");
            ws.send('');
        }
        ws.onclose = function (e) {
            console.log("服务器关闭");
        }
        ws.onerror = function () {
            console.log("连接出错");
        }
        // 接收服务器的消息
        ws.onmessage = function (e) {
            let message = "message:" + e.data + "";
            reserve.innerHTML = e.data;
            // console.log(message);
        }
    }

    const onCreateWss = function () {
        try {
            ws = new WebSocket('ws://localhost:3000');
            onStart();
        } catch (err) {
            console.log(err)
        }

    }

    onCreateWss();


</script>

WebSocket心跳检测的介绍

  1. 心跳检测的原因
    判断WebSocket的连接是否正常,一般采用定时发送简单的通讯包,如果在指定时间段内未收到对方响应,则判断对方已经当掉。用于检测TCP的异常断开。基本原因是服务器端不能有效的判断客户端是否在线也就是说,服务器无法区分客户端是长时间在空闲,还是已经掉线的情况。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。

  2. 如何进行心跳检测
    代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息。如果服务端几分钟内没有收到客户端信息则视客户端断开。比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。

  3. 心跳检测的实现

    客户端发送心跳包
    var timer = setInterval(function() {
      if (websocket.readyState == WebSocket.OPEN) {
        websocket.send("heartbeat");
      } else {
        clearInterval(timer);
      }
    }, 1000 * 10);
    
    服务器发送心跳包
    var timer = setInterval(function() {
      websocket.send("heartbeat");
    }, 1000 * 10);
    
    websocket.onmessage = function(evt) {
      if (evt.data === "heartbeat") {
        return;
      }
      // 处理服务器传来的数据
    };

WebSocket心跳的注意事项:

  1. 心跳包的发送周期
    心跳包的发送周期需要根据实际网络情况来决定,过长的周期会增加连接的延迟,过短的周期会增加服务器的压力。
  2. 心跳包的内容
    心跳包的内容需要和服务器约定好,并且在接收到数据包时进行忽略。
  3. 客户端断开连接时的处理
    如果客户端断开了连接,需要及时清除定时器,避免浪费服务器资源。
  4. 服务器断开连接时的处理
    如果服务器断开了连接,需要及时进行重连或其他处理,避免影响客户端的使用。
  5. 避免网络拥塞
    过多的心跳包会导致网络拥塞,需要在发送心跳包时考虑平衡发送数量和频率。

心跳检测部分代码来源于 心跳检测部分代码
掘金心跳检测