套接字关闭:我应该什么时候使用 SocketShutdown.Both
Socket Shutdown: when should I use SocketShutdown.Both
我相信关机顺序如下(如here所述):
MSDN documentation(备注部分)内容为:
When using a connection-oriented Socket
, always call the Shutdown
method before closing the Socket
. This ensures that all data is sent and received on the connected socket before it is closed.
这似乎意味着如果我使用Shutdown(SocketShutdown.Both)
,任何尚未接收到的数据可能仍会被消耗。要对此进行测试:
- 我不断向客户端发送数据(通过
Send
在单独的线程中)。
- 客户端执行了
Shutdown(SocketShutdown.Both)
.
- 服务器上的
BeginReceive
回调执行,但是,EndReceive
抛出异常:现有连接被远程主机强行关闭。这意味着我无法接收 0
return 值,因此无法调用 Shutdown
.
根据要求,我在下面发布了服务器端代码(它包装在 Windows 表单中,只是作为实验创建的)。在我的测试场景中,我没有像往常一样在不发送连续数据的情况下看到 CLOSE_WAIT
state in TCPView 。所以可能我做错了什么,我错误地打断了后果。在另一个实验中:
- 客户端连接到服务器。
- 客户端执行
Shutdown(SocketShutdown.Both)
.
- 服务器收到关闭确认并发送一些数据作为响应。服务器还执行
Shutdown
.
- 客户端从服务器接收数据,但不允许下一个
BeginReceive
:发送或接收数据的请求被禁止,因为套接字已经在那个方向上被关闭了关机呼叫
在这种情况下,我仍然期待从 EndReceive
到 Close
套接字的 0
return 值。这是否意味着我应该改用 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();
}
Shutdown(SocketShutdown.Both)
当您不想接收或发送时应该使用。您要么想突然关闭连接,要么您知道对方已使用 SocketShutdown.Receive
关闭。例如,您有一个时间服务器将当前时间发送到连接它的客户端,服务器发送时间并调用 Shutdown(SocketShutdown.Received)
因为它不再期望来自客户端的任何更多数据。客户端在收到时间数据后应调用 Shutdown(SocketShutdown.Both),因为它不会发送或接收任何进一步的数据。
Shutdown(SocketShutdown.Both) 禁用当前套接字上的发送和接收操作。调用 Shutdown(SocketShutdown.Both) 是您的客户端与服务器的实际 断开连接。您可以通过检查服务器端 SocketState 对象中的套接字 Connected 属性 来查看:它将为 false。
发生这种情况是因为关闭操作是不可逆的,所以在停止套接字上的发送和接收之后,保持连接是没有意义的,因为它是孤立的。
“一旦调用关闭函数来禁用发送和/或接收,就没有方法可以为现有的套接字连接重新启用发送或接收。”
(https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown)
关于你的问题:
- 我不断地向客户端发送数据(通过在单独的线程中发送)。
- 客户端执行了关机(SocketShutdown.Both)。 --> 这将断开客户端连接
- 服务器上的 BeginReceive 回调执行,但是,EndReceive 抛出一个
异常:现有连接被远程主机强行关闭。这意味着
我无法收到 0 return 值并调用 Shutdown。
EndReceive 抛出异常,因为客户端套接字不再连接。
要优雅地终止套接字:
客户端套接字调用 Shutdown(SocketShutdown.Send)) 但应该继续接收
在服务器上,EndReceive returns 读取了 0 个字节(客户端发出信号表明其端没有更多数据)
服务器
A)发送它的最后一个数据
B) 调用 Shutdown(SocketShutdown.Send))
C) 在套接字上调用 Close,可选择超时以允许从客户端读取数据
客户
A) 从服务器读取剩余数据,然后接收到 0 字节(服务器发出信号,表明其端没有更多数据)
B) 在套接字上调用 Close
我相信关机顺序如下(如here所述):
MSDN documentation(备注部分)内容为:
When using a connection-oriented
Socket
, always call theShutdown
method before closing theSocket
. This ensures that all data is sent and received on the connected socket before it is closed.
这似乎意味着如果我使用Shutdown(SocketShutdown.Both)
,任何尚未接收到的数据可能仍会被消耗。要对此进行测试:
- 我不断向客户端发送数据(通过
Send
在单独的线程中)。 - 客户端执行了
Shutdown(SocketShutdown.Both)
. - 服务器上的
BeginReceive
回调执行,但是,EndReceive
抛出异常:现有连接被远程主机强行关闭。这意味着我无法接收0
return 值,因此无法调用Shutdown
.
根据要求,我在下面发布了服务器端代码(它包装在 Windows 表单中,只是作为实验创建的)。在我的测试场景中,我没有像往常一样在不发送连续数据的情况下看到 CLOSE_WAIT
state in TCPView 。所以可能我做错了什么,我错误地打断了后果。在另一个实验中:
- 客户端连接到服务器。
- 客户端执行
Shutdown(SocketShutdown.Both)
. - 服务器收到关闭确认并发送一些数据作为响应。服务器还执行
Shutdown
. - 客户端从服务器接收数据,但不允许下一个
BeginReceive
:发送或接收数据的请求被禁止,因为套接字已经在那个方向上被关闭了关机呼叫
在这种情况下,我仍然期待从 EndReceive
到 Close
套接字的 0
return 值。这是否意味着我应该改用 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();
}
Shutdown(SocketShutdown.Both)
当您不想接收或发送时应该使用。您要么想突然关闭连接,要么您知道对方已使用 SocketShutdown.Receive
关闭。例如,您有一个时间服务器将当前时间发送到连接它的客户端,服务器发送时间并调用 Shutdown(SocketShutdown.Received)
因为它不再期望来自客户端的任何更多数据。客户端在收到时间数据后应调用 Shutdown(SocketShutdown.Both),因为它不会发送或接收任何进一步的数据。
Shutdown(SocketShutdown.Both) 禁用当前套接字上的发送和接收操作。调用 Shutdown(SocketShutdown.Both) 是您的客户端与服务器的实际 断开连接。您可以通过检查服务器端 SocketState 对象中的套接字 Connected 属性 来查看:它将为 false。
发生这种情况是因为关闭操作是不可逆的,所以在停止套接字上的发送和接收之后,保持连接是没有意义的,因为它是孤立的。
“一旦调用关闭函数来禁用发送和/或接收,就没有方法可以为现有的套接字连接重新启用发送或接收。” (https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown)
关于你的问题:
- 我不断地向客户端发送数据(通过在单独的线程中发送)。
- 客户端执行了关机(SocketShutdown.Both)。 --> 这将断开客户端连接
- 服务器上的 BeginReceive 回调执行,但是,EndReceive 抛出一个 异常:现有连接被远程主机强行关闭。这意味着 我无法收到 0 return 值并调用 Shutdown。
EndReceive 抛出异常,因为客户端套接字不再连接。
要优雅地终止套接字:
客户端套接字调用 Shutdown(SocketShutdown.Send)) 但应该继续接收
在服务器上,EndReceive returns 读取了 0 个字节(客户端发出信号表明其端没有更多数据)
服务器 A)发送它的最后一个数据 B) 调用 Shutdown(SocketShutdown.Send)) C) 在套接字上调用 Close,可选择超时以允许从客户端读取数据
客户 A) 从服务器读取剩余数据,然后接收到 0 字节(服务器发出信号,表明其端没有更多数据) B) 在套接字上调用 Close