当前位置:   article > 正文

Http短轮询、WebSocket与SSE三种通讯方式和它们之间的区别

Http短轮询、WebSocket与SSE三种通讯方式和它们之间的区别

概要

本篇文章着重讲述了Http、WebSocket与SSE三种通讯方式的特点与实现方式以及它们之间的区别。

Http

  1. 短轮询(Short Polling)
  • 工作原理:一般是由客户端每隔一段时间(如每隔5s)向服务器发起一次普通 HTTP 请求,询问是否有新的数据,服务器响应后返回最新的数据。
  • 优点:简单易实现,支持跨浏览器,适用于低延迟要求不高的场景。
  • 缺点:会造成频繁的网络请求和服务器压力,无论有无新数据都会发起请求。

代码实现

function startPolling(url, interval=5000) {
  setInterval(async () => {
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }
      const data = await response.json();
      console.log(data);
    } catch (error) {
      console.error(error);
    }
  }, interval);
}
 
// 使用方法:
startPolling('https://api.example.com/data', 5000); // 每5秒轮询一次

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这段代码定义了一个startPolling函数,它接收一个URL和轮询间隔(以毫秒为单位,默认5000毫秒)。使用setInterval来周期性地发送HTTP请求到指定的URL,并在收到响应后打印结果。如果发生错误,会在控制台中记录错误信息。

WebSocket

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

  • 工作原理:Websocket 是一种全双工通信协议,通过建立持久连接,双方可以实时进行数据传输。
  • 优点:实时性更好,支持服务器主动推送数据,减少了不必要的请求开销。
  • 缺点:相对短轮询更复杂,对服务器和客户端都有一定的要求,可能受到网络限制。

代码实现
前端

<script>
// 1.实例化
let ws = new WebSocket('ws://localhost:3001')
// 2.open
ws.onopen = function () {
    // 客户端给服务端发送的数据
    ws.send('13312434')
     console.log("连接成功")
}
// 3.message
ws.onmessage = function (e) {
    // 服务端给客户端的数据:客户端进行接收
     console.log("接收消息",e.data)
}
// 4.close
ws.onclose = function () {
     console.log("连接关闭")
}
// 5.error
ws.onerror = function () {
    console.log("连接错误")
}
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

nodejs(服务器)完整代码

var express = require('express');
var router = express.Router();

//引入ws模块
const WebSocket = require('ws').Server;
const port = 3001;

//创建服务
const server = new WebSocket({ port }, () => {
    console.log('webSocket服务开启');
})

const errorHandler = (error) => {
    console.log('errorHandler====>客户端出错了', error)
}

const closeHandler = (e) => {
    console.log('closeHandler====>客户端断开了', e)
}
function messageHandler(data) {
    console.log('messageHandler====>接收客户端消息', JSON.parse(data))
    this.send(JSON.stringify(JSON.parse(data)))
}

const connectHandler = (ws) => {
    console.log('客户端已连接');
    //监听客户端出错
    ws.on('error', errorHandler);
    //监听客户端断开链接
    ws.on('close', closeHandler);
    //监听客户端发来的消息
    ws.on('message', messageHandler);
}
//建立连接
server.on('connection', connectHandler);

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
  • 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

除了最基本的实现外,WebSocket还具有心跳检测机制与断线重连机制。

  • 心跳检测机制:一种用于检测系统、应用程序或网络连接状态的技术。基本原理是定期发送小型数据包(称为心跳包或心跳消息)以确认远程设备或连接仍然处于活动状态。如果一段时间内没有收到心跳响应,系统就会认为连接已断开或设备不再可用。服务端和客户端都会定时发送一段用于心跳检测的信息,如果能接受对方传递过来的信息,证明此时TCP连接正常,反正,则会认为连接已经终端,就会进入断线重连阶段。
  • 断线重连:当系统判断此时TCP连接中断后,就会开始重新开始自动从头建立TCP通道。

具体实现代码
前端

// Socket.js
class Socket {
    wsUrl: string = "";
    constructor(url: string) {
        this.wsUrl = url;
    }
    ModeCode = {
        MSG: "message", // 普通消息
        HEART_BETA: "heart_beta", // 心跳检测
    };
    ws: WebSocket;
    webSocketState: boolean = false; // webSocket连接状态

    heartBeta = {
        time: 5 * 1000, //心跳检测间隔时间
        timeout: 3 * 1000, //心跳超时间隔
        reconnection: 10 * 1000, //断线重连时间
    };
    reconnectTimer;  // 断线重连时间器
    connectWebSocket() {
        this.ws = new WebSocket(this.wsUrl);
        this.init();
    }
    // 初始化函数
    init() {
        this.ws.addEventListener("open", () => {
            //ws状态设置为连接:true
            this.webSocketState = true;
            //是否启动心跳检测
            this.heartBeta?.time
                ? this.startHeartBeta(this.heartBeta.time)
                : "";
            console.log("开启连接");
        });

        this.ws.addEventListener('message', (e) => {
            const data = JSON.parse(e.data);
            switch (data.ModeCode) {
                case this.ModeCode.MSG:
                    console.log("普通消息");
                    break;
                case this.ModeCode.HEART_BETA:
                    this.webSocketState = true;
                    console.log("心跳消息", data.msg);
                    break;
            }
        })

        this.ws.addEventListener('error', (error) => {
            this.webSocketState = false
        })

        this.ws.addEventListener("close", (e) => {
            this.webSocketState = false;
        });
    }
    // 心跳检测初始化
    startHeartBeta(time: number) {
        setTimeout(() => {
            this.ws.send(
                JSON.stringify({ ModeCode: this.ModeCode, msg: new Date() })
            );
            this.waitingServer();
        }, time);
    }
    // 延时等待服务端响应,通过webSocketState判断是否连线成功
    waitingServer() {
        this.webSocketState = false;
        setTimeout(() => {
            if (this.webSocketState) {
                this.startHeartBeta(this.heartBeta.time);
            }
            console.log("心跳无响应,已经断线了");

            try {
                this.ws.close();
            } catch (e) {
                console.log("连接已关闭");
            }
            this.reconnectWebSocket();
        }, this.heartBeta.timeout);
    }
    // 重新连接
    reconnectWebSocket() {
        this.reconnectTimer = setTimeout(() => {
            this.reconnectWs();
        }, this.heartBeta.timeout);
    }
    reconnectWs() {
        if (!this.ws) {  // 第一次执行,初始化
            this.connectWebSocket()
        } 
        if (this.ws && this.reconnectTimer) {
            // 防止多个webSocket同时执行
            clearTimeout(this.reconnectTimer)
            this.reconnectTimer = null;
            this.reconnectWebSocket()
        }
    }
}

export default Socket;
  • 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
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

后端 node.js

var express = require('express');
var router = express.Router();

//引入ws模块
const WebSocket = require('ws').Server;
const port = 3001;

//创建服务
const server = new WebSocket({ port }, () => {
    console.log('webSocket服务开启');
})

const errorHandler = (error) => {
    console.log('errorHandler====>客户端出错了', error)
}

const closeHandler = (e) => {
    console.log('closeHandler====>客户端断开了', e)
}
function messageHandler(data) {
    console.log('messageHandler====>接收客户端消息', JSON.parse(data))
    // this.send(JSON.stringify(JSON.parse(data)))
    const { ModeCode } = JSON.parse(data)
    
    switch (ModeCode) {
        case 'message':
            console.log('收到前端的消息');
            break;
        case 'heart_beta':
            console.log('心跳检测');
            this.send(JSON.stringify(JSON.parse(data)));
            break
        default:
            break;
    }
}

const connectHandler = (ws) => {
    console.log('客户端已连接');
    //监听客户端出错
    ws.on('error', errorHandler);
    //监听客户端断开链接
    ws.on('close', closeHandler);
    //监听客户端发来的消息
    ws.on('message', messageHandler);
}
//建立连接
server.on('connection', connectHandler);

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
  • 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

SSE

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

SSE与WebSocket 作用相似,但是WebSocket更强大和灵活。因为WebSocket是全双工通道,可以双向通信;SSE 是单向通道,只能服务器向浏览器发送。
和WebSocket相比SSE优点:

  • SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
  • SSE 属于轻量级,使用简单;WebSocket协议相对复杂。
  • SSE 默认支持断线重连,WebSocket 需要自己实现。
  • SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
  • SSE 支持自定义发送的消息类型。

代码实现:

<template>
  <div>
    <h1>SSE数据:</h1>
    <ul>
      <li v-for="item in messages" :key="message.id">{{ item.data }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messages: []
    };
  },
  mounted() {
    const eventSource = new EventSource('https://example.com/sse'); // 替换为你的SSE接口地址

    eventSource.addEventListener('message', (event) => {
      this.messages.push({ id: Date.now(), data: event.data });
    });

    eventSource.addEventListener('error', (event) => {
      console.error('连接关闭:', event);
    });
  }
};
</script>
  • 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

小结

SSE与Http短轮训和WebSocket之间的区别就是其他两种通讯方式是双向的,而SSE则是服务端向客服端传输数据(单向的),例如ChatGPT。而短轮询相比WebSocket则适用于对实时性要求不高、数据更新频率较低的场景,例如一些简单的数据展示页面。WebSocket则适用于对实时性要求较高,需要频繁传输大量数据的场景,例如在线聊天、实时数据更新等。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/848070
推荐阅读
相关标签
  

闽ICP备14008679号