2010-08-26 85 views
3

使用Accept方法創建的TcpClient實例用於管理客戶端連接。當我需要終止服務器線程時會出現問題,因爲它在接收呼叫時被阻止。處理阻塞.NET套接字的超時時間

因此,我設置了一個TcpClient ReceiveTimeout以循環每個n毫秒來測試退出條件。結果是Receive操作引發了一個具有錯誤代碼SocketError.TimedOut的異常(SocketException)。好,我在想......

的問題是,Socket.Connected返回false,但作爲MSDN文檔中規定的屬性:

Connected屬性的值反映了作爲連接的狀態最近的操作。如果您需要確定連接的當前狀態,請進行非阻塞的零字節發送呼叫。如果調用成功返回或拋出WAEWOULDBLOCK錯誤代碼(10035),那麼套接字仍然連接;否則,套接字不再連接。

所以,我做什麼規定:

try { 
    // Receive operation on socket stream 
    // Send operation on socket stream 
} catch (SocketException e) { 
    if (e.SocketErrorCode == SocketError.TimedOut) { 
    try { 
     IAsyncResult asyncResult; 
     int sResult; 

     asyncResult = mSocket.Client.BeginSend(new byte[] {}, 0, 0, SocketFlags.None, delegate(IAsyncResult result) { }, null); 
     sResult = mSocket.Client.EndSend(asyncResult); 
     Debug.Assert(asyncResult.IsCompleted == true); 

     if (mSocket.Connected == false) 
      throw new Exception("not more connected"); // Always thrown 
    } catch (Exception e) { 
      // ... 
     } 
} 

但是,即使執行aynch發送操作,財產mSocket.Connected永遠是假的,導致外環終止(其它線程調用Disconnect方法來終止服務器線程)。

我錯過了什麼?

回答

2

的問題是,如果出現超時的TcpClient斷開連接。所以你的方法將無法工作。 使用異步讀/寫功能或使用select。

與異步函數調用的可能最簡單的方法是這樣的:

byte[] data = new byte[4096]; 
IASyncResult result = stream.BeginRead(data, 0, data.Length, null, null); 
result.AsyncWaitHandle.WaitOne(<timeout value in ms>); 
int bytes = stream.EndRead(result); 

if (!result.IsCompleted) 
    <timed out> 
else 
    <read data> 
... 
+0

恭候您的第一個答案!但這正是我試圖避免的。對於使用非阻塞套接字,我會重構服務器邏輯並使每個網絡操作異步,而不會產生額外的線程。 「問題是如果發生超時TcpClient斷開連接」:它寫在哪裏?在stdc非阻塞套接字中,在超時(使用select實現)之後,不會斷開連接。 – Luca 2010-08-26 13:20:44

+0

我發佈的代碼使用異步BeginRead/EndRead函數。但它阻止使用WaitOne。所以你不必改變你的服務器邏輯。 BeginRead不會阻止。 WaitOne阻塞,直到出現數據或發生指定的超時。使用!result.IsCompleted來檢查它是否超時或有數據可用。 – pitt7 2010-08-26 14:07:25

+0

我不知道「如果發生超時,TcpClient斷開連接」在哪裏寫入。但我有完全相同的問題。如果Read由超時返回,則連接爲False。我沒有對此做過多的研究,只是認爲C#中並沒有設置超時值,並定期運行到這個超時。 – pitt7 2010-08-26 14:11:04

0

您應該看看鏈接到的Socket.Connected MSDN頁面上的C#示例。它具有明顯不同的方法來確定套接字是否仍然連接。

// .Connect throws an exception if unsuccessful 
client.Connect(anEndPoint); 

// This is how you can determine whether a socket is still connected. 
bool blockingState = client.Blocking; 
try 
{ 
    byte [] tmp = new byte[1]; 

    client.Blocking = false; 
    client.Send(tmp, 0, 0); 
    Console.WriteLine("Connected!"); 
} 
catch (SocketException e) 
{ 
    // 10035 == WSAEWOULDBLOCK 
    if (e.NativeErrorCode.Equals(10035)) 
     Console.WriteLine("Still Connected, but the Send would block"); 
    else 
    { 
     Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode); 
    } 
} 
finally 
{ 
    client.Blocking = blockingState; 
} 

Console.WriteLine("Connected: {0}", client.Connected); 
+0

我也有同樣的行爲。發送例程不會拋出異常,並且Connected屬性保持設置爲false。後來的例程調用(發送/接收)的行爲與galbarm答案描述的一樣。 – Luca 2010-08-26 13:11:28