tcp 粘包 丢包 解决方案

本文深入探讨了TCP粘包和丢包的原因,详细分析了发送与接收缓冲区的作用及潜在问题,并提出了一种有效解决方案:通过双缓冲区机制和包头长度判断来避免粘包和丢包,确保数据的准确传输。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、分析tcp粘包和丢包的原因

   发送数据的时候有  发送缓冲区senBuff,

   接收数据的时候有  接收缓冲区recvBuff,

   假如接收数据方一直不recv, 则recvBuff就会堆满, 这个时候tcp就会停止发送数据了。

   同样因为接收缓冲区的数据满了, 所以发送缓冲区也不会再就收发送方发来的消息。

2、解决方案

// 接收数据  处理年包 拆分包
int CEasyTcpClient::RecvData()
{
	//char szRecv[1024] = {};
	printf("RecvData, 11111");
	// 5 接收客户端请求
	int nLen = (int)recv(m_sock, m_szRecv, RECV_BUFF_SIZE, 0);
	if (nLen <= 0)
	{
		printf("<socket = %d>与服务器断开连接, 任务结束。 \n", (int)m_sock);
		return -1;
	}

	printf("client recv data , nLend = %d\n", nLen);

	//将收取到的数据拷贝到消息缓冲区
	memcpy(m_szMsgBuf + m_nLastPos, m_szRecv, nLen);
	//消息缓冲区的数据尾部位置后移
	m_nLastPos += nLen;
	//判断消息缓冲区的数据长度大于消息头DataHeader长度
	while (m_nLastPos >= sizeof(DataHeader))
	{
		//这时就可以知道当前消息的长度
		DataHeader* header = (DataHeader*)m_szMsgBuf;
		//判断消息缓冲区的数据长度大于消息长度
		if (m_nLastPos >= header->dataLength)
		{
			//消息缓冲区剩余未处理数据的长度
			int nSize = m_nLastPos - header->dataLength;
			//处理网络消息
			OnNetMsg(header);
			//将消息缓冲区剩余未处理数据前移
			memcpy(m_szMsgBuf, m_szMsgBuf + header->dataLength, nSize);
			//消息缓冲区的数据尾部位置前移
			m_nLastPos = nSize;
		}
		else {
			//消息缓冲区剩余数据不够一条完整消息
			break;
		}
	}
	return 0;
}

类似这样的大概思路就是  接收到数据存到一级缓冲区, 然后从一级缓冲区拷贝到二级缓冲区, 之后判断二级缓冲区长度是否大于包头长度, 如果大于包头长度就进行解析,  因为包头里面包含了完整消息协议的总长度。  这样就不会出现粘包和丢包的问题了。

### 上位机 TCP 通信中的防丢包解决方案 #### 防止问题 由于TCP协议本身是一个面向字节流的传输层协议,所谓的“”的概念并不存在于TCP内部实现中[^1]。因此,在应用层面设计时需考虑如何区分消息边界。 一种常见方法是在每条消息前加上固定长度的消息头,该头部含整个消息体的实际大小信息。接收端读取到足够的字节数作为消息头后解析出后续应接收到的数据量,从而可以准确无误地区分不同消息之间的界限。 ```c++ // 发送方代码示例 void send_message(int sockfd, const char* message){ int length = strlen(message); // 将消息长度转换成网络字节序 uint32_t net_length = htonl(length); // 先发送消息长度再发实际内容 write(sockfd, &net_length, sizeof(net_length)); write(sockfd, message, length); } ``` 对于接收者而言,则需要先读取消息长度部分,然后根据此长度继续读入相应数量的内容直到完成一条完整消息的接收为止: ```c++ // 接收方代码示例 char buffer[BUFFER_SIZE]; int readn(int fd,void *vptr,size_t n){ size_t nleft=n; ssize_t nread=0; char *ptr=(char*)vptr; while(nleft>0){ if((nread=read(fd,ptr,nleft))<0){ if(errno==EINTR) continue; /*中断则重新尝试*/ return(-1);/*其他错误返回-1 */ }else if(nread==0){/*EOF*/ break;/*结束循环*/ } nleft-=nread; ptr+=nread; } return (n-nleft); } uint32_t msg_len_net_order; if(readn(connfd,&msg_len_net_order,sizeof(msg_len_net_order))==sizeof(msg_len_net_order)){ uint32_t msg_len_host_order = ntohl(msg_len_net_order); if(readn(connfd,buffer,msg_len_host_order)==msg_len_host_order){ printf("Received message:%s\n",buffer); } } ``` #### 处理丢包情况 理论上讲,在正常工作状态下TCP不会丢失任何已建立连接上传输的数据报文片段;即使在网络状况不佳的情况下也会自动请求重传未成功送达的部分直至全部正确抵达目的地位置[^2]。然而为了进一步提高系统的健壮性和用户体验质量,仍然建议采取一些预防措施来应对可能出现异常情形下的数据完整性保障需求。 例如可以在应用程序逻辑里加入心跳机制定期向对方发送探测信号确认链路状态良好与否;当检测到长时间没有新的有效载荷到来时主动发起询问操作以尽早发现问题所在并及时处理恢复正常的交互流程。 另外还可以利用更高层次的应用级协议特性比如HTTP/HTTPS自带的状态码反馈机制或者自定义RPC框架内的错误捕捉功能来进行更细粒度上的监控管理确保每一次请求响应都能得到妥善处置而不至于因底层不可控因素影响整体服务稳定性表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值