WebSocket—实现实时通讯


前言

电子邮件系统能够做到不刷新(不主动请求)就能收到新邮件提醒,主要依赖于WebSocket等实时通信技术。


一、websocket是什么

  • 全双工通信
    一种双向同时通信的通信方式,其中通信的双方可以同时发送和接收信息。在这种通信方式下,通信系统的每一端都设置了发送器和接收器,因此能控制数据同时在两个方向上传送。这类似于我们平时打电话,可以在说话的同时听到对方的声音。
    特点:高效率、低延迟、宽带利用率高,适用于实时、双向交互场景。
  • WebSocket
    WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,建立在 HTTP 协议基础之上。应用程序与服务器之间需要建立长连接,通过 WebSocket 协议进行实时通信。服务器端可以在有新邮件到达时主动向客户端推送通知,客户端接收到通知后即时展示给用户。
    特点:(1)全双工;(2)二进制帧;(3)协议名;(4)握手

二、WebSocket 连接过程

当用户成功登录,WebSocket 开始建立连接。
(1)客户端发起连接请求:客户端首先创建一个WebSocket对象,并向服务器发送一个 HTTP 请求以建立 WebSocket 连接。这个请求中包含了希望升级为 WebSocket 协议的信息,在请求头中设置Upgrade: Websocket、Connection: Upgrade。
(2)服务器响应并同意升级:服务器收到客户端的请求后,会验证请求并决定是否同意升级为 WebSocket 协议。如果同意,服务器会返回一个HTTP 101状态码,表示协议切换成功,并在响应头中添加 Upgrade 和 Connection 字段,告知客户端连接已经升级为 WebSocket 协议。
(3)TCP通道建立与通信:一旦连接升级为 WebSocket 协议,客户端和服务器之间会通过单一的TCP通道进行全双工通信。这意味着双方都可以在任何时候主动发送数据,而不需要像传统的HTTP协议那样建立多个连接。
(4)保持连接状态与实时通信:WebSocket 协议使用长连接,一旦建立连接后,会保持持久的连接状态,直到显式关闭。这种特性使得服务器可以随时主动向客户端下发数据,实现实时通信。
(5)控制开销与简单实现:WebSocket 协议在连接创建后,通过优化数据包头部大小来减少通信时的开销。同时,由于它建立在 TCP 协议之上,服务器端的实现相对容易,且没有同源限制,客户端可以与任意服务器通信。

三、WebSocket 使用步骤

1. 后端 websocket 服务器

以 Node.js 为例:

const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })
...

2. 前端 JS 代码

代码如下(示例):

// WebSocket服务器地址  
const webSocketServerUrl = 'wss://your-websocket-server-url';  
  
// WebSocket连接实例  
let socket;  
  
// 心跳检测相关变量  
let heartbeatIntervalId;  
let heartbeatTimeoutId; 
const heartbeatInterval = 5000; // 心跳间隔(毫秒)  
const heartbeatTimeout = 10000; // 心跳超时时间(毫秒)   
  
// 重连相关变量  
let reconnectIntervalId;  
const reconnectInterval = 2000; // 重连间隔(毫秒)  
const maxReconnectAttempts = 5; // 最大重连尝试次数  
let reconnectAttempts = 0;  
  
// 初始化WebSocket连接  
function initializeWebSocket() {  
    socket = new WebSocket(webSocketServerUrl);  
  
    // 监听连接打开事件  
    socket.onopen = function (event) {  
        console.log('WebSocket连接成功');  
        reconnectAttempts = 0; // 重置重连尝试次数  
        startHeartbeat(); // 开始心跳检测  
    };  
  
    // 监听接收消息事件  
    socket.onmessage = function (event) {  
        const data = JSON.parse(event.data);  
        console.log('收到消息:', data);  
        // 处理接收到的消息,比如更新UI等  
    };  
  
    // 监听连接关闭事件  
    socket.onclose = function (event) {  
        console.log('WebSocket连接关闭', event);  
        clearHeartbeat(); // 清除心跳检测  
        reconnect(); // 尝试重连  
    };  
  
    // 监听连接错误事件  
    socket.onerror = function (error) {  
        console.error('WebSocket错误:', error);  
        reconnect(); // 尝试重连  
    };  
}  
  
// 发送消息到WebSocket服务器  
function sendMessage(message) {  
    if (socket.readyState === WebSocket.OPEN) {  
        socket.send(JSON.stringify(message));  
    } else {  
        console.log('WebSocket连接未打开,消息暂存待发送');  
        // 这里可以添加逻辑来暂存消息,待连接打开后发送  
    }  
}  
  
// 开始心跳检测  
function startHeartbeat() {  
    heartbeatIntervalId = setInterval(function () {  
        if (socket.readyState === WebSocket.OPEN) {  
            socket.send(JSON.stringify({ type: 'heartbeat' }));  
        } else {  
            clearInterval(heartbeatIntervalId);  
            clearTimeout(heartbeatTimeoutId);  
            console.log('WebSocket连接已关闭,停止心跳检测');  
        }  
    }, heartbeatInterval);  
  
    heartbeatTimeoutId = setTimeout(function () {  
        if (socket.readyState === WebSocket.OPEN) {  
            console.log('心跳超时,关闭WebSocket连接');  
            socket.close();  
        }  
    }, heartbeatTimeout);  
}  
  
// 清除心跳检测定时器  
function clearHeartbeat() {  
    clearInterval(heartbeatIntervalId);  
    clearTimeout(heartbeatTimeoutId);  
}  
  
// 尝试重连WebSocket服务器  
function reconnect() {  
    if (reconnectAttempts >= maxReconnectAttempts) {  
        console.log('已达到最大重连尝试次数,不再尝试重连');  
        return;  
    }  
  
    console.log('尝试重连...');  
    reconnectAttempts++;  
    clearTimeout(reconnectIntervalId);  
    reconnectIntervalId = setTimeout(function () {  
        initializeWebSocket();   //初始化WebSocket连接
    }, reconnectInterval);  
}  

// 断开WebSocket连接  
function disconnectWebSocket() {  
    if (socket) {  
        socket.close();  
        socket = null;  
        clearHeartbeat();  
    }  
}  
  
// 初始化WebSocket连接  
initializeWebSocket();  
  
// 发送邮件示例  
const emailMessage = {  
    to: 'recipient@example.com',  
    subject: 'Hello',  
    body: 'This is a test email message.'  
};  
sendMessage(emailMessage);  
  
// 当不再需要WebSocket连接时,可以调用disconnectWebSocket函数来断开连接  
// disconnectWebSocket();

四、连接成功:心跳检测

心跳检测(heartbeat detection)是一种用于确保连接保持活跃和可用的机制。由于WebSocket连接本质上是长连接,长时间没有数据传输可能会导致连接被某些网络设备(如防火墙、路由器或代理服务器)断开,或者由于网络问题导致连接不稳定。为了解决这个问题,心跳检测被引入来定期发送小的数据包以检测连接的活性。
心跳检测通常包含两部分:
(1)心跳发送:客户端定期向服务器发送一个心跳消息,通常是一个简单的、格式化的数据包,不包含实际的应用数据。这个心跳消息的目的是告诉服务器客户端仍然在线并且连接是活跃的。
(2)心跳响应:服务器在收到心跳消息后,可以发送一个响应消息回客户端,确认它收到了心跳。这个响应消息是可选的,因为仅仅发送心跳消息本身就已经足够表明客户端的活性。但是,在某些情况下,服务器可能会利用这个机会发送一些状态信息或者更新给客户端。
在实现心跳检测时,需要考虑频率、超时、异常处理等等。

五、连接失败:重连机制

当WebSocket连接失败时,重连机制会自动启动,按照预设的策略进行多次尝试重新建立连接。这一机制能够确保在网络不稳定或服务器故障等情况下,客户端能够迅速恢复与服务器的通信,从而提高应用的可用性和用户体验。通过合理的重连间隔和次数设置,可以有效避免频繁重连带来的服务器压力,实现优雅的恢复连接。

六、Websocket 的限制

(1)不提供加密功能
明文传输,如果有安全上的需求,需要采用其他方式来确保安全性,例如使用 SSL 协议对 Websocket 连接进行加密,防止敏感信息被窃听或者篡改,另外也可以在服务端设置黑白名单,只允许特定IP地址或域名的客户端进行连接;
(2)不支持古老的浏览器
不支持 IE10 之前的版本,需要使用 AJAX 或者其他方式来进行替代;
(3)需要优化
保持长连接需要服务器不断地维护和处理连接状态,不做优化会过度消耗服务器的资源。

  • 26
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在 React TypeScript 中实现实时通信,可以使用 WebSocket 技术。下面是一个简单的示例,展示了如何使用 WebSocket 实现实时通信: ```typescript import React, { useState, useEffect } from 'react'; function ChatRoom() { const [messages, setMessages] = useState<string[]>([]); const [newMessage, setNewMessage] = useState<string>(''); useEffect(() => { const socket = new WebSocket('ws://localhost:8080'); socket.onopen = () => { console.log('WebSocket connected'); }; socket.onmessage = (event) => { const message = event.data; setMessages((prevMessages) => [...prevMessages, message]); }; socket.onclose = () => { console.log('WebSocket disconnected'); }; return () => { socket.close(); }; }, []); const handleNewMessage = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); setMessages((prevMessages) => [...prevMessages, newMessage]); const socket = new WebSocket('ws://localhost:8080'); socket.send(newMessage); setNewMessage(''); }; return ( <div> <ul> {messages.map((message, index) => ( <li key={index}>{message}</li> ))} </ul> <form onSubmit={handleNewMessage}> <input type="text" value={newMessage} onChange={(event) => setNewMessage(event.target.value)} /> <button type="submit">Send</button> </form> </div> ); } ``` 在上面的示例中,我们使用 `useState` 钩子来存储消息列表和输入框中的新消息。在 `useEffect` 钩子中,我们创建了一个 WebSocket 对象并通过 `onopen`、`onmessage` 和 `onclose` 方法来处理连接、接收消息和断开连接的事件。在 `handleNewMessage` 函数中,我们发送新消息到服务器并将其添加到消息列表中。最后,我们渲染了一个消息列表和一个输入框,使用户可以输入新消息并发送到服务器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值