0、引言
大家都知道当使用udp通信时,最大的一个问题是会出现丢包的情况,那么如何可以既使用udp来传输,又同时能有效防止丢包呢?
本文提供一种简单有效的方法,可以显著避免udp丢包的问题。此外,如果要达到类似tcp通信的效果,则需要相应添加其他规则。基于udp来实现tcp的方法有google提供的开源框架——libjingle,大家感兴趣的话,可以下载源码来学习参考。
1、建立发送消息结构体和接收消息结构体
struct SendMsg { time_t expire_time; unsigned int try_count; int data_len; char* data; SendMsg():expire_time(0), try_count(0), data_len(0), data(NULL){} } struct RecvMsg { time_t expire_time; int data_len; char* data; RecvMsg():expire_time(0), data_len(0), data(NULL){} }
2、唯一标识消息的来源或消息发送的去处,暂时使用对方的ip和port
struct AddrIndex { unsigned short port; unsigned int ip; unsigned in tran_id; //数据包标识,由对方生成数据包时进行累加 bool isSend; //是发送的数据还是接收的数据 //实现<运算符操作,因若要在map中使用AddrIndex作为索引项,需要将之实现 bool operator<(const AddrIndex& addrIndex) const { if (this->ip < addrIndex.ip) { return true; } else if (this->ip < addrIndex.ip) { return false; } if (this->port < addrIndex.port) { return true; } else if (this->port < addrIndex.port) { return false; } if (this->tran_id < addrIndex.tran_id) { return true; } else { return false; } } }
3、创建udp数据包核查的数据结构
class UdpCheck { private: map<AddrIndex, SendMsg> send_check_msg; map<AddrIndex, RecvMsg> recv_check_msg; int sock_fd; unsigned int timeout_unit; unsigned int max_try_count; public: //公有接口主要如下所示: //1.当接收到数据包后,查看接收历史是否有一致的数据包,若有则更新历史的数据包 //2.当发送数据包后,查看发送历史是否有一致的数据包,若有则更新历史的数据包,否则插入历史中 //3.当接收数据包后,查看接收历史是否有一致的数据包,若有则更新历史的数据包,否则插入历史中 //4.当接收到发送数据的应答后,就从发送历史中删除相应数据包 //5.当接收请求,但处理失败时,就从接收历史中删除相应数据包 //6. 遍历发送历史中的数据包,判断是否重发,是否删除 //当重试次数没有超过最大值,且记录维持时间小于time_unit*max_try_count时,则不删除,且重发udp数据包 //7. 遍历接收历史,判断是否过期删除 //8. 外部还需要提供一个定时器来定时查看接收和发送历史 }