2011-08-01 294 views
3

我正在練習異步CTP框架,並且作爲練習,我將創建一個能夠查詢服務器(使用任意協議)的TCP客戶端。無論如何,因爲連接上的問題,我在最初的階段被卡住了。要麼我還是不明白一些基本觀點,或者有些奇怪的東西。異步TCP客戶端連接掛斷

所以,這裏是異步連接器:

public class TaskClient 
{ 
    public static Task<TcpClient> Connect(IPEndPoint endPoint) 
    { 
     //create a tcp client 
     var client = new TcpClient(AddressFamily.InterNetwork); 

     //define a function to return the client 
     Func<IAsyncResult, TcpClient> em = iar => 
     { 
      var c = (TcpClient)iar.AsyncState; 
      c.EndConnect(iar); 
      return c; 
     }; 

     //create a task to connect the end-point async 
     var t = Task<TcpClient>.Factory.FromAsync(
      client.BeginConnect, 
      em, 
      endPoint.Address.ToString(), 
      endPoint.Port, 
      client); 

     return t; 
    } 
} 

我的意思是隻有一次調用此函數,然後有回TcpClient實例用於任何後續的查詢(代碼這裏沒有顯示)。

某處在我的形式,我所說的以上功能如下:

//this method runs on the UI thread, so can't block 
    private void TryConnect() 
    { 
     //create the end-point 
     var ep = new IPEndPoint(
      IPAddress.Parse("192.168.14.112"), //this is not reachable: correct! 
      1601); 

     var t = TaskClient 
      .Connect(ep) 
      .ContinueWith<TcpClient>(_ => 
      { 
       //tell me what's up 
       if (_.IsFaulted) 
        Console.WriteLine(_.Exception); 
       else 
        Console.WriteLine(_.Result.Connected); 

       return _.Result; 
      }) 
      .ContinueWith(_ => _.Result.Close()); 

     Console.WriteLine("connection in progress..."); 

     //wait for 2" then abort the connection 
     //Thread.Sleep(2000); 
     //t.Result.Client.Close(); 
    } 

的測試是嘗試連接遠程服務器,但它必須是不可達(PC,但服務已停止)。

當我運行TryConnect函數時,它會盡快返回正確的「正在進行連接...」,然後顯示異常,因爲遠程端點已關閉。優秀!

問題是它需要幾秒鐘來返回異常,我想給用戶機會取消正在進行的操作。根據有關BeginConnect方法的MSDN規範,如果您想中止異步操作,只需在工作套接字上調用Close即可。

所以,我試圖在最後添加幾行(註釋如上),以便在2秒後模擬用戶取消。結果看起來像是應用程序(沙漏)的掛起。通過暫停IDE,它停在最後一行t.Result.Client.Close()。但是,通過停止IDE,一切正常結束,沒有任何例外。

我也試過直接關閉客戶端t.Result.Close(),但它完全一樣。

這是我,還是連接過程中有任何問題?

非常感謝。

回答

1

嘗試在對象上調用Dispose() - 這比Close()更具侵略性。您可以查看TcpClient類中的各種Timeout成員,並將它們設置爲更合適的值(例如,在LAN環境中1秒可能足夠好)。您還可以查看.Net 4.0中的CancellationTokenSource功能。這允許你發信號給Task你希望它停止 - 我發現一個article可能讓你開始。

您還應該知道哪個線程實際上處於停滯狀態(主線程可能正在等待另一個被阻塞的線程),例如, .ContinueWith(_ => _.Result.Close())可能是問題(你應該檢查兩次關閉套接字時的行爲)。在調試時打開線程窗口(Debug - > Windows - > Threads)並查看每個線程。

2

t.Result.Close()將等待t任務完成。 t.ContinueWith()也將等待任務的完成。

要取消您必須等待2個任務:tcp和一個計時器。
使用async tcp語法:

await Task.WhenAny(t,Task.Delay(QueryTimeout)); 
if (!t.IsCompleted) 
    tcpClient.Close(); //Cancel task