2011-11-17 66 views
6

最近我解決了.Net同步接收方法的一個奇怪行爲。我需要編寫一個應用程序,它具有通過發送/接收數據相互通信的節點。每個服務器都有一個同步的接收循環,接收到一個序列化的類後,它將對其進行反序列化並對其進行處理。之後,它將這個序列化的類異步發送給一些選定的節點(使用AsynchSendTo)。.NET C#同步接收不會阻止

的MSDN明確說:

「如果您使用的是面向連接的插座,Receive方法 將讀取儘可能多的數據是可用的,直到緩衝區的大小 如果。遠程主機使用Shutdown 方法關閉Socket連接,並且已收到所有可用數據,接收方法 將立即完成並返回零字節。「

在我的情況是不正確的。在Receive建立連接之後,有一些隨機情況會立即阻塞並返回0字節(非確定性位置)。我100%確定發件人發送的是至少1000字節。另一個有趣的事實是:在收到之前將睡眠(500)放在一切正常的情況下。以下,對接收代碼:

_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
try 
{ 
    _listener.Bind(_serverEndpoint); 
    _listener.Listen(Int32.MaxValue); 
    while (true) 
    { 
     Console.WriteLine("Waiting for connection..."); 
     Socket handler = _listener.Accept(); 

     int totalBytes = 0; 
     int bytesRec; 
     var bytes = new byte[DATAGRAM_BUFFER]; 
     do 
     { 
      //Thread.Sleep(500); 
      bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None); 
      totalBytes += bytesRec; 
     } while (bytesRec > 0); 

     handler.Shutdown(SocketShutdown.Both); 
     handler.Close(); 
    } 
} 
catch (SocketException e) 
{ 
    Console.WriteLine(e); 
} 

而且發送部分:

public void AsynchSendTo(Datagram datagram, IPEndPoint recipient) 
{ 

    byte[] byteDatagram = SerializeDatagram(datagram); 
    try 
    { 
     var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     socket.BeginConnect(recipient, ConnectCallback, new StateObject(byteDatagram, byteDatagram.Length, socket)); 
    } 
    catch (SocketException e) 
    { 
     Console.WriteLine(e); 
    } 
} 

public void ConnectCallback(IAsyncResult result) 
{ 
    try 
    { 
     var stateObject = (StateObject)result.AsyncState; 
     var socket = stateObject.Socket; 
     socket.EndConnect(result); 
     socket.BeginSend(stateObject.Data, 0, stateObject.Data.Length, 0, new AsyncCallback(SendCallback), socket); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine("catched!" + ex.ToString()); 
    } 
} 

public void SendCallback(IAsyncResult result) 
{ 
    try 
    { 
     var client = (Socket)result.AsyncState; 
     client.EndSend(result); 
     client.Shutdown(SocketShutdown.Both); 
     client.Close(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
    } 
} 

class StateObject 
{ 
    public Byte[] Data { get; set; } 
    public int Size; 
    public Socket Socket; 
} 

我的問題:我使用的同步收到一條錯誤的路?儘管有數據要接收,爲什麼它不阻止事件?

+0

由於您的問題並不是專門針對您在閱讀前通過睡眠解決的問題。如果一次讀取很多,每個循環都會有半個秒的延遲,所以您是否嘗試了睡眠後睡眠時間更短,比如100msecs?這對我來說很適合從串口讀取數據,但也是在發生數據接收事件時。 – jlafay

+0

這只是套接字的工作方式。通過刪除所有try/catch塊來開始診斷。 –

回答

4

你正在拍攝自己的腳。

bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None); 

在連接的開始,Available將爲0,迫使其以0立即返回相反,你應該指定哪些是免費的在您的緩衝區的字節(如bytes.Length-totalBytes)的數量,那麼它也會阻止。

6

這裏可能有一個併發問題。接受連接後,您直接跳入接收。發件人進程可能沒有足夠的時間來發送呼叫,因此您的handler.Available爲0,並且接收返回。

這也是爲什麼當您添加500 ms的睡眠時不會發生「錯誤」的原因。

+0

你是對的,但晚了7分鐘;) – Lucero

+0

我想我還補充了500毫秒修復它的原因。我沒有複製你的答案... – Tudor

+0

對不起,我的意圖是*不*使你的答案看起來好像它是從我的複製。儘管如此,他們基本上也是這樣說的 - 我的內容通過「最初......」的句子暗示了延遲的不同行爲。 ;) – Lucero