套接字关闭:什么时候应该使用SocketShutdown.Both [英] Socket Shutdown: when should I use SocketShutdown.Both

查看:52
本文介绍了套接字关闭:什么时候应该使用SocketShutdown.Both的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为关机顺序如下(如

MSDN文档(备注"部分)为:

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

这似乎意味着如果我使用 Shutdown(SocketShutdown.Both),则仍可能会消耗尚未接收到的任何数据.要对此进行测试:

  • 我不断地将数据发送到客户端(通过单独线程中的 Send 发送).
  • 客户端执行了 Shutdown(SocketShutdown.Both).
  • 执行服务器上的 BeginReceive 回调,但是 EndReceive 引发异常:远程主机强行关闭了现有连接.这意味着我无法接收到 0 返回值,而又无法调用 Shutdown .

根据要求,我在下面张贴了服务器端代码(它包装在Windows窗体中,是作为实验而创建的).在我的测试方案中,我没有在 CLOSE_WAIT 状态://technet.microsoft.com/zh-cn/library/bb897437.aspx"rel =" nofollow noreferrer> TCPView ,通常不会发送连续数据.因此,可能我做错了什么,而我错误地打断了后果.在另一个实验中:

  • 客户端连接到服务器.
  • 客户端执行 Shutdown(SocketShutdown.Both).
  • 服务器收到关闭确认,并发送一些数据作为响应.服务器还执行 Shutdown .
  • 客户端从服务器接收数据,但不允许下一个 BeginReceive :不允许发送或接收数据的请求,因为套接字已在该方向上被关闭,而先前已关闭呼叫

在这种情况下,我仍然期望从 EndReceive 到套接字 Close 0 返回值.这是否意味着我应该改用 Shutdown(SocketShutdown.Send)?如果是这样,什么时候应该使用 Shutdown(SocketShutdown.Both)?

第一个实验的代码:

  private TcpListener SocketListener {get;放;}专用Socket ConnectedClient {放;}私人布尔服务器私有对象shutdownLock = new object();私有结构SocketState{公共套接字套接字;公共字节[]个字节;}私有void ProcessIncoming(IAsyncResult ar){var state =(SocketState)ar.AsyncState;//客户端执行Shutdown时在此处引发异常:var dataRead = state.socket.EndReceive(ar);如果(dataRead> 0){state.socket.BeginReceive(state.bytes,0,state.bytes.Length,SocketFlags.None,ProcessIncoming,state);}别的{锁(shutdownLock){serverShutdownRequested = true;state.socket.Shutdown(SocketShutdown.Both);state.socket.Close();state.socket.Dispose();}}}私人无效Spam(){int i = 0;一会儿(true){锁(shutdownLock){如果(!serverShutdownRequested){尝试{ConnectedClient.Send(Encoding.Default.GetBytes(i.ToString()));}赶上{休息;}++ i;}否则{休息;}}}}私人void Listen(){一会儿(true){ConnectedClient = SocketListener.AcceptSocket();var data = new SocketState();data.bytes =新的字节[1024];data.socket = ConnectedClient;ConnectedClient.BeginReceive(data.bytes,0,data.bytes.Length,SocketFlags.None,ProcessIncoming,data);serverShutdownRequested = false;new Thread(Spam).Start();}}公共ServerForm(){InitializeComponent();var hostEntry = Dns.GetHostEntry("localhost");var endPoint = new IPEndPoint(hostEntry.AddressList [0],11000);SocketListener =新的TcpListener(endPoint);SocketListener.Start();new Thread(Listen).Start();} 

解决方案

Shutdown(SocketShutdown.Both)禁用当前套接字上的发送和接收操作.调用Shutdown(SocketShutdown.Both)是客户端与服务器的实际断开连接.您可以通过在服务器端的SocketState对象中检查socket Connected属性来查看此情况:它将为false.

发生这种情况是因为Shutdown操作是不可逆的,因此在套接字上停止发送和接收之后,没有任何必要使其保持连接状态,因为它是隔离的.

一旦调用了关机功能以禁用发送,接收或同时禁用这两者,就没有方法为现有的套接字连接重新启用发送或接收."( https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown )

关于您的问题:

  • 我不断将数据发送到客户端(通过单独的线程发送).
  • 客户端执行Shutdown(SocketShutdown.Both).->这会断开与客户端的连接
  • 执行服务器上的BeginReceive回调,但是,EndReceive引发一个例外:远程主机强行关闭了现有连接.这意味着我无法收到0的返回值,因此无法调用Shutdown.

EndReceive引发异常,因为客户端套接字不再连接.

要正常终止套接字,请执行以下操作:

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

  2. 在服务器上,EndReceive返回读取的0字节(客户端发出信号,表示从其一侧不再有数据)

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

  4. 客户A)从服务器读取剩余的数据,然后接收0字节(服务器发出信号,表示从其端不再有数据)B)调用套接字上的关闭"

( https://docs.microsoft.com/it-it/windows/win32/winsock/graceful-shutdown-linger-options-and-socket-closure-2?redirectedfrom=MSDN )

I believe the shutdown sequence is as follows (as described here):

The MSDN documentation (remarks section) reads:

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.

This seems to imply that if I use Shutdown(SocketShutdown.Both), any data that has not yet been received, may still be consumed. To test this:

  • I continuously send data to the client (via Send in a separate thread).
  • The client executed Shutdown(SocketShutdown.Both).
  • The BeginReceive callback on the server executes, however, EndReceive throws an exception: An existing connection was forcibly closed by the remote host. This means that I am unable to receive the 0 return value and in turn call Shutdown.

As requested, I've posted the Server side code below (it's wrapped in a Windows Form and it was created just as an experiment). In my test scenario I did not see the CLOSE_WAIT state in TCPView as I normally did without sending the continuous data. So potentially I've done something wrong and I'm interrupting the consequences incorrectly. In another experiment:

  • Client connects to server.
  • Client executes Shutdown(SocketShutdown.Both).
  • Server receives shutdown acknowledgement and sends some data in response. Server also executes Shutdown.
  • Client receives data from server but the next BeginReceive is not allowed: A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call

In this scenario, I was still expecting a 0 return value from EndReceive to Close the socket. Does this mean that I should use Shutdown(SocketShutdown.Send) instead? If so, when should one use Shutdown(SocketShutdown.Both)?

Code from first experiment:

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) disables both the send and receive operations on the current socket. Calling Shutdown(SocketShutdown.Both) is an actual disconnection of your client from the server. You can see this by checking the socket Connected property in your SocketState object on the server side: it will be false.

This happens because the Shutdown operation is not reversible, so after stopping both send and receive on the socket, there's no point in keeping it connected as it is isolated.

"Once the shutdown function is called to disable send, receive, or both, there is no method to re-enable send or receive for the existing socket connection." (https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-shutdown)

As for your question:

  • I continuously send data to the client (via Send in a separate thread).
  • The client executed Shutdown(SocketShutdown.Both). --> this disconnects the client
  • The BeginReceive callback on the server executes, however, EndReceive throws an exception: An existing connection was forcibly closed by the remote host. This means that I am unable to receive the 0 return value and in turn call Shutdown.

EndReceive throws an exception because the client socket is not connected anymore.

To gracefully terminate the socket:

  1. the client socket calls Shutdown(SocketShutdown.Send)) but should keep receiving

  2. on the server, EndReceive returns 0 bytes read (the client signals there is no more data from its side)

  3. the server A) sends its last data B) calls Shutdown(SocketShutdown.Send)) C) calls Close on the socket, optionally with a timeout to allow the data to be read from the client

  4. the client A) reads the remaining data from the server and then receives 0 bytes (the server signals there is no more data from its side) B) calls Close on the socket

(https://docs.microsoft.com/it-it/windows/win32/winsock/graceful-shutdown-linger-options-and-socket-closure-2?redirectedfrom=MSDN)

这篇关于套接字关闭:什么时候应该使用SocketShutdown.Both的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆