前往顾页
以后地位: 主页 > 收集编程 > .Net实例教程 >

c# Socket通信数据包机关的问题

时候:2014-06-01 23:00来源:知行网www.zhixing123.cn 编辑:麦田守望者

前文讲到我用tcp通信来做金域的接口代办代理,做好了以后,却发明了一个问题,对TCP通信来讲,默许的数据包长度是1492,是以,当我发送的数据年夜于1492的时候,问题就呈现了,数据被拆分了,要不就是没法判定数据都领受完了,要不就是粘包了。

1、若何判定数据是不是领受完?

凡是,我们经由过程socket来领受数据是利用一个循环去读取的,读到了数据就插手到缓存的数据中去。但是,如果仅仅是循环的话,你怎样晓得甚么时候数据就领受完了呢?
 

while (bLoop == true)
            {

                if (ASocket.Available > 0)
                {
netStream.Read(buffer, 0, buffer.Length);
}

}

要处理这个问题,机关数据包就显得十分需求了,经由过程获得包头中数据总量的年夜小,来判定,这一次的数据有没有领受完。当然会有更加便利的体例,比如定义一个包的ID和包数量,当这个包的ID和包数量相称时,就是最后一个数据包了。

下面两个函数就是用来机关和翻译数据包的:

        public DataPacket BytesToPacket(byte[] ABytes)
        {
            DataPacket dp = new DataPacket();
            byte[] btID = new byte[4];
            Array.Copy(ABytes, 0, btID, 0, btID.Length);
            dp.ID=BitConverter.ToInt32(btID, 0);

            byte[] btMaxID = new byte[4];
            Array.Copy(ABytes, 4, btMaxID, 0, btMaxID.Length);
            dp.MaxID =BitConverter.ToInt32(btMaxID,0);

            byte[] btLenth = new byte[4];
            Array.Copy(ABytes, 8, btLenth, 0, btLenth.Length);
            dp.Length = BitConverter.ToInt32(btLenth, 0);

            byte[] btTotalLength = new byte[4];
            Array.Copy(ABytes, 12, btTotalLength, 0, btTotalLength.Length);
            dp.TotalLength = BitConverter.ToInt32(btTotalLength, 0);

            dp.Content=new byte[1024];
            Array.Copy(ABytes, 16, dp.Content, 0, dp.Content.Length);

            return dp;

        }

 public byte[] PacketToBytes(DataPacket ADp)
        {
            byte[] btSend = new byte[1024 + 16];
            byte[] btID = new byte[4];
            BitConverter.GetBytes(ADp.ID).CopyTo(btID, 0);
            btID.CopyTo(btSend, 0);

            byte[] btMaxID = new byte[4];
            BitConverter.GetBytes(ADp.MaxID).CopyTo(btMaxID, 0);
            btMaxID.CopyTo(btSend, 4);

            byte[] btLength = new byte[4];
            BitConverter.GetBytes(ADp.Length).CopyTo(btLength, 0);
            btLength.CopyTo(btSend, 8);

            byte[] btTotalLength = new byte[4];
            BitConverter.GetBytes(ADp.TotalLength).CopyTo(btTotalLength, 0);
            btTotalLength.CopyTo(btSend, 12);

            ADp.Content.CopyTo(btSend, 16);

            return btSend;

        }

那么,如果我们需求发送数据的话,就得先计较数据有多年夜,要分多少个包去发送了:

public void SendString(Socket ASocket, string AStr)
        {
            Byte[] sendBytes = Encoding.Unicode.GetBytes(AStr);
            //计较包数
            int iBagNum = (sendBytes.Length / MaxLength) + 1;
            // MessageBox.Show(iBagNum.ToString());
            int iLastBagLength = sendBytes.Length % MaxLength;
            // MessageBox.Show(iLastBagLength.ToString());
            for (int i = 0; i < iBagNum; ++i)
            {
                //  MessageBox.Show(i.ToString());
                DataPacket dp = new DataPacket();
                if (i == (iBagNum - 1))
                {

                    dp.Length = iLastBagLength;

                }
                else
                {
                    dp.Length = MaxLength;
                }
                dp.ID = i + 1;
                dp.MaxID = iBagNum;
                dp.MaxLength = MaxLength;
                dp.TotalLength = sendBytes.Length;
                dp.Offset = i * 1024;

                dp.Content = new byte[1024];
                Array.Copy(sendBytes, dp.Offset, dp.Content, 0, dp.Length);

                SendData(ASocket, PacketToBytes(dp));
            }

        }

2、粘包问题。

甚么是粘包问题?粘包就是在socket领受数据的时候,socket的缓存内里存有2个或两个以上包年夜小的数据,如果你的包年夜小不一,或压根就没有定义包的布局,那就会很费事,因为,完整不晓得要读的数据有多年夜。但是当我们机关了有包头的数据包来传输数据的时候,就没有这个问题了,粘多少个包,就多读取多少次了:

 public string ReceiveString(Socket ASocket)
        {
            NetworkStream netStream =new NetworkStream( ASocket);
            bool bLoop = true;
            byte[] bySource=null;
            while (bLoop == true)
            {

                if (ASocket.Available > 0)
                {
                    int iNum = (ASocket.Available / 1040);
                    //MessageBox.Show(iNum.ToString());
                    for (int i = 0; i < iNum; ++i)
                    {

                        byte[] buffer = new byte[1040];
                        netStream.Read(buffer, 0, buffer.Length);
                        DataPacket dp = BytesToPacket(buffer);

                        if (dp.ID == 1)
                        {
                            bySource = new byte[dp.TotalLength];
                        }
                        //MessageBox.Show(dp.ID.ToString());
                        //MessageBox.Show(dp.Length.ToString());
                        Array.Copy(dp.Content, 0, bySource, (dp.ID - 1) * 1024, dp.Length);
                        //Debug.WriteLine(dp.MaxID);
                        if (dp.ID == dp.MaxID)
                        {
                            bLoop = false;
                        }
                    }
                }
            }
            string str = Encoding.Unicode.GetString(bySource);
            return str;

        }

这此中发明了socket通信的一个小bug,ASocket.Available代表的是缓存内里有多少字节可以读取,当你读取一半今后,这个参数其实不会变成剩下一半,而是没有了,只需你读取,这个参数就变成0了,是以如果你的语句是这么写的:

while(ASocket.Available > 0)
{
//读取数据
}
粘包过去的前面的包,你就都丧失落了,这是个很恼火的问题,当我在此中插手MessageBox.show()等操纵的时候,多是因为等了一下数据,就不会丢包,这让调试起来,非常恼火!

顶一下
(1)
100%
踩一下
(0)
0%
------分开线----------------------------
标签(Tag):C# C#实例教程 c#根本教程 C#源代码 c#技能
------分开线----------------------------
颁发评论
请自发遵循互联网相关的政策法规,严禁公布色情、暴力、革命的谈吐。
评价:
神色:
考证码:点击我更换图片
猜你感兴趣