我认为关机顺序如下(如here所述):
MSDN documentation(备注部分)读取:
使用面向连接的
Socket
时,请在关闭Shutdown
之前调用Socket
方法。这可确保在连接的套接字关闭之前发送和接收所有数据。
这似乎暗示如果我使用Shutdown(SocketShutdown.Both)
,任何尚未收到的数据仍可能被使用。为了测试这个:
Send
在一个单独的线程中)。Shutdown(SocketShutdown.Both)
。BeginReceive
回调会执行,但EndReceive
会引发异常:远程主机强制关闭现有连接。这意味着我无法收到0
返回值,然后拨打Shutdown
。根据要求,我已经发布了下面的服务器端代码(它包装在Windows窗体中,它只是作为实验创建的)。在我的测试场景中,我没有像往常那样在CLOSE_WAIT
中看到TCPView状态而没有发送连续数据。因此,我可能做错了什么,并且我错误地打断了后果。在另一个实验中:
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();
}
答案 0 :(得分:2)
Shutdown(SocketShutdown.Both)禁用当前套接字上的发送和接收操作。调用Shutdown(SocketShutdown.Both)是客户端与服务器的实际断开连接。您可以通过检查服务器端SocketState对象中的socket Connected属性来查看此情况:它将为false。
发生这种情况是因为Shutdown操作是不可逆的,因此在套接字上停止发送和接收之后,没有任何必要保持它的连接状态,因为它是隔离的。
“一旦调用了关闭功能以禁用发送,接收或两者都禁用,就没有方法可以重新启用现有套接字连接的发送或接收。” (https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown)
关于您的问题:
EndReceive引发异常,因为客户端套接字不再连接。
要正常终止套接字,请执行以下操作:
客户端套接字调用Shutdown(SocketShutdown.Send)),但应继续接收
在服务器上,EndReceive返回读取的0个字节(客户端发出信号,表示从其端不再有数据)
服务器 A)发送其最后数据 B)调用Shutdown(SocketShutdown.Send)) C)在套接字上调用Close,并选择超时,以允许从客户端读取数据
客户 A)从服务器读取剩余的数据,然后接收0字节(服务器发出信号,表示不再有来自其端的数据) B)在套接字上调用Close
答案 1 :(得分:0)
Shutdown(SocketShutdown.Both)
在您不想接收或发送时应使用。您可能要突然关闭连接,或者您知道另一方已使用SocketShutdown.Receive
关闭了。例如,您有一个时间服务器,该服务器将当前时间发送到与其连接的客户端,该服务器发送时间并调用Shutdown(SocketShutdown.Received)
,因为它不希望客户端再有任何数据。客户端在接收到时间数据后应调用Shutdown(SocketShutdown.Both),因为它不会发送或接收任何其他数据。