基于超时重传协议的websocket优化方案

概述

在实时通信系统中,确保消息的可靠传输是非常重要的。超时重传协议是一种常见的机制,用于在网络不稳定或消息丢失的情况下保证消息的最终送达。本文将详细介绍超时重传协议的工作原理,并提供一种基于SignalR的实现方案。此外,还将讨论如何优化该协议以降低服务器负载。

工作原理

超时重传协议的基本思想是在发送消息后设置一个定时器。如果在指定的时间内(例如10秒)没有收到确认(ACK),则认为消息未成功送达,并重新发送该消息。这种机制可以有效地应对网络延迟和丢包问题。

Hub

服务端使用SignalR库来处理客户端的连接和消息传递。以下是关键部分的代码实现:

C# 复制代码
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

public class WebsocketLib : Hub
{
    // 存储待处理消息的计时器
    private readonly Dictionary<string, Timer> _pendingMessages = new Dictionary<string, Timer>();

    // 设置最大重试次数
    private readonly int maxRetries = 3;

    // 发送单条消息的方法
    public async Task EchoSingleMessage(string sender, string receiver, string message)
    {
        // 生成唯一的消息ID
        var messageId = Guid.NewGuid().ToString();
        int retryCount = 0;

        // 启动计时器以检查ACK
        var timer = new Timer(async state =>
        {
            // 如果计时器不在待处理消息字典中,则直接返回
            if (!_pendingMessages.ContainsKey(messageId))
            {
                return;
            }

            // 如果在10秒内没有收到ACK,重新发送消息
            if (retryCount < maxRetries)
            {
                //发送信息
                await WebsocketLib.SendMessage(sender, receiver, message,messageId);
                
                retryCount++;
            }
            else
            {   // 重传失败需要记录到数据库
                WebsocketLib.logFailMessgae(sender, receiver, messagemessageId);
                // 取消计时器
                _pendingMessages[messageId].Change(-1, 0);
                // 从待处理消息字典中移除计时器
                _pendingMessages.Remove(messageId);
            }
        }, null, TimeSpan.FromSeconds(10), TimeSpan.Zero);

        // 将计时器添加到待处理消息字典中
        _pendingMessages[messageId] = timer;
    }

    // 接收ACK消息的方法
    public async Task AcknowledgeMessage(string ack)
    {
        // 从ACK消息中提取消息ID
        var parts = ackMessage.Split(':');
        var messageId = parts[0];

        // 如果消息ID存在于待处理消息字典中
        if (_pendingMessages.ContainsKey(messageId))
        {
            // 取消计时器
            _pendingMessages[messageId].Change(-1, 0);
            // 从待处理消息字典中移除计时器
            _pendingMessages.Remove(messageId);
            Console.WriteLine($"Received ACK for message: {ackMessage}");
        }
    }
}

Clients

客户端使用SignalR库连接到服务端,并处理接收到的消息。以下是关键部分的代码实现:

js 复制代码
    <script>
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("/chat")
            .build();


        connection.on("receiveMessage", (message) => {
            console.log(`Received Message: ${message}`);
            
            if(message.messageId){
            //回传messageId
            setTimeout(() => {
                connection.invoke("AcknowledgeMessage", `${messageId}:ACK`).catch(err => console.error(err));
            }, 5000); 
            }
        });

        async function start() {
            try {
                await connection.start();
                console.log("SignalR Connected.");
            } catch (err) {
                console.error(err.toString());
            }
        }

        connection.onclose(async () => {
            await start();
        });

        start();
    </script>

希望对大家有所帮助。

相关推荐
samble12 小时前
从零搭建灌装监控系统(十九):单元测试实战,xUnit + 内存SQLite + 反射测试私有方法
c#·wpf·mvvm·modbus·工业控制
人道领域15 小时前
【面试八股速通篇01】锁与并发工具
java·开发语言·面试·c#
宇卿.15 小时前
C#字段、属性、索引器、常量
开发语言·计算机视觉·c#
程序员Linc16 小时前
ONNXOCRSharp 开源手记 02|v0.4.0 PDF识别
开发语言·pdf·c#·ocr·onnxocrsharp
慧都小妮子16 小时前
MSG 附件太占内存?Aspose.Email for .NET 26.9 延迟加载
c#·.net·aspose·内存优化·延迟加载·aspose.email·附件内存
慧都小妮子17 小时前
FastReport PDF/UA 导出:让出海 PDF 满足无障碍合规要求
pdf·c#·fastreport·报表导出·pdf/ua·pdf 无障碍
淡海水20 小时前
03-02-架构篇-Boehm GC架构解析
unity·架构·c#·gc·boehm
samble2 天前
从零搭建灌装监控系统(十八):自定义控件,三色灯与状态指示
c#·wpf·mvvm·modbus·工业控制
jun_bai2 天前
使用Aspose实现word文档合并,内容替换
windows·c#·word
字节暗面2 天前
客户端上报不可信:游戏服务端数据校验与对局重演设计
c#·游戏开发·服务端校验·反作弊