C++ UDP消息重传+乱序处理

功能:管理和自动重发网络数据包,处理乱序到达的数据包

用途:适用于需要数据可靠传输的UDP通信场景

使用场景:

UDP是无连接的,面向消息的数据传输协议,与TCP相比,有两个致命的缺点,一是数据包容易丢失,二是数据包无序。
要实现可靠传输,就必须在上层对数据丢包和乱序作特殊处理,必须要有要有丢包重发机制和超时机制。 该类可以被用于网络通信程序中,确保数据包在未被确认接收的情况下能够被自动重发,增加数据传输的可靠性。

注意:
代码中timetool工具为我自己封装的定时器工具,内在还是使用定时器计算时间差,使用时替换成自己的

超时重发代码:

/*
 * @Author: ThankYou
 * @Date: 2024-08-22 16:11:24
 * @LastEditors: ThankYou
 * @LastEditTime: 2024-08-27 16:47:42
 * @FilePath: \debug_master\src\CAutoResend.h
 * @Description: 自动重发类
 *
 * Copyright (c) 2024 by ThankYou, All Rights Reserved.
 */
#ifndef CAUTORESEND_H
#define CAUTORESEND_H
#include <map>
#include "timetool.h"
#include <stdint.h>
#include <stdio.h>
// 自动重发类
class CAutoResend
{
private:
    struct PacketInfo
    {
        std::vector<uint8_t> vecData; // 数据
        TimeOutStruct_t stTimer;      // 定时器
        uint32_t m_u32ResendCount;    // 重发次数计数器
    };

    std::map<uint32_t, PacketInfo> m_mapPendingPackets; // 待发送数据包
    const uint32_t m_u32MaxPendingPackets = 100;        // 最大待发送数据包数
    uint32_t m_u32MaxResendCount;                       // 最大重发次数
    uint32_t m_u32ResendInterval;                       // 重发间隔
    /**
     * @description: 发送数据函数
     * @param {const std::vector<uint8_t> &} sendData:发送数据
     * @return {void}
     */
    std::function<void(const std::vector<uint8_t> &sendData)> m_pFunSend;

public:
    /**
     * @description: 构造函数
     * @param {std::function<void(const std::vector<uint8_t> &sendData)>} m_pCFunSend:发送数据函数
     * @param {uint32_t} Cu32ResendInterval:重发间隔
     * @param {uint32_t} Cu32MaxResendCount:最大重发次数
     * @return {*}
     */
    CAutoResend(std::function<void(const std::vector<uint8_t> &CSendData)> m_pCFunSend,
                uint32_t Cu32ResendInterval = 100,
                uint32_t Cu32MaxResendCount = 50)
        : m_pFunSend(m_pCFunSend), m_u32ResendInterval(Cu32ResendInterval), m_u32MaxResendCount(Cu32MaxResendCount)
    {
    }

    /**
     * @description: 添加数据包
     * @param {uint32_t} u32CTransid:传输ID
     * @param {const std::vector<uint8_t> &} CPacket:数据包
     * @return {*}
     */
    void addPacket(const uint32_t u32CTransid, const std::vector<uint8_t> &CPacket)
    {
        if (m_mapPendingPackets.size() > m_u32MaxPendingPackets)
        {
            printf("m_mapPendingPackets size > %d, drop this packet\r\n", m_u32MaxPendingPackets);
            m_mapPendingPackets.erase(m_mapPendingPackets.begin());
        }
        PacketInfo stInfo;
        stInfo.vecData = CPacket;
        stInfo.m_u32ResendCount = 0;
        tt_InitTimeOutStruct(&stInfo.stTimer);
        m_mapPendingPackets[u32CTransid] = stInfo;
    }

    /**
     * @description: 确认收到数据包,从待发送数据包中移除
     * @param {uint32_t} u32CTransid:传输ID
     * @return {*}
     */
    void acknowledgePacket(uint32_t u32CTransid)
    {
        m_mapPendingPackets.erase(u32CTransid);
    }

    /**
     * @description: 打印待发送数据包数量
     * @return {*}
     */
    void print_map_num()
    {
        printf("m_mapPendingPackets size: %d\r\n", m_mapPendingPackets.size());
    }

    /**
     * @description: 执行重发
     * @return {*}
     */
    void resend()
    {
        for (auto it = m_mapPendingPackets.begin(); it != m_mapPendingPackets.end();)
        {
            if (0 == tt_TestIsTimeOutAndIncrease(&it->second.stTimer, m_u32ResendInterval))//定时器时间到了循环执行
            {
                if ((it->second.m_u32ResendCount < m_u32MaxResendCount) && (NULL != m_pFunSend))
                {
                    m_pFunSend(it->second.vecData);
                    it->second.m_u32ResendCount++;
                    ++it;
                }
                else if (it->second.m_u32ResendCount >= m_u32MaxResendCount)
                {
                    std::cout << "数据包 达到最大重发次数 " << m_u32MaxResendCount << ",停止重发" << std::endl;
                    it = m_mapPendingPackets.erase(it);
                }
                else
                {
                    printf("pSendToSlv 为空!\r\n");
                    ++it;
                }
            }
            else
            {
                ++it;
            }
        }
    }

    // 重置重发器
    void reset()
    {
        m_mapPendingPackets.clear();
    }
};
#endif // CAUTORESEND_H

乱序处理代码

/*
 * @Author: ThankYou
 * @Date: 2024-08-22 16:16:29
 * @LastEditors: ThankYou
 * @LastEditTime: 2024-08-22 16:20:15
 * @FilePath: \debugslaver\src\CReOrder.h
 * @Description: 乱序处理类
 *
 * Copyright (c) 2024 by ThankYou, All Rights Reserved.
 */
#ifndef CREORDER_H
#define CREORDER_H
#include <map>
#include "timetool.h"
#include <stdint.h>
#include <stdio.h>
// 乱序处理类
class CReorder
{
private:
    std::map<uint64_t, std::vector<uint8_t>> m_mapPacketBuffer; // 用于存储乱序数据包的缓冲区
    uint64_t m_u64ExpectedSeq;                                  // 期望的下一个序列号
    const size_t m_szMaxBufferSize = 1024 * 1024;                     // 缓冲区最大大小
    const size_t m_szMaxMsgLen = 2048;//一帧数据最大长度

public:
    CReorder() : m_u64ExpectedSeq(0) {}

    /**
     * @description: 添加数据包到缓冲区
     * @param {uint64_t} u64COffset:偏移量
     * @param {const std::vector<uint8_t> &} CPacket:数据包
     * @return {*}
     */
    void addPacket(uint64_t u64COffset, const std::vector<uint8_t> &CPacket)
    {
        if (CPacket.size() <= m_szMaxMsgLen)
        {
            if (u64COffset >= m_u64ExpectedSeq)
            {
                m_mapPacketBuffer[u64COffset] = CPacket;

                // 如果缓冲区过大,移除最旧的数据包
                while (m_mapPacketBuffer.size() > m_szMaxBufferSize)
                {
                    m_mapPacketBuffer.erase(m_mapPacketBuffer.begin());
                }
            }
            else
            {
                printf("数据包 %llu 已收到,丢弃\r\n", u64COffset);
            }
        }
    }

    /**
     * @description: 检查是否有可用的有序数据包
     * @return {bool}:是否有可用的有序数据包
     */
    bool hasOrderedPackets() const
    {
        return m_mapPacketBuffer.find(m_u64ExpectedSeq) != m_mapPacketBuffer.end();
    }

    /**
     * @description: 获取有序的数据包
     * @param {std::vector<std::vector<uint8_t>> &} CResult:有序数据包
     * @return {int32_t}:返回值0成功,-1失败
     */
    int32_t getOrderedPackets(std::vector<std::vector<uint8_t>> &CResult)
    {
        int32_t i32Ret = -1;
        if (hasOrderedPackets())
        {
            std::vector<std::vector<uint8_t>> vecOrderedPackets;

            auto it = m_mapPacketBuffer.find(m_u64ExpectedSeq);
            while (it != m_mapPacketBuffer.end())
            {
                vecOrderedPackets.push_back(it->second);
                m_mapPacketBuffer.erase(it);
                m_u64ExpectedSeq++;
                it = m_mapPacketBuffer.find(m_u64ExpectedSeq);
            }

            i32Ret = 0;
            CResult = vecOrderedPackets;
        }
        return i32Ret;
    }

    /**
     * @description: 重置重排序器
     * @return {*}
     */
    void reset()
    {
        m_mapPacketBuffer.clear();
        m_u64ExpectedSeq = 0;
    }
};
#endif // CREORDER_H
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值