Socket TCP协议解决粘包、半包问题的三种解决方案

简介: Socket TCP协议解决粘包、半包问题的三种解决方案

 

什么是粘包、半包问题:

      粘包:例如服务端依次将两条消息发送给客户端,我们暂且简单的将这两条消息举例为"Hello"、"Unity",而客户端一次性读取到的内容却是"HelloUnity",像这种一次性读取到两条消息中数据内容的情况称之为粘包。

       半包:例如服务端发送消息"Hello"给客户端,而客户端依次读取到"Hel","lo"两条消息,这种情况称之为半包。

粘包、半包发生的原因:

       粘包:消息发送方发送完完整的消息后,接收方没有及时处理(比如网络开小差,未能及时读取消息),数据滞留于缓冲区,此时发送方又继续发送了其他消息,那么接收方下次在缓冲区读取时,一次性读取到大于一条消息数据造成粘包。

       半包:发送方发送消息数据大小为512字节,而接收方缓冲区剩余已不足512字节,造成半包。

       究其根本原因,TCP为流式协议,消息不存在边界。

解决方案:

      1.固定长度法:服务端和客户端规定固定长度的缓冲区,当消息数据长度不足时,使用规定的填充字符进行填充。弊端:增加了不必要的数据传输,造成网络传输负担,不建议使用。

       2.结束标识法:在包体尾部增加标识符表示一条完整的消息数据已经结束。弊端:若消息体本身包含该标识符需要做转义处理,因此效率依然不高。

      3.长度信息法:将包体分为消息头+消息体,消息头中信息为消息体的长度,接收方通过该长度信息读取后面指定长度的内容,需要注意的是需限制可能的最大长度从而规定长度占用字节数。该方法为处理粘包半包问题的常用方法。核心代码:

发送端:

//将发送的内容转化为字节数据byte[] bytes=Encoding.Default.GetBytes(content);
//消息体长度Int16length= (Int16)bytes.Length;
//消息头长度byte[] lengthBytes=BitConverter.GetBytes(length);
//发送的包体 = 消息头 + 消息体byte[] sendBytes=lengthBytes.Concat(bytes).ToArray();
//发送socket.Send(sendBytes);

image.gif

接收端:

//接收数据缓冲区byte[] readBuffer=newbyte[1024];
//接收缓冲区的数据长度intbufferCount=0;

image.gif

bufferCount用于记录缓冲区中的有效数据长度,BeginReceive从缓冲区bufferCount的位置开始写入,缓冲区长度为1024,那么可写入的剩余量为1024 - bufferCount

socket.BeginReceive(readBuffer,             //接收缓冲区bufferCount,            //开始位置1024-bufferCount,     //最多读取的数据长度0,                      //标志位ReceiveCallback,        //接收数据回调函数socket);

image.gif

在收到新数据后,需要在回调函数中更新bufferCount,以便在下次接收数据时,写入到缓冲区中有效数据的后面。

Socketsocket= (Socket)ar.AsyncState;
//接收数据的长度intcount=socket.EndReceive(ar);
bufferCount+=count;

image.gif

因为使用了Int16表示消息长度,所以缓冲区中至少有2个字节以上的数据时才去读取并处理,如果小于2,不足以解析出长度信息,如果大于2但小于消息长度+2,表示不足以读取到完整消息。

if (bufferCount<=2) return;
Int16length=BitConverter.ToInt16(readBuffer, 0);
if (bufferCount<length+2) return;
//代码执行到此处表示已经有完整的消息,进行处理stringcontent=Encoding.UTF8.GetString(readBuffer, 2, length);

image.gif

完整消息读取后,将缓冲区的后续数据向前移位,更新缓冲区。

intstartIndex=2+length;
intcount=bufferCount-startIndex;
Array.Copy(readBuffer, startIndex, readBuffer, 0, count);
bufferCount-=startIndex;

image.gif

目录
相关文章
|
2月前
|
网络协议 安全 API
WebSocket、Socket、TCP 和 HTTP 的差别与应用场景
WebSocket、Socket、TCP 和 HTTP 是网络通信中的四大“使者”,各具特色:HTTP 适合短时请求,TCP 稳定可靠,Socket 灵活定制,WebSocket 实现实时双向通信。本文用通俗语言解析它们的区别与应用场景,助你为项目选择最合适的通信方式。
976 3
|
5月前
|
网络协议
TCP/IP与OPC协议的深度比较
总的来说,TCP/IP和OPC协议各有其优点和应用场景。TCP/IP协议是网络通信的基础,而OPC协议则是工业自动化领域的重要工具。在实际应用中,我们需要根据具体的需求和场景,选择合适的协议。
164 11
|
10月前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
184 13
|
10月前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
723 3
|
10月前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
11月前
|
网络协议 算法 数据格式
【TCP/IP】UDP协议数据格式和报文格式
【TCP/IP】UDP协议数据格式和报文格式
651 3
|
11月前
|
XML JSON 网络协议
【TCP/IP】自定义应用层协议,常见端口号
【TCP/IP】自定义应用层协议,常见端口号
232 3
|
11月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
400 1
|
网络协议 网络架构 数据格式
TCP/IP基础:工作原理、协议栈与网络层
TCP/IP(传输控制协议/互联网协议)是互联网通信的基础协议,支持数据传输和网络连接。本文详细阐述了其工作原理、协议栈构成及网络层功能。TCP/IP采用客户端/服务器模型,通过四个层次——应用层、传输层、网络层和数据链路层,确保数据可靠传输。网络层负责IP寻址、路由选择、分片重组及数据包传输,是TCP/IP的核心部分。理解TCP/IP有助于深入掌握互联网底层机制。
1376 2
|
网络协议 Java
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
这篇文章全面讲解了基于Socket的TCP网络编程,包括Socket基本概念、TCP编程步骤、客户端和服务端的通信过程,并通过具体代码示例展示了客户端与服务端之间的数据通信。同时,还提供了多个案例分析,如客户端发送信息给服务端、客户端发送文件给服务端以及服务端保存文件并返回确认信息给客户端的场景。
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例