Tcp流式传输解决粘包和少包的方案

23 篇文章 0 订阅
11 篇文章 0 订阅
该博客探讨了TCP流式传输中可能出现的消息粘包问题,以及如何通过定义消息头和消息体来解决这一问题。发送端在每个消息前添加4字节的消息头,存储消息体的长度,确保接收端能够正确拆分消息。接收端使用大缓冲区接收数据,并通过循环处理确保完整消息的拆包。该方法涉及网络字节序转换和内存拷贝,确保了跨平台的数据一致性。
摘要由CSDN通过智能技术生成

tcp是流式传输协议,接收端和发送端的 收发时间延时,此时会出现粘包现象

比如:再不考虑内核双向缓冲区的延时发送问题,客户端假设1s 发送100k,循环发,服务器每2s收一次数据,此时服务器收到了200k,出现了消息粘包,服务器也不知道客户端的消息分组情况,所以我们需要和客户端协商一个解决方案,类型tcp底层封装思想一下定义为 消息头和消息体:
下面伪代码展示一下:

发送端:

void sendMsg(int fd, char* sendData)
{
	/*这里的sendData数据体他是字符串形式的 不需要考虑字节对齐问题,
	sendData处理后的数据我们可以通过protobuf工具进行序列化得到一个字符串
	*/
	//在每条消息发送前面我们和服务器协商好定义一个int类型4字节的消息头里面存储消息实际长度
	int allLen = strlen(sendData) + 4;  //要发送的消息总长度  4就是消息头的大小,内容是要发送实际数据
	char* netData = new char[allLen]; //一条完整消息 包含消息体和消息头
	memset(netData, 0 sizeof(netData));
	uint32_t head = strlen(sendData);  //头保存消息体数据长度
	//int 是4字节的数据 由于不同机器要考虑到网络传输的字节对齐问题,而字符串不用考虑,所以需要将主机字节序转换为网络字节序
	head = htonl(head);  //head 对应4字节所以用 htohl()
	
	memstrcpy(netData , &head, 4);  //将消息头数据先拷贝要发送的数据包里面
	//然后将消息体实际数据拷贝发送包里面
	memstrcpy(netData +4, sendData, strlen(sendData));
	
	send(fd, netData, allLen, 0); //将数据发送给接收端
}

接收端:

#define RECVSIZE  40960000
char tmpbuffer[RECVSIZE] = {0}//超大缓冲区  
void recvMsg(fd, char* recvData)
{
	 //这里准确的做法是应该用个缓冲区一次性接收所有数据 防止的不完整

	 int len = recv(fd, tmpbuffer+strlen(tmpbuffer), 40960000, 0) //这里tmpbuffer+strlen(tmpbuffer) 因为有没有满足一条完整消息 而留下的数据此时要接着缓冲区后面的位置继续写
	 if( len > 4)  //>4先取头信息 看消息体长度够不够
	 {
	  	uint32_t head = 0;
		memstrcpy(&head, tmpbuffer, 4)
		head = ntohl(head);  //将读到的数据网络字节序转换为本机字节序 head 里面的大小就是消息体内容的长度
		int readLen = 0  //已经读取的数据
		while(true)   //通过循环将消息拆分一条条处理完
		{
			if(len >= head + 4)  //满足一条完整的数据, 拆包过程
			{
				char* lineData = new char[head]
				readLen + = 4 //如果满足了head + 4 <= len 才可以设置readLen加,要不然丢失头数据信息
				memstrcpy(lineData, tmpbuffer+readLen , head) //取数据从缓冲区的头依次取数据
				readLen += head   //4+head 读走了一条完整数据的长度
				len -= readLen    //剩下的数据长度
				//将lineData通过protocol反序列化
				//处理对应得了逻辑
				delete [] lineData;
				lienData = NULL; 
			}
			else //不满足一条完整的数据
			{
				memstrcpy(tmpbuffer, tmpbuffer+readLen, len-readLen); //这里将剩下数据拷回临时缓冲区起始地址
				tmpbuffer[readLen+1] = '\0';   //strlen 遇到\0停止
				break;
			}
			memstrcpy(&head, tmpbuffer+readLen , 4)
			head = ntohl(head);  //将读到的数据网络字节序转换为本机字节序 head 里面的大小就是消息体内
			
		}
	 }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时间溜走了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值