C# TCP网络编程4(分包和粘包一)

C# TCP网络编程4(分包和粘包一)
原文:https://blog.****.net/weixin_42033401/article/details/81558391
一:通过图解法来描述一下分包和粘包,这样客户更清晰直观的了解:
C# TCP网络编程4(分包和粘包一)
下面对上面的图进行解释:
1.正常情况:如果Socket Client 发送的数据包,在Socket Server端也是一个一个完整接收的,那个就不会出现粘包和分包情况,数据正常读取。
2.粘包情况:Socket Client发送的数据包,在客户端发送和服务器接收的情况下都有可能发送,因为客户端发送的数据都是发送的一个缓冲buffer,然后由缓冲buffer最后刷到数据链路层的,那么就有可能把数据包2的一部分数据结合数据包1的全部被一起发送出去了,这样在服务器端就有可能出现这样的情况,导致读取的数据包包含了数据包2的一部分数据,这就产生粘包,当然也有可能把数据包1和数据包2全部读取出来。
3.分包情况:意思就是把数据包2或者数据包1都有可能被分开一部分发送出去,接着发另外的部分,在服务器端有可能一次读取操作只读到一个完整数据包的一部分。
4.在数据包发送的情况下,有可能后面的数据包分开成2个或者多个,但是最前面的部分包,黏住在前面的一个完整或者部分包的后面,也就是粘包和分包同时产生了。

二:产生上情况的内部原因有下面几点:
1.数据发送端发送数据给缓冲buffer太大,导致发送一个完整的数据包被分几次发送给缓存buffer,然而缓冲buffer等到数据满了以后会自动把数据发送的数据链路层去,这样就导致分包了。
2.TCP协议定义有一个选项叫做最大报文段长度(MSS,Maximum Segment Size),该选项用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度。在一定程度上MSS应该能尽可能多地承载用户数据,用于在传输通路上又可能避免分片,但是在复杂的网络环境下确定这个长度值非常困难,那么在这样的情况下在传输过程中产生分包,粘包就很常见了
3.以太网,IP,TCP,UDP数据包分析大家可以仔细圆研究这篇文章http://www.cnblogs.com/feitian629/archive/2012/11/16/2774065.html
4.数据帧的有效载荷(payload)比以太网的最大传输单元(MTU)大的时候,进行了IP分片。

三:解决数据分包和粘包的基本策略如下
1.消息定长,比如定一个100,那么读取端每次读取数据就截取100个长度的数据,然后交给业务成去做解析
2.在消息的尾部加一些特殊字符,那么在读取数据的时候,只要读到这个特殊字符,就认为已经可以截取一个完整的数据包了,这种情况在一定的业务情况下实用。
3.读取缓存的数据是不定长的,所以我们把读取到的数据添加到我们自己的一个byte[]数组中,然后根据我们的业务逻辑来找到指定的特殊协议头部,协议长度,协议尾部,然后从我们的byte[]中获取一个完整的数据包,然后再对数据包进行业务解析就可以得到正确结果。

########################################################################
########################################################################

实验 一(粘包实验)

1、客户端启动的时候,向服务端发送250条数据

        static void Main(string[] args)
        {
            ///客户端代码
            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.102"), 3344);
            clientSocket.Connect(ipEndPoint);

            byte[] receiveBuffer = new byte[1024];
            int count = clientSocket.Receive(receiveBuffer);

            string msg = Encoding.UTF8.GetString(receiveBuffer, 0, count);

            Console.WriteLine("接收到服务端的消息:" + msg);

            byte[] sendBytes = Encoding.UTF8.GetBytes("我是客户端");
            clientSocket.Send(sendBytes);
            for (int i = 0; i < 250; i++) ///客户端启动向服务端发送250条数据
            {
                sendBytes = Encoding.UTF8.GetBytes(i.ToString());
                clientSocket.Send(sendBytes);
            }
            //while (true)
            //{
            //    string s = Console.ReadLine();
            //    sendBytes = Encoding.UTF8.GetBytes(s);
            //    clientSocket.Send(sendBytes);
            //}

            Console.ReadKey();
        }
    }
客户端启动的时候,向服务端发送250条数据,第一次结果服务端只是收到了3条客户端发送过来的数据…

C# TCP网络编程4(分包和粘包一)

客户端启动的时候,向服务端发送250条数据,第二次结果服务端只是收到了十几条条客户端发送过来的数据…

C# TCP网络编程4(分包和粘包一)

分包实现(发送了一条超过1024的数据,结果服务端接收到了多条数据)
服务端的数据接收缓存大小: static byte[] dataBuffer = new byte[1024]; // 接收数据缓存区

C# TCP网络编程4(分包和粘包一)