相信關閉序列如下(如所描述的here):套接字關閉:當我應該使用SocketShutdown.Both
的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
返回值從EndReceive
到Close
的套接字。這是否意味着我應該使用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();
}
在關閉序列期間'Send'也返回'0'。如果我更新*服務器*來考慮這一點,在我的標記可以在'Spam'方法中設置之前,'EndReceive'仍然會引發異常。我懷疑這一切都是因爲'CLOSE_WAIT'套接字狀態以某種方式被跳過了。 – Pooven