2 rb622 rb622 于 2017.09.01 16:26 提问

c# Socket,tcp接收时会粘包?

private void RecMsg(object sokConnectionparn)
{
Socket sokClient = sokConnectionparn as Socket;
while (true)
{

            byte[] arrMsgRec = new byte[1024 * 1024 * 3+3];
            // 将接受到的数据存入到输入  arrMsgRec中;
            int length = -1;
            try
            {
                length = sokClient.Receive(arrMsgRec); // 接收数据,并返回数据的长度;

            }
            catch (SocketException se)
            {
                ShowMsg("异常:" + se.Message);

                dictSocket.Remove(sokClient.RemoteEndPoint.ToString());

                dictThread.Remove(sokClient.RemoteEndPoint.ToString());

                DeleteLib(sokClient.RemoteEndPoint.ToString());

                break;
            }
            catch (Exception e)
            {
                ShowMsg("异常:" + e.Message);
                dictSocket.Remove(sokClient.RemoteEndPoint.ToString());
                dictThread.Remove(sokClient.RemoteEndPoint.ToString());
                DeleteLib(sokClient.RemoteEndPoint.ToString());
                break;
            }
            try
            {
                if (arrMsgRec[0] == 0)  // 表示接收到的是数据;
                {
                    string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);// 将接受到的字节数据转化成字符串;
                    ShowMsg(strMsg);
                }
                if (arrMsgRec[0] == 1) // 表示接收到的是文件;
                {
                    filesource = sokClient.RemoteEndPoint.ToString();

                    //var i = BitConverter.ToInt32(arrMsgRec, arrMsgRec.Length-3);
                    if (System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1).Contains("FileName"))
                    {
                        string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);
                        bpp.FileName = strMsg.Split(':')[1];
                        bpp.Index = 0;
                        savepath = "";
                        savepath = Path.Combine(savepath, bpp.FileName);
                    }
                    else if (System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1).Contains("PackageCount"))
                    {
                        string strMsg = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length - 1);
                        bpp.PackageCount = int.Parse(strMsg.Split(':')[1]);
                    }

                    else if (arrMsgRec[1] == 22 && arrMsgRec[2] == 33)
                    {
                        //int i = BitConverter.ToInt32(arrMsgRec, arrMsgRec.Length - 3);
                        byte[] buffer = new byte[length - 3];
                        Buffer.BlockCopy(arrMsgRec, 3, buffer, 0, buffer.Length);
                        if (!string.IsNullOrEmpty(bpp.FileName))
                        {

                            Thread fw = new Thread(() => FileWrite(savepath, bpp.Index, 1024 * 1024 * 3, buffer.Length, buffer));
                            fw.IsBackground = true;
                            fw.Start();
                        }
                    }
                    else
                    {
                        stringtobyte("Err:" + bpp.Index.ToString(), 1);
                        dictSocket[filesource].Send(stringtobyte("Err:" + bpp.Index.ToString(), 1));
                    }

                }
            }
            catch(Exception ex)
            {
                //MessageBox.Show(ex.Message);

            }
        }
    }
            用这个作为后台线程接收,然后发文件(客户端线程分包自动发的)的同时传输信息的话会粘包吧,好像是这个说法,就是那一包文件数据会收不到,然后聊天信息会出现乱码,求教怎么解决,新手实在搞不定- -谢谢.

1个回答

code68
code68   2017.09.01 16:55

当socket接收到数据后,会根据buffer的大小一点一点的接收数据,比如:

对方发来了1M的数据量过来,但是,本地的buffer只有1024字节,那就代表socket需要重复很多次才能真正收完这逻辑上的一整个消息。
对方发来了5条2个字符的消息,本地的buffer(大小1024字节)会将这5条消息全部收入囊下...
那么,如何处理呢?下面我以最简单的一种文本消息来demo

根据上面所描述的情况,最重要的关键落在了下面3个因素的处理上

消息的结尾标记
接收消息时判断结尾标记
当本次buffer中没有结尾标记时怎么处理
我把写好的核心算法贴出来:

 StringBuilder sb = new StringBuilder();             //这个是用来保存:接收到了的,但是还没有结束的消息
        public void ReceiveMessage(object state)            //这个函数会被以线程方式运行
        {
            Socket socket = (Socket)state;
            while(true)
            {
                byte[] buffer = new byte[receiveBufferSize];  //buffer大小,此处为1024
                int receivedSize=socket.Receive(buffer);

                string rawMsg=System.Text.Encoding.Default.GetString(buffer, 0, receivedSize);
                int rnFixLength = terminateString.Length;   //这个是指消息结束符的长度,此处为\r\n
                for(int i=0;i<rawMsg.Length;)               //遍历接收到的整个buffer文本
                {
                    if (i <= rawMsg.Length - rnFixLength)
                    {
                        if (rawMsg.Substring(i, rnFixLength) != terminateString)//非消息结束符,则加入sb
                        {
                            sb.Append(rawMsg[i]);
                            i++;
                        }
                        else
                        {
                            this.OnNewMessageReceived(sb.ToString());//找到了消息结束符,触发消息接收完成事件
                            sb.Clear();
                            i += rnFixLength;
                        }   
                    }
                    else
                    {
                        sb.Append(rawMsg[i]);
                        i++;
                    }
                }
            }
        }

A2DTcpClient client = new A2DTcpClient("127.0.0.1", 5000);
client.NewMessageReceived += new MessageReceived(client_NewMessageReceived);
client.Connect();
client.Send("HELLO");
client.Close();

    static void client_NewMessageReceived(string msg)
    {
        Console.WriteLine(msg);
    }
code68
code68 回复rb622: 不客气,免开发为您提供技术资讯
3 个月之前 回复
rb622
rb622 谢谢我研究研究
3 个月之前 回复
Csdn user default icon
上传中...
上传图片
插入图片