你们在使用SOCKET通讯编程的时候,通常会采用UDP和TCP两种方式;TCP由于它没有包的概念,它只有流的概念,而且由于发送或接收缓冲区大小的设置问题,会产生粘包及半包的现象。编程
场景:blog
服务端向连续发送三个“HelloWorld”(三次消息无间隔),那么客户端接收到的状况会有如下三种:博客
1)HelloWorld HelloWorld HelloWorld (客户端接收三次)string
2)HelloWorldHelloWor ldHelloWorld (客户端接收两次)io
3)HelloWorldHelloWorldHelloWorld (客户端接收一次)class
咱们这里不详细讨论这些状况是如何产生的(博客园相关的文章有不少,你们不清楚的能够去查一查),我以本身的方式来描述一下如何处理粘包、半包的消息。coding
1)不要使消息产生粘包、半包现象循环
这个我是这样作的:把每一个包的大小固定,而且把发送缓冲区和接收缓冲区的大小都设置成包的大小(这个作法也许是不成熟的,但我试验下来,仍是比较有效并且高效的,但愿有其它更好处理方式的人能够指正)通信
2)把消息进行包装,根据外部包装特性来剥出每个粘在一块儿的消息数据
好比,发送HelloWorld,在HelloWorld外套个壳,变成<msg>HelloWorld</msg>,那么这个时候可能会收到这样的一个包:<msg>HelloWorld</msg><msg>HelloWorld</msg><msg>Hello和另外一个这样的包World</msg>,我是以最简单的方式,把这两个消息加工成三个HellWorld,请看代码:
//这是暂存上一个消息中不完整的消息内容 private string halfMsg = ""; private void ReceiveCallback(IAsyncResult AR) { try { int REnd = sckClient.EndReceive(AR); //上次未处理的消息内容+本次接收到的内容 string temp = halfMsg + Encoding.Default.GetString(msgBuffer, 0, REnd); //使用正则来提取消息内容 string pattern = "^<msg>.*?</msg>"; //循环提取,直到剩下的消息是不完整的数据(或恰好所有提取完) while(Regex.IsMatch(temp, pattern)) { string match = Regex.Match(temp, pattern).Groups[0].Value; temp = temp.Remove(0,match.Length); } //将正则循环提取后剩下的内容暂存(可能为空串) halfMsg = temp; msgBuffer = new byte[128]; //同时接收客户端回发的数据,用于回发 sckClient.BeginReceive(msgBuffer, 0, msgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null); } catch (Exception ex) { msgBuffer = new byte[128]; sckClient.BeginReceive(msgBuffer, 0, msgBuffer.Length, 0, new AsyncCallback(ReceiveCallback), null); } }
你们不喜勿喷,此篇文章的主要目的是给本身作个笔记,如能帮到一些后来人,那固然是极好的事情了。