Poo*_*ven 8 .net c# sockets berkeley-sockets
我相信关机顺序如下(如描述这里):
在MSDN文档(备注部分)如下:
使用面向连接时
Socket
,请务必Shutdown
在关闭之前调用方法Socket
.这可确保在连接的套接字关闭之前发送和接收所有数据.
这似乎暗示如果我使用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();
}
Run Code Online (Sandbox Code Playgroud)
小智 2
Shutdown(SocketShutdown.Both) 禁用当前套接字上的发送和接收操作。调用 Shutdown(SocketShutdown.Both) 是客户端与服务器的实际断开连接。您可以通过检查服务器端 SocketState 对象中的套接字 Connected 属性来看到这一点:它将为 false。
发生这种情况是因为关闭操作是不可逆的,因此在停止套接字上的发送和接收后,没有必要保持连接,因为它是隔离的。
“一旦调用关闭函数来禁用发送、接收或两者,就没有方法可以重新启用现有套接字连接的发送或接收。” (https://learn.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