你們在使用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); } }
你們不喜勿噴,此篇文章的主要目的是給本身作個筆記,如能幫到一些後來人,那固然是極好的事情了。