2015-09-04 64 views
6

相信關閉序列如下(如所描述的here):套接字關閉:當我應該使用SocketShutdown.Both

Socket Shutdown sequence

MSDN documentation(備註部分)讀取:

當使用面向連接的Socket時,請在關閉Socket之前始終調用Shutdown方法。這可確保所有數據在關閉之前在連接的套接字上發送和接收。

這似乎意味着如果我使用Shutdown(SocketShutdown.Both),任何尚未收到的數據仍可能被使用。要測試此:

  • 我連續發送數據到客戶端(通過Send在一個單獨的線程)。
  • 客戶端執行Shutdown(SocketShutdown.Both)
  • 服務器上的BeginReceive回調會執行,但是EndReceive會引發異常:現有連接被遠程主機強制關閉。這意味着我無法收到0返回值,並依次呼叫Shutdown

根據要求,我已經發布了下面的服務器端代碼(它被封裝在一個Windows窗體中,它被創建爲一個實驗)。在我的測試場景中,我沒有看到TCPView中的CLOSE_WAIT狀態,因爲我通常沒有發送連續數據。所以可能我做了一些錯誤的事情,並且我錯誤地中斷了後果。在另一個實驗中:

  • 客戶端連接到服務器。
  • 客戶端執行Shutdown(SocketShutdown.Both)
  • 服務器收到關機確認併發送一些數據作爲響應。服務器也執行Shutdown
  • 客戶端從服務器接收數據,但在未來BeginReceive是不允許的:請求發送或接收數據是不允許的,因爲套接字已經在這個方向與以前的關閉調用關閉

在在這種情況下,我仍然期待0返回值從EndReceiveClose的套接字。這是否意味着我應該使用Shutdown(SocketShutdown.Send)?如果是這樣,那麼應該在什麼時候使用Shutdown(SocketShutdown.Both)

碼從第一個實驗:

private TcpListener SocketListener { get; set; } 
private Socket ConnectedClient { get; set; } 
private bool serverShutdownRequested; 
private object shutdownLock = new object(); 

private struct SocketState 
{ 
    public Socket socket; 
    public byte[] bytes; 
} 

private void ProcessIncoming(IAsyncResult ar) 
{ 
    var state = (SocketState)ar.AsyncState; 
    // Exception thrown here when client executes Shutdown: 
    var dataRead = state.socket.EndReceive(ar); 
    if (dataRead > 0) 
    { 
    state.socket.BeginReceive(state.bytes, 0, state.bytes.Length, SocketFlags.None, ProcessIncoming, state); 
    } 
    else 
    { 
    lock (shutdownLock) 
    { 
     serverShutdownRequested = true; 
     state.socket.Shutdown(SocketShutdown.Both); 
     state.socket.Close(); 
     state.socket.Dispose(); 
    } 
    } 
} 

private void Spam() 
{ 
    int i = 0; 
    while (true) 
    { 
    lock (shutdownLock) 
    { 
     if (!serverShutdownRequested) 
     { 
     try { ConnectedClient.Send(Encoding.Default.GetBytes(i.ToString())); } 
     catch { break; } 
     ++i; 
     } 
     else { break; } 
    } 
    } 
} 

private void Listen() 
{ 
    while (true) 
    { 
    ConnectedClient = SocketListener.AcceptSocket(); 
    var data = new SocketState(); 
    data.bytes = new byte[1024]; 
    data.socket = ConnectedClient; 
    ConnectedClient.BeginReceive(data.bytes, 0, data.bytes.Length, SocketFlags.None, ProcessIncoming, data); 
    serverShutdownRequested = false; 
    new Thread(Spam).Start(); 
    } 
} 

public ServerForm() 
{ 
    InitializeComponent(); 
    var hostEntry = Dns.GetHostEntry("localhost"); 
    var endPoint = new IPEndPoint(hostEntry.AddressList[0], 11000); 
    SocketListener = new TcpListener(endPoint); 
    SocketListener.Start(); 
    new Thread(Listen).Start(); 
} 
+0

在關閉序列期間'Send'也返回'0'。如果我更新*服務器*來考慮這一點,在我的標記可以在'Spam'方法中設置之前,'EndReceive'仍然會引發異常。我懷疑這一切都是因爲'CLOSE_WAIT'套接字狀態以某種方式被跳過了。 – Pooven

回答

1

這似乎意味着,如果我用shutdown(SocketShutdown.Both),即尚未收到任何數據,仍可以消耗

不,數據不見了。您通過關閉明確清除它。沒有辦法知道遠程端將發送多少數據,所以即使關機不清除結果也是非確定性的,並且它對你毫無幫助。

一個現有的連接被強行遠程主機

這不應該發生關閉。郵政編碼。

這是否意味着我應該使用Shutdown(SocketShutdown.Send)來代替?

如果你想繼續接收你爲什麼要關閉接收?只是不要那樣做。只關閉發送。

如果是這樣,什麼時候應該使用Shutdown(SocketShutdown.Both)?

當你既不想接收也不發送。你能否詳細說明爲什麼這對你不明顯?也許有誤解,或者我不明白這個問題。

+0

謝謝你的回覆。 ** 1。**如果數據不存在,那麼'Shutdown'和'Close'有什麼不同?文檔意味着什麼:'這確保了所有數據的發送和接收'? ** 2。**我只發佈了服務器代碼;夠了嗎? ** 3。**不關閉(SocketShutdown.Send)意味着我允許傳入數據? ** 4。**如果我不想接收或發送,那麼爲什麼我需要'Shutdown'而不是'Close'?與第1項有關,我試圖瞭解正確的套接字斷開順序以及不執行此操作的後果。 – Pooven

+0

1.關閉不報告錯誤。通常,您應該在關閉之前調用shutdown,以便向您報告錯誤。我也需要客戶。服務器看起來很好。 3.是的。 4.參見(1)。正確的順序是關閉(兩者);處置。切勿致電關閉。 – usr