功能:管理和自动重发网络数据包,处理乱序到达的数据包
用途:适用于需要数据可靠传输的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