C# 套接字关闭:我应该在什么时候使用SocketShutdown

C# 套接字关闭:我应该在什么时候使用SocketShutdown,c#,.net,sockets,berkeley-sockets,C#,.net,Sockets,Berkeley Sockets,我认为停机顺序如下(如上所述): (备注部分)内容如下: 使用面向连接的套接字时,在关闭套接字之前,请始终调用关闭方法。这样可以确保在关闭连接的套接字之前,所有数据都在该套接字上发送和接收 这似乎意味着,如果我使用Shutdown(SocketShutdown.Both),任何尚未收到的数据都可能会被消耗。要测试这一点: 我不断地向客户机发送数据(在单独的线程中通过send) 客户端执行了Shutdown(SocketShutdown.Both) 服务器上的BeginReceive回调执行,

我认为停机顺序如下(如上所述):

(备注部分)内容如下:

使用面向连接的
套接字
时,在关闭
套接字
之前,请始终调用
关闭
方法。这样可以确保在关闭连接的套接字之前,所有数据都在该套接字上发送和接收

这似乎意味着,如果我使用
Shutdown(SocketShutdown.Both)
,任何尚未收到的数据都可能会被消耗。要测试这一点:

  • 我不断地向客户机发送数据(在单独的线程中通过
    send
  • 客户端执行了
    Shutdown(SocketShutdown.Both)
  • 服务器上的
    BeginReceive
    回调执行,但是,
    EndReceive
    引发异常:远程主机强制关闭了现有连接。这意味着我无法接收
    0
    返回值并依次调用
    Shutdown
根据要求,我在下面发布了服务器端代码(它被包装在Windows窗体中,创建时只是一个实验)。在我的测试场景中,在没有发送连续数据的情况下,我没有看到中的状态。因此,我可能做了错事,并且错误地中断了后果。在另一个实验中:

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)
  • 服务器接收关机确认并发送一些数据作为响应。服务器还执行
    关闭
  • 客户端从服务器接收数据,但不允许下一个
    BeginReceive
    :不允许发送或接收数据的请求,因为套接字已在该方向上通过上次关闭调用关闭
在这个场景中,我仍然希望从
EndReceive
Close
套接字返回一个
0
值。这是否意味着我应该使用
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对象中的socket Connected属性看到这一点:它将为false

这是因为关闭操作是不可逆的,所以在停止套接字上的发送和接收之后,没有必要保持连接,因为它是隔离的

一旦调用shutdown函数来禁用send、receive或两者,就没有方法为现有套接字连接重新启用send或receive ()

关于你的问题:

  • 我不断地向客户端发送数据(通过单独的线程发送)
  • 客户端执行了关机(SocketShutdown.Both)-->这会断开客户端的连接
  • 服务器上的BeginReceive回调执行时,EndReceive抛出 异常:远程主机强制关闭了现有连接。这意味着 我无法接收0返回值,从而调用Shutdown
EndReceive引发异常,因为客户端套接字不再连接

要优雅地终止套接字,请执行以下操作:

  • 客户端套接字调用Shutdown(SocketShutdown.Send)),但应继续接收

  • 在服务器上,EndReceive返回读取的0字节(客户端表示没有来自其端的更多数据)

  • 服务器 A) 发送其最后的数据 B) 调用Shutdown(SocketShutdown.Send)) C) 在套接字上调用Close,可以选择超时以允许从客户端读取数据

  • 客户 A) 从服务器读取剩余数据,然后接收0字节(服务器发出信号,表示没有来自其端的更多数据) B) 在套接字上关闭调用


  • ()

    Send
    在关机序列期间也返回
    0
    。如果我更新服务器以考虑这一点,在
    Spam
    方法中设置我的标志之前,
    EndReceive
    仍会引发异常。我怀疑这一切都是因为
    CLOSE\u WAIT
    socket状态不知何故被跳过。您是否找到了答案?是的,但当用户在10秒后尝试重复该操作时,您会收到错误“发送或接收数据的请求失败,因为socket已关闭”我对C语言中的套接字实现非常失望#