立即检测客户端与服务器套接字的断开连接

72 .net c# sockets connection tcp

如何检测客户端是否与服务器断开连接?

我有下面的代码在我的AcceptCallBack方法

static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
  //Accept incoming connection
  Socket listener = (Socket)ar.AsyncState;
  handler = listener.EndAccept(ar);
}
Run Code Online (Sandbox Code Playgroud)

我需要找到一种方法来尽快发现客户端已从handlerSocket 断开连接.

我试过了:

  1. handler.Available;
  2. handler.Send(new byte[1], 0, SocketFlags.None);
  3. handler.Receive(new byte[1], 0, SocketFlags.None);

当您连接到服务器并且想要检测服务器何时断开连接但是当您是服务器并且想要检测客户端断开连接时它们不起作用时,上述方法会起作用.

任何帮助将不胜感激.

Sam*_*uel 101

由于在断开套接字时没有可用于发出信号的事件,因此您必须以您可接受的频率轮询它.

使用此扩展方法,您可以使用可靠的方法来检测套接字是否已断开连接.

static class SocketExtensions
{
  public static bool IsConnected(this Socket socket)
  {
    try
    {
      return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
    }
    catch (SocketException) { return false; }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 除非连接的另一端实际关闭/关闭套接字,否则此方法不起作用.在超时期限之前,不会注意到未插入的网络/电源线.立即通知断开连接的唯一方法是使用心跳功能连续检查连接. (26认同)
  • @Smart Alec:实际上,您应该使用上面显示的示例.如果更改顺序,则存在潜在的竞争条件:如果`socket.Available`返回0并且您在`socket.Poll`被调用之前收到一个数据包,`Poll`将返回true并且方法将返回`false`,尽管套接字实际上仍然是健康的. (7认同)
  • 就像Matthew Finlay注意到的那样,这会报告错误的断开连接,因为Poll方法的结果与检查Available属性之间仍然存在竞争条件.一个数据包可能*几乎准备好*被读取但尚未被读取,因此可用为0 - 但是毫秒之后,有数据需要读取.更好的选择是尝试接收一个字节和SocketFlags.Peek标志.或者实现某种形式的心跳并将连接状态保持在更高的级别.或者依赖于发送/接收方法的错误处理(如果使用异步版本,则依赖于回调). (5认同)
  • 这种方法在99%的情况下运行良好,但有时会出现错误的断开. (4认同)

Maj*_*jak 14

有人提到TCP套接字的keepAlive功能.这里描述得很好:

http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

我正在使用它:在连接套接字后,我正在调用此函数,它将keepAlive设置为on.该keepAliveTime参数指定超时(以毫秒为单位),在发送第一个保持活动数据包之前没有活动.该keepAliveInterval参数指定在未收到确认的情况下发送连续保持活动数据包之间的间隔(以毫秒为单位).

    void SetKeepAlive(bool on, uint keepAliveTime , uint keepAliveInterval )
    {
        int size = Marshal.SizeOf(new uint());

        var inOptionValues = new byte[size * 3];

        BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size);
        BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2);

        socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }
Run Code Online (Sandbox Code Playgroud)

我也在使用同步阅读:

socket.BeginReceive(packet.dataBuffer, 0, 128,
                    SocketFlags.None, new AsyncCallback(OnDataReceived), packet);
Run Code Online (Sandbox Code Playgroud)

在回调中,这里被捕获超时SocketException,当套接字在保持活动数据包之后没有得到ACK信号时会引发超时.

public void OnDataReceived(IAsyncResult asyn)
{
    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;

        int iRx = socket.EndReceive(asyn);
    catch (SocketException ex)
    {
        SocketExceptionCaught(ex);
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,我就能安全地检测TCP客户端和服务器之间的断开连接.

  • 是的,将keepalive + interval设置为较低值,keepalive + 10*interval毫秒后任何轮询/发送都应该失败.自Vista以来,10次重试似乎是硬编码的?即使您拔掉电缆也可以使用,这与其他大多数答案不同.这应该是公认的答案. (4认同)

Eri*_*sch 13

这根本不可能.您和服务器之间没有物理连接(极少数情况下,您使用环回电缆连接两个编译器).

正常关闭连接后,将通知另一方.但是如果连接以其他方式断开(比如用户连接被丢弃),那么服务器将不会知道,直到它超时(或尝试写入连接并且确认超时).这就是TCP工作的方式,你必须忍受它.

因此,"即时"是不现实的.您可以做的最好的事情是在超时期限内,这取决于代码运行的平台.

编辑:如果您只是寻找优美的连接,那么为什么不从您的客户端向服务器发送"DISCONNECT"命令呢?


小智 6

"这就是TCP的工作方式,你必须忍受它."

是的,你是对的.这是生活中我已经意识到的事实.即使在使用该协议(甚至其他协议)的专业应用程序中,您也会看到相同的行为.我甚至看到它出现在网络游戏中; 你哥们说"再见",他似乎又在网上待了1-2分钟,直到服务员"清理房子".

您可以在此处使用建议的方法,或实施"心跳",如同建议的那样.我选择前者.但是,如果我确实选择了后者,我只需让服务器每隔一个字节"ping"一个客户端,看看我们是否有超时或没有响应.您甚至可以使用后台线程来实现精确计时.如果你真的担心它,甚至可以在某种选项列表(枚举标志或其他东西)中实现组合.但是,只要你更新,在更新服务器方面有一点延迟并不是什么大不了的事.这是互联网,没有人期望它是魔术!:)