解决粘包问题

Posted 吃豆人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决粘包问题相关的知识,希望对你有一定的参考价值。

粘包分包问题

# 如果使用异步方式 beginreceive() 如果开辟的数组足够大,是不会产生分包发送的问题,但是- - 足够大是多大….

上一篇写了Socket通讯,这一篇整理一下粘包分包的问题, 主要思路是在字节数据前四个位置保存一个数据的长度后边保存数据,通过一个类进行存储传输过来的数据,再使用这个类进行解析.

服务器

namespace 服务器
{
//解析类
class Message
    {
        private byte[] date = new byte[10240];
        public int startIndex = 0; //存储了多少字节的数据

        public byte[] Date
        {
            get { return date; }
        }

        public int ReMainSize//得到剩余date大小
        {
            get { return date.Length - startIndex; }
        }

        public void AddCount(int count) //添加数据
        {
            startIndex += count;
        }

        public void ReadMessage()
        {

            while (true)
            {
                if (startIndex <= 4) //如果不大于4表示数据可能不完整.则不解析
                    return;

                int count = BitConverter.ToInt32(date, 0); //将date中的前4个字节(当前数据完整的长度是多少)读取到
                if ((startIndex - 4) >= count) //判断剩余的字节够不够,
                {

                   // Console.WriteLine("count" + count);
                    string temp = Encoding.UTF8.GetString(date, 4, count); //如果够, 读取当前完整的记录
                    Console.WriteLine("startIndex" + startIndex);
                    Console.WriteLine("解析出来一条数据:" + temp);
                    Array.Copy(date, count + 4, date, 0, startIndex - count - 4);// 复制当前数组到自己(就是替换) 把date数据,从已经解析的位置开始,赋值到自己,赋值到自己的0开始到剩余的数据长度
                    startIndex -= (count + 4); //把startIndex的位置向前提,(减去刚刚解析的数据长度)
                }
                else
                {
                    break;
                }

            }



        }

    }




    class Program
    {
        static void Main(string[] args)
        {
            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            serverSocket.Bind(new IPEndPoint(IPAddress.Parse("10.6.0.38"), 88));

            serverSocket.Listen(19);

            serverSocket.BeginAccept(ClientAccept, serverSocket);//异步连接(回调函数,参数)
            Console.ReadLine();

        }
        //static byte[] buffer = new byte[1024];
        static Message msg = new Message();
        static void ClientAccept(IAsyncResult ar)
        {


            Socket serverSocket = ar.AsyncState as Socket;

            Socket clientSocket = serverSocket.EndAccept(ar);

            clientSocket.Send(Encoding.UTF8.GetBytes("hellooo"));

            clientSocket.BeginReceive(msg.Date, msg.startIndex, msg.ReMainSize, SocketFlags.None, ServerReceive, clientSocket);//(存储到的数据,从第几个位置开始存储,向里存的最大数量,标志,回调,参数)这里如果接收到的数据大于ReMainSize也不会超过这个ReMainSize,如果ReMainSize设置的太大, 大于接收到的数据,那么存储到msg.date中会报错

            serverSocket.BeginAccept(ClientAccept, serverSocket);//在这里继续进行异步连接,有点递归的意思

        }

        static void ServerReceive(IAsyncResult ar)
        {
            Socket clientSocket = null;

            try
            {
                clientSocket = ar.AsyncState as Socket;
                int len = clientSocket.EndReceive(ar);

                if (len == 0)
                {
                    clientSocket.Close();
                    return;
                }
                msg.AddCount(len);

                msg.ReadMessage();//这里注意先手顺序, ↓这行代码一定要在之后
                clientSocket.BeginReceive(msg.Date, msg.startIndex, msg.ReMainSize, SocketFlags.None, ServerReceive, clientSocket);//这里如果接收到的数据大于ReMainSize也不会超过这个ReMainSize,如果ReMainSize设置的太大, 大于接收到的数据,那么存储到msg.date中会报错

            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                clientSocket.Close();
            }


            Console.ReadLine();
        }
    }
}

客户端

namespace 客户端
{
    class Program
    {
        static byte[] GetBytes(string date)
        {
            byte[] dateBytes = Encoding.UTF8.GetBytes(date);
            int len = dateBytes.Length;
            byte[] byteLengths = BitConverter.GetBytes(len);
            byte[] newBytes = byteLengths.Concat(dateBytes).ToArray();
            return newBytes;
        }




        static void Main(string[] args)
        {

            byte[] buffer = new byte[1024];

            Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            clientSocket.Connect(new IPEndPoint(IPAddress.Parse("10.6.0.38"), 88));

            int len = clientSocket.Receive(buffer);

            Console.WriteLine(Encoding.UTF8.GetString(buffer, 0, len));

            //while (true)
            //{
            //    string temp = Console.ReadLine();

            //    if (temp == "c")
            //    {
            //        clientSocket.Close();
            //        return;
            //    }

            //    byte[] tempByte = Encoding.UTF8.GetBytes(temp);

            //    clientSocket.Send(tempByte);
            //}

            for (int i = 0; i < 100; i++)
            {
                clientSocket.Send(GetBytes(i.ToString()));
            }

            Console.ReadLine();

        }
    }
}

以上是关于解决粘包问题的主要内容,如果未能解决你的问题,请参考以下文章

Netty进阶——粘包与半包(固定长度方式解决粘包问题)

Netty进阶——粘包与半包(固定长度方式解决粘包问题)

Netty进阶——粘包与半包(预设长度方式解决粘包问题)

Netty进阶——粘包与半包(预设长度方式解决粘包问题)

Netty进阶——粘包与半包(固定分隔符方式解决粘包问题)

Netty进阶——粘包与半包(固定分隔符方式解决粘包问题)