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

希望对大家有所帮助。

相关推荐
椒颜皮皮虾྅12 小时前
OpenVINO C# API 3.3.1:支持OpenVINO 2026.3及GenAI增强
人工智能·c#·openvino
半亩码田15 小时前
C#转Python第3.1篇:Python 的 class 没有访问修饰符?面向对象的另一条路
开发语言·python·c#
唐青枫21 小时前
别再手写 args[0]:C#.NET CommandLineParser 命令行工具实战详解
c#·.net
LccKyI1 天前
C#学习day06(方法/函数及其关键字,附思维导图)
经验分享·学习·c#
.Peter1 天前
.NET/WPF 程序在部分 Windows 电脑无法保存用户环境变量:使用 DPAPI 实现安全兼容
windows·信息安全·c#·.net·wpf·环境变量·dpapi
农村小镇哥1 天前
怎么把HTM格式转化成WORD
ui·c#·word
农村小镇哥1 天前
C#读取CSV文件的方法
服务器·数据库·c#
FuckPatience1 天前
C# 报错参数无效
c#
北域码匠2 天前
PID 控制算法完整详解(C# 原生实现,无第三方库)
c#·pid控制·工控开发·闭环控制·自动控制算法·数字pid·增量式pid
离陌在学C#2 天前
C# 重载与重写:深入理解面向对象编程的核心概念
java·c#