C# Socket通信三大問題詳解


C# Socket通信三大問題是什么呢?讓我們開始講述:

C# Socket通信三大問題之數據包界限符問題。

根據原項目中交通部標准,在連續觀測站中數據包中,使用﹤﹥兩個字符表示有效數據包開始和結束。實際項目有各自的具體技術規范

C# Socket通信三大問題之數據包不連續問題。

在TCP/IP等通信中,由於時延等原因,一個數據包被Socket做兩次或多次接收,此時在接收第一個包后,必須保存到TSession的DatagramBuffer中,在以后一並處理

C# Socket通信三大問題包並發與重疊問題。

由於客戶端發送過快或設備故障等原因,一次接收到一個半、兩個或多個包文。此時,也需要處理、一個半、兩個或多個包

先補充異步BeginReceive()回調函數EndReceiveData()中的數據包分合函數ResolveBuffer()。

下面是C# Socket通信三大問題的實例演示:

/// ﹤summary﹥  
/// 1) 報文界限字符為﹤﹥,其它為合法字符,   
/// 2) 按報文頭、界限標志抽取報文,可能合並包文  
/// 3) 如果一次收完數據,此時 DatagramBuffer 為空  
/// 4) 否則轉存到包文緩沖區 session.DatagramBuffer  
/// ﹤/summary﹥  
private void ResolveBuffer(TSession session, int receivedSize)  
{  
// 上次留下的報文緩沖區非空(注意:必然含有開始字符 ﹤,空時不含 ﹤)  
bool hasBeginChar = (session.DatagramBufferLength ﹥ 0);   
 
int packPos = 0;  // ReceiveBuffer 緩沖區中包的開始位置  
int packLen = 0;  // 已經解析的接收緩沖區大小  
 
byte dataByte = 0;  // 緩沖區字節  
int subIndex = 0;   // 緩沖區下標  
 
while (subIndex ﹤ receivedSize)  
{  
   // 接收緩沖區數據,要與報文緩沖區 session.DatagramBuffer 同時考慮  
   dataByte = session.ReceiveBuffer[subIndex];  
     
   if (dataByte == TDatagram.BeginChar) // 是數據包的開始字符﹤,則前面的包文均要放棄  
   {  
  // ﹤前面有非空串(包括報文緩沖區),則前面是錯包文,防止 AAA﹤A,1,A﹥ 兩個報文一次讀現象  
  if (packLen ﹥ 0)    
  {  
 Interlocked.Increment(ref _datagramCount);  // 前面有非空字符  
 Interlocked.Increment(ref _errorDatagramCount);  // 一個錯誤包  
 this.OnDatagramError();  
  }  
  session.ClearDatagramBuffer();  // 清空會話緩沖區,開始一個新包  
 
  packPos = subIndex;   // 新包起點,即﹤所在位置  
  packLen = 1;// 新包的長度(即﹤)  
  hasBeginChar = true;  // 新包有開始字符  
   }     
   else if (dataByte == TDatagram.EndChar)  // 數據包的結束字符 ﹥  
   {  
  if (hasBeginChar)  // 兩個緩沖區中有開始字符﹤  
  {  
 ++packLen;  // 長度包括結束字符﹥  
 
 // ﹥前面的為正確格式的包,則分析該包,並准備加入包隊列  
 AnalyzeOneDatagram(session, packPos, packLen);  
 
 packPos = subIndex + 1;  // 新包起點。注意:subIndex 在循環最后處 + 1  
 packLen = 0;   // 新包長度  
  }  
  else  // ﹥前面沒有開始字符,則認為結束字符﹥為一般字符,待后續的錯誤包處理  
  {  
 ++packLen;  //  hasBeginChar = false;  
  }  
   }  
   else  // 非界限字符﹤﹥,就是是一般字符,長度 + 1,待解析包處理  
   {  
  ++packLen;  
   }  
   ++subIndex;  // 增加下標號  
}  // end while  
 
if (packLen ﹥ 0)  // 剩下的待處理串,分兩種情況  
{  
   // 剩下包文,已經包含首字符且不超長,轉存到包文緩沖區中,待下次處理  
   if (hasBeginChar && packLen + 
session.DatagramBufferLength ﹤= _maxDatagramSize)  
   {  
  session.CopyToDatagramBuffer(packPos, packLen);  
   }  
   else  // 不含首字符,或超長  
   {  
  Interlocked.Increment(ref _datagramCount);  
  Interlocked.Increment(ref _errorDatagramCount);  
 
  this.OnDatagramError();  
  session.ClearDatagramBuffer();  // 丟棄全部數據  
   }  
}  
}  

  

C# Socket通信三大問題之分析包文AnalyzeOneDatagram()函數代碼補充如下:

/// ﹤summary﹥  
/// 具有﹤﹥格式的數據包加入到隊列中  
/// ﹤/summary﹥  
private void AnalyzeOneDatagram(  
TSession session, int packPos, int packLen)  
{  
if (packLen + session.DatagramBufferLength ﹥ _maxDatagramSize)    
// 超過長度限制  
{  
   Interlocked.Increment(ref _datagramCount);  
   Interlocked.Increment(ref _errorDatagramCount);  
   this.OnDatagramError();  
}  
else // 一個首尾字符相符的包,此時需要判斷其類型  
{  
   Interlocked.Increment(ref _datagramCount);  
   TDatagram datagram = new TDatagram();  
 
   if (!datagram.CheckDatagramKind())    
// 包格式錯誤(只能是短期BG、或長期SG包)  
   {  
  Interlocked.Increment(ref _datagramCount);  
  Interlocked.Increment(ref _errorDatagramCount);  
  this.OnDatagramError();  
  datagram = null;  // 丟棄當前包  
   }  
   else  // 實時包、定期包,先解析數據,判斷正誤,並發回確認包  
   {  
  datagram.ResolveDatagram();  
  if (true)  // 正確的包才入包隊列  
  {  
 Interlocked.Increment(ref _datagramQueueCount);  
 lock (_datagramQueue)  
 {  
_datagramQueue.Enqueue(datagram);  // 數據包入隊列  
 }  
  }  
  else 
  {  
 Interlocked.Increment(ref _errorDatagramCount);  
 this.OnDatagramError();  
  }  
   }  
}  
session.ClearDatagramBuffer();  // 清包文緩沖區  
} 

  

 

C# Socket通信三大問題之TSession的拷貝轉存數據包文的方法CopyToDatagramBuffer()代碼如下:

/// ﹤summary﹥  
/// 拷貝接收緩沖區的數據到數據緩沖區(即多次讀一個包文)  
/// ﹤/summary﹥  
public void CopyToDatagramBuffer(int startPos, int packLen)    
{  
int datagramLen = 0;  
if (DatagramBuffer != null) datagramLen =   
DatagramBuffer.Length;  
 
// 調整長度(DataBuffer 為 null 不會出錯)  
Array.Resize(ref DatagramBuffer,   
datagramLen + packLen);  
 
// 拷貝到數據就緩沖區  
Array.Copy(ReceiveBuffer, startPos,   
DatagramBuffer, datagramLen, packLen);  
}  

  

代碼中注釋比較詳細了,下面指出C# Socket通信三大問題實例開發思路:

使用TSession會話對象的字節數組ReceiveBuffer保存BeginReceiver()接收到的數據,使用字節數組DatagramBuffer保存一次接收后分解或合並的剩下的包文。本項目中,由於是5分鍾一個包,正常情況下不需要用到DatagramBuffer數組

處理ReceiveBuffer中的字節數據包時,先考慮DatagramBuffer是否有開始字符﹤。如果有,則當前包文是前個包文的補充,否則前個包文是錯誤的。正確的包文可能存在於兩個緩沖區中,見分析函數AnalyzeOneDatagram()

分析完接收數據包后,剩下的轉存到DatagramBuffer中,見函數CopyToDatagramBuffer()

設計時考慮的另一個重要問題就是處理速度。如果自動觀測站達到100個,此時5*60=300秒鍾就有100個包,即每3秒種一個包,不存在處理速度慢問題。但是,真正耗時的是判斷包是否重復!特別地,當設備故障時存在混亂上傳數據包現象,此時將存在大量的重復包。筆者采用了所謂的區間判重算法,較好地解決了判重速度問題,使得系統具有很好的可伸縮性(分析算法的論文被EI核心版收錄,呵呵,意外收獲)。事實上,前年的交通部接收服務器還不具備該項功能,可能是太費時間了。

還有,就是在.NET Framework的托管CLR下,系統本身的響應速度如何?當時的確沒有把握,認為只要穩定性和速度滿足要求就行了。三年半運行情況表明,系統有良好的處理速度、很好的穩定性、滿足了部省要求。

C# Socket通信三大問題的基本內容就向你介紹到這里了,希望對你了解和學習C# Socket通信三大問題有所幫助。


免責聲明!

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



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