基于超时重传协议的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>

希望对大家有所帮助。

相关推荐
快乐非自愿1 小时前
C# 中的 Span 和内存:.NET 中的高性能内存处理
java·c#·.net
Sunsets_Red3 小时前
P8277 [USACO22OPEN] Up Down Subsequence P 题解
c语言·c++·算法·c#·学习方法·洛谷·信息学竞赛
yuan199973 小时前
基于C#实现的专业级DXF文件显示控件
windows·microsoft·c#
wy3258643644 小时前
Unity 新输入系统InputSystem(基本操作)
unity·c#·游戏引擎
jghhh014 小时前
基于C# WinForm实现自动在线升级的方案
开发语言·c#
大尚来也4 小时前
从调度到实时:Linux 下 DolphinScheduler 驱动 Flink 消费 Kafka 的实战指南
c#·linq
cici158744 小时前
基于C#的智能仓储上位机系统实现方案
开发语言·c#
星和月4 小时前
Untiy使用说明
c#·游戏引擎
kylezhao20195 小时前
C#中 Invoke、begininvoke、InvokeRequired的详细讲解和三者之间的区别
开发语言·c#