SOCKET TCP 粘包及半包問題

你們在使用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);   
  } 
} 

  

你們不喜勿噴,此篇文章的主要目的是給本身作個筆記,如能幫到一些後來人,那固然是極好的事情了。

相關文章
相關標籤/搜索