SOCKET TCP 粘包及半包問題


大家在使用SOCKET通信編程的時候,一般會采用UDP和TCP兩種方式;TCP因為它沒有包的概念,它只有流的概念,並且因為發送或接收緩沖區大小的設置問題,會產生粘包及半包的現象。

 

場景:

服務端向連續發送三個“HelloWorld”(三次消息無間隔),那么客戶端接收到的情況會有以下三種:

1)HelloWorld  HelloWorld  HelloWorld  (客戶端接收三次)

2)HelloWorldHelloWor  ldHelloWorld    (客戶端接收兩次)

3)HelloWorldHelloWorldHelloWorld      (客戶端接收一次)

 

我們這里不詳細討論這些情況是如何產生的(博客園相關的文章有很多,大家不清楚的可以去查一查),我以自己的方式來描述一下如何處理粘包、半包的消息。

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);   
  } 
} 

  

大家不喜勿噴,此篇文章的主要目的是給自己做個筆記,如能幫到一些后來人,那當然是極好的事情了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM