2009-08-26 47 views
1

什麼是模式和/或如何更改以下代碼,以便可以從服務器或客戶端正常關閉連接,而無需執行異常處理。如何正常關閉TCP連接,沒有例外?

  1. 的TcpClient與插座:我不依賴於使用TcpClient的 客戶端類。我寫了以下 代碼來嘗試演示 最簡單的情況。我一直在 使用套接字& SocketAsyncEventArgs 但事情變得太 複雜處理此問題。

  2. 阻斷與異步:由於 阻塞調用的,這可能是困難的 或不可能的。如果是這樣,那很好,但是 你如何解決它在 異步情況下?

  3. 退出令牌?:我已經嘗試了發送某種「退出」令牌另一邊,以便它知道要關機,但沒有得到它的工作,我想在這裏提出一個小例子, 。

  4. 異常處理不管怎麼說需要:我知道異常處理將在實際應用程序,網絡連接等將失敗是必要的。但無法正常關機的情況下,無法正常處理預計

編輯:我感動原件及答案代碼gist

原始遇事:遷至此地:https://gist.github.com/958955#file_original.cs

當前工作答https://gist.github.com/958955#file_answer.cs

class Channel 
{ 
    private readonly TcpClient client; 
    private readonly NetworkStream stream; 
    private readonly string name; 
    private readonly ManualResetEvent quit = new ManualResetEvent(false); 

    public Channel(string name, TcpClient client) 
    { 
     this.name = name; 
     this.client = client; 
     stream = client.GetStream(); 
    } 

    public void Run() 
    { 
     Console.WriteLine(name + ": connected"); 
     byte[] buffer = new byte[client.Client.ReceiveBufferSize]; 
     stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer); 

     var writer = new StreamWriter(stream, Encoding.ASCII); 

     while (true) 
     { 
      var line = Console.ReadLine(); 
      if (String.IsNullOrEmpty(line) || !this.client.Connected) break; 

      writer.WriteLine(line); 
      writer.Flush(); 
     } 

     if (client.Connected) 
     { 
      Console.WriteLine(name + " shutting down send."); 
      client.Client.Shutdown(SocketShutdown.Send); 
      Console.WriteLine(name + " waiting for read to quit."); 
      quit.WaitOne(); 
     } 
     else 
     { 
      Console.WriteLine(name + " socket already closed"); 
     } 

     Console.WriteLine(name + " quit, press key to exit."); 
     Console.ReadKey(); 
    } 

    private void Read(IAsyncResult result) 
    { 
     var bytesRead = this.stream.EndRead(result); 
     if (bytesRead == 0) 
     { 
      Console.WriteLine(name + " read stopped, closing socket."); 
      this.client.Close(); 
      this.quit.Set(); 
      return; 
     } 

     var buffer = (byte[])result.AsyncState; 
     Console.WriteLine(name + " recieved:" + Encoding.ASCII.GetString((byte[])result.AsyncState, 0, bytesRead)); 

     stream.BeginRead(buffer, 0, buffer.Length, this.Read, buffer); 
    } 
} 

回答

2

在調用Socket.Close()之前使用Socket.Shutdown()。等待關閉處理完成(例如ReceiveAsync()將返回0字節)。

  1. 抽象選擇(大部分)是一個非問題。
  2. 異步I/O意味着永遠不會看到你的遺憾。
  3. 來自針對Socket.ReceiveAsync()方法的MS文檔:對於字節流,已讀取的零字節表示正常關閉,並且不會再有字節被讀取。
0

TcpClient的實現IDisposable,所以你應該能夠使用它像這一點,然後只是不關心關閉客戶端 - 使用語句應該爲你做或無不是拋出一個異常:

class Client 
{ 
    static void Main(string[] args) 
    { 
     using (TcpClient client = new TcpClient(AddressFamily.InterNetwork)) 
     { 

      Console.WriteLine("client: created, press key to connect"); 
      Console.ReadKey(); 

      client.Connect(IPAddress.Loopback, 5000); 

      var channel = new Channel("client", client); 
      channel.Run(); 
     } 
    } 
} 

這就是說,實現了IDisposable將明確地說,他們關閉自己的具體基礎資源的文檔中(例如:SqlConnection)正常CLR類型,但對此事的TcpClientdocs是出奇安靜—你可能想先測試一下。

+0

當前示例如果一方關閉連接上述將拋出異常。使用不會陷入異常。無論如何,我正在尋找一種避免在正常關機期間完全拋出異常的方法。 – 2009-08-26 19:07:48

0

我還沒有做過一段時間的套接字,但我記得你必須爲它們調用shutdown()。

在.NET等效,我想,將socket.Shutdown(SocketShutdown.Both)