数据包协议结构,粘包问题,半包问题,缝包 C++ 实现

/*
* @note		:数据包协议结构
*
* @mark		:数据包收发流程:
*				发送端:拆包/分包:将整体数据拆分成一个或多个数据包(数据套上包头) -> 依次通知发送回调(单包发送)
*				接收端:并发接收 -> 粘包分离,缝包(前后半包拼接) -> 拼包/组包:去掉各数据包包头并拼接整体数据 -> 通知接收回调(整体接收)
*
* @author	:Andy.Ro
* @email	:Qwuloo@qq.com
*/
#ifndef _XPACKET_H_INCLUDE_
#define _XPACKET_H_INCLUDE_

#include "../xBuffer/xBuffer_p.h"

#pragma pack(push,1)

//------------------------------------------------------------------------
// packet_t 数据包协议结构,发送端和接收端协议必须完全一致
//------------------------------------------------------------------------
typedef struct _packet_t
{
#define FMT_RAW		0
#define FMT_JSON	1
#define FMT_XML		2
#define FMT_TEXT	3
	//
	// 包头(header)

	// 0  ~ 1 位:包体(数据部分)格式(0:raw|1:json|2:xml|3:text),相对整体而言,各数据包包体格式肯定是一致的
	// 2      位:数据包开始符(0:非开始 1:开始),相对整体而言,当前包是否是第一个包,即首包
	// 3      位:数据包结束符(0:非结束 1:结束),相对整体而言,当前包是否是最后个包,即尾包
	// 4  ~ 31位:所在整体所含数据包总数(total)
	// 32 ~ 47位:当前包体(数据部分)大小(len ),即追加缓存部分存放的实际数据长度,不超过 MAX_PACKETSZ
	// 48 ~ 63位:尾包包体(数据部分)大小(tail),不超过 MAX_PACKETSZ
	u64_t		flag;					
										
										
	
	u64_t		gid;					// 发送端全局唯一标识各数据包发送序列且连续(编号1开始),接收端处理并发接收问题
	
	u32_t		pid;					// 唯一标识数据包所在整体(编号1开始),即发送数据时将一个整体分拆成多个数据包的 pid 相同
	
	u32_t		id;						// 相对整体而言,其相关各数据包间的序列id唯一且连续(编号1开始),拼包后检验其连续性(针对 UDP 不可靠传输来说,完全漏掉某个包有可能的)
	
	u64_t		offset;					// 当前包体(数据部分)相对整体的偏移
	
	// 校验和
	u16_t		checksum;
	
	//
	// 包体(body-content),即数据部分		// 追加缓存部分,首址为 (char *)this + sizeof(packet_header_t)

	// 对于小数据量(总数据大小不超过MAX_PACKETSZ,total=1)而言,发送端无需分包发送,接收端无需拼包,一个数据包即可表示整体:len
	// 对于大数据量(总数据大小已超过MAX_PACKETSZ,total>1)而言,发送端需要分包发送,接收端需要拼包,由各个数据包拼接成整体:(total - 1) * len(MAX_PACKETSZ) + tail

}packet_t;

#pragma pack(pop)

// 直观看其实就是数据包头结构
typedef packet_t packet_header_t;

#define MAX_BUF_SIZE	4096// 一次数据收发最大缓存大小(自定义任意大小,但别不超过 65535 + sizeof(packet_header_t))
#define MAX_PACKETSZ   ((MAX_BUF_SIZE - sizeof(packet_header_t)) >= 65535 ? 65535:(MAX_BUF_SIZE - sizeof(packet_header_t)) ) // 单数据包包体大小不超过65535

// 获取数据包格式/开始符/结束符/总包数/包体大小/尾包包体大小
#define __packet_get_type(flag)		(u8_t )(((flag) & 0x0000000000000003)      )
#define __packet_get_start(flag)	(u8_t )(((flag) & 0x0000000000000004) >> 2 )
#define	__packet_get_end(flag)		(u8_t )(((flag) & 0x0000000000000008) >> 3 )
#define __packet_get_total(flag)	(u32_t)(((flag) & 0x00000000FFFFFFF0) >> 4 )
#define __packet_get_len(flag)		(u16_t)(((flag) & 0x0000FFFF00000000) >> 32)
#define __packet_get_tail(flag)     (u16_t)(((flag) & 0xFFFF000000000000) >> 48)

// 指定数据包格式/开始符/结束符/总包数/包体大小/尾包包体大小
#define __packet_set_type( flag, t)	((flag) = ( ((flag) & 0xFFFFFFFFFFFFFFFC) | (( (u8_t )(t))        & 0x0000000000000003) ))
#define __packet_set_start(flag, b)	((flag) = ( ((flag) & 0xFFFFFFFFFFFFFFFB) | ((((u8_t )(b)) <<  2) & 0x0000000000000004) ))
#define	__packet_set_end(  flag, b)	((flag) = ( ((flag) & 0xFFFFFFFFFFFFFFF7) | ((((u8_t )(b)) <<  3) & 0x0000000000000008) ))
#define __packet_set_total(flag, n)	((flag) = ( ((flag) & 0xFFFFFFFF0000000F) | ((((u32_t)(n)) <<  4) & 0x00000000FFFFFFF0) ))
#define __packet_set_len(  flag, l)	((flag) = ( ((flag) & 0xFFFF0000FFFFFFFF) | ((((u64_t)(l)) << 32) & 0x0000FFFF00000000) ))
#define __packet_set_tail( flag, l)	((flag) = ( ((flag) & 0x0000FFFFFFFFFFFF) | ((((u64_t)(l)) << 48) & 0xFFFF000000000000) ))

#define __packet_data_addr(p) (char *)((char *)(p) + sizeof(packet_header_t))

typedef xBuffer_p packet_pool_t;
typedef std::map<u64_t,std::pair<char*,u32_t>> map_packet_t;
typedef map_packet_t::iterator				  itor_packet_t;

/*
******************************************* 应用层开放调用接口 *******************************************
*/
// @note:通知接收回调函数
typedef int (*recv_callback_func)(xNetObject* pthis, char const* buf, u64_t bytes);

// @note:通知发送回调函数
typedef int (*send_callback_func)(xNetObject* pthis, char const* buf, ulong_t bytes);

// @note:拆包:将一个数据整体拆分成一个或多个数据包(数据块套上包头)后通知用户回调发送
// @mark:发送端:拆包/分包:将整体数据拆分成一个或多个数据包(数据套上包头) -> 依次通知发送回调(单包发送)
extern int split_packet(char const* buf, u64_t bytes,
						send_callback_func callback_send,
						xNetObject* pthis);

// @note:粘包分离,缝包(前后半包拼接)
// @mark:接收端:并发接收 -> 粘包分离,缝包(前后半包拼接) -> 拼包/组包:去掉各数据包包头并拼接整体数据 -> 通知接收回调(整体接收)
extern int separate_continuous_packet(char const* buf, ulong_t bytes,
									  recv_callback_func callback_recv,
									  xNetObject* pthis);



#endif // _XPACKjoint_packetET_H_INCLUDE_


/*
* @note		:数据包协议结构
*
* @mark		:数据包收发流程:
*				发送端:拆包/分包:将整体数据拆分成一个或多个数据包(数据套上包头) -> 依次通知发送回调(单包发送)
*				接收端:并发接收 -> 粘包分离,缝包(前后半包拼接) -> 拼包/组包:去掉各数据包包头并拼接整体数据 -> 通知接收回调(整体接收)
*
* @author	:Andy.Ro
* @email	:Qwuloo@qq.com
*/
#include "../xNet/xNet.h"

#include "../xBuffer/xBuffer.h"
#include "../xBuffer/xBufferEx.h"
#include "../xBuffer/xBuffer_p.h"

#include "xPacket.h"

#include "../xNet/xService.h"

// @note:计算校验和
static inline u16_t s_sum(u16_t * addr, int size)
{
	ulong_t sum = 0;
	while ( size > 1 )
	{
		sum  += *addr++;
		size -= sizeof(u16_t);
	}
	if ( 1 == size )
	{
		sum += *(u8_t *)addr;
	}
	sum  = (sum >> 16) + (sum & 0XFFFF);
	sum += (sum >> 16);
	return (u16_t)(~sum);
}

// @note:验证校验和
static inline BOOL s_check_sum(packet_header_t * h
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值