2012-05-30 62 views
1

我正在使用C#套接字(異步模式)並需要從流中讀取確切的字節數來正確解析消息。由於系統中的消息非常長,看起來像socket.EndRead操作返回的字節數少於socket.BeginRead中所請求的字節數。只有在讀取了確切的字節數時,纔有可能完成c#套接字標記操作?被uing的NetworkStream是這樣的??? \從c#套接字讀取確切的字節數

IAsyncRes ar = socket.BeginRead(1Mb byte message) 
ar.Handle.Wait() // --> will signal ONLY when 1Mb us read !!!! 
socket.EndRead() 

UPD:

我已經用C#迭代器解決了這個問題。 (這裏沒有顯示它運行的irator環和負責做的MoveNext線程)

protected IEnumerator<IAsyncResult> EnumReceiveExact(byte[] array) 
     { 


      int offset = 0; 

      while (offset < array.Length) 
      { 
       SocketError err = SocketError.Success; 
       IAsyncResult ar = _socket.BeginReceive(array, offset, array.Length - offset, SocketFlags.None, out err, null, null); 
       Console.WriteLine("{0}:err:{1}", this, err); 
       if (err != SocketError.Success) 
       { 
        _socket.Close(); 
        throw new Exception("Error " + err); 
       } 

       yield return ar; 
       while (!ar.IsCompleted) 
       { 
        yield return ar; 
       } 

       offset += _socket.EndReceive(ar, out err); 
       if (err != SocketError.Success) 
       { 
        _socket.Close(); 
        throw new Exception("Error " + err); 
       } 

      } 

     } 
+0

在調用'socket.EndRead()'時,它將返回可讀取的字節數。 – Gavin

+0

我知道,但我不想複雜的閱讀大塊的消息,有沒有一些包裝已經做到了。 – Boris

+0

你不應該大塊閱讀它?如果你的客戶端發送1024字節,你應該「一次接收1024個字節」。 http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.endread(v=vs.80).aspx – Gavin

回答

0

與統計員良好的通話,但我希望你的外部編碼,不只是調用了WaitOne在asyncresults,因爲這會阻止正在運行的線程。

如果您喜歡這種異步編碼風格,請在NuGet上檢查Wintellect AsyncEnumerator - 它也使用迭代器,使代碼非常節約資源,並增加更簡單的方法來處理取消和異常,同時確保APM最終方法全部被調用。

我以前所要解決的具體問題讀:

1)添加長度前綴定義助手類,它具有以下方法,對一個Socket作品插座
2)發送的數據:

public IAsyncResult BeginRead(AsyncCallback callback) 
// Calculate whether to read length header or remaining payload bytes 
// Issue socket recieve and return its IAsyncResult 

public MemoryStream EndRead(IAsyncResult result) 
// Process socket read, if payload read completely return as a memorystream 
// If length header has been recieved make sure buffer is big enough 
// If 0 bytes recieved, throw SocketException(10057) as conn is closed 

public IAsyncResult BeginSend(AsyncCallback callback, MemoryStream data) 
// Issue sends for the length header or payload (data.GetBuffer()) on the first call 

public Boolean EndSend(IAsyncResult result) 
// Process bytes sent, return true if payload sent completely. 
// If 0 bytes sent, throw SocketException(10057) 

所以它仍然需要在循環中調用,但看起來像一個正常的異步操作,例如通過asyncenumerator調用(沒有取消檢查和異常處理):

do 
{ 
    socketHelper.BeginSend(ae.End(1, ar => socketHelper.EndSend(ar)), sendData); 
    yield return 1; 
    doneSend = socketHelper.EndSend(ae.DequeueAsyncResult()); 
} 
while (!doneSend); 
+0

外部循環非常複雜,因爲它管理多個客戶端的讀取。它接收來自每個迭代器的句柄並等待,直到其中一個信號發出,然後在適當的迭代器上調用MoveNext – Boris

+0

好吧,所以假設你的外部循環等待多個短暫超時的asyncresult句柄,它會稍微高效地使用回調異步操作 - 保存阻塞外部線程。 –