【问题标题】:How to detect a disconnected socket C#?如何检测断开连接的套接字 C#?
【发布时间】:2011-04-15 17:51:07
【问题描述】:

我一直在使用 C# 编写套接字客户端程序,我想知道如何检测套接字的另一端何时“不正常”地断开自身连接,例如拔掉网线或硬重置。

我在下面有这些函数来访问套接字,根据 SO 问题 here 和这个 MSDN article,检查断开连接的套接字的最佳方法是发送长度为 0 的 1 字节消息。如果抛出异常并且 WSAEWOULDBLOCK 不是错误代码,则套接字断开连接。我已经尝试过了,但是在硬重置服务器连接后,客户端将调用Send(new byte[1], 0, 0, SocketFlags.None) 并成功返回,然后Receive() 命令立即返回 WSAEWOULDBLOCK 错误。

什么给了??

下面是我的代码。 _socket 设置为非阻塞模式:

private int nonBlockRecv(byte[] recvBytes, int offset, int size, SocketFlags sf)
{
    int bytesRecv = 0;

    while (true)
    {
        try
        {
            nonBlockSend(new byte[1], 0, 0, sf);
            bytesRecv = _socket.Receive(recvBytes, offset, size, sf);
            break;
        }
        catch (SocketException excp)
        {
            if (excp.ErrorCode != 10035) // WSAEWOULDBLOCK
                throw excp;
        }
    }

    return bytesRecv;
}

private int nonBlockSend(byte[] sendBytes, int offset, int size, SocketFlags sf)
{
    int bytesSent = 0;

    while (true)
    {
        try
        {
            _socket.Send(sendBytes, offset, size, sf);
            break;
        }
        catch (SocketException excp)
        {
            if (excp.ErrorCode != 10035) // WSAEWOULDBLOCK
                throw excp;
        }
    }

    return bytesSent;
}

编辑:这可能是有益的,但服务器是 Windows Mobile 设备。我在另一个线程中读到,不同的操作系统可能会在它们死亡时发送套接字关闭信号。也许 Windows Mobile 操作系统不这样做?

【问题讨论】:

  • 1 字节消息的长度怎么可能为零? :-)
  • 好问题哈哈。这就是 MSDN 文章所说的。但是,如果我确实将大小设置为 1,那么它就可以工作!我想这很明显,但是额外的位通过流发送到服务器端套接字。我希望 Receive() 抛出一个异常,而不是在套接字关闭时会阻塞一个异常,但情况似乎并非如此......

标签: c# sockets


【解决方案1】:

如果远程计算机正常断开会话,则 Socket.Receive() 方法将返回 0 个字节。你必须检测到 要知道远端已经断开:

int recv = sock.Receive(data);
if (recv == 0)
{
    // Remote client has disconnected.
}
else
{
    // Remote client has sent data.
}

此外,即使出现SocketException,您也可以识别套接字断开的异常。

希望这有助于解决您的问题。

【讨论】:

  • 但是当会话以非优雅的方式关闭时,例如在拔下网络电缆时呢? Receive() 调用不会抛出异常或返回,它只是继续阻塞。
  • 我已经编辑了我的帖子,但服务器端是 WM6 设备。当服务器关闭程序时,我会在客户端程序上收到关于套接字连接已关闭的通知,但在我硬重置设备时不会收到通知。
  • 看起来没有直接的方法可以告诉 WM6 套接字的接收端是否已取消连接。不过,您的方法在大多数其他情况下都有效,所以谢谢!
  • 在这种情况下,优雅是什么意思? IE。远程计算机调用serverSocket.disconnect()?
【解决方案2】:

我知道这已经晚了,但我想出了一个巧妙的解决方案。

我必须与第 3 方软件通信,该软件希望在发送的每个命令上都有回车,否则它会忽略它。

在主要阶段,我的客户端套接字处于循环接收来自第 3 方软件的响应中。我的解决方案并不理想,但基本前提是我在套接字上设置了接收超时,这样循环将尝试读取 5 秒,然后陷入捕获,然后再次循环。在每次接收之前,我都会调用我自己的 isconnected 方法,该方法执行一个不带回车的小写入,因此它会被第 3 方软件忽略,但如果网络中断,它会给我一个可靠的故障转移。如果写入失败,我所做的只是抛出一个 LostConnectionException 并在外部处理它。

如果你同时编写服务器和客户端,你可以很容易地想出一些其他人忽略的校验位。

这可能并不完美,但对我来说是可靠的。

while (numberOfBytesRead == 0)
{          
    try
    {
        IsConnected();
        _socket.ReceiveTimeout = 5000;
        numberOfBytesRead = _socket.Receive(myReadBuffer, 0, myReadBuffer.Length, SocketFlags.None);

    }
    catch (Exception e)
    {
        if (e.GetType() == typeof (LostConnection))
        {
            Status = SocketStatus.offline;
            throw;
        }
    }
}

isconnected 方法看起来像这样

public bool IsConnected(Socket s)
{
    try
    {
        ASCIIEncoding encoder = new ASCIIEncoding();
        byte[] buffer = encoder.GetBytes("test");
        s.Send(buffer, 0, buffer.Length, SocketFlags.None);
    }
    catch (Exception)
    {
        throw new LostConnection();
    }
    return s.Connected;
}

【讨论】:

    猜你喜欢
    • 2020-07-04
    • 2019-06-23
    • 1970-01-01
    • 2012-12-10
    • 2011-09-18
    • 1970-01-01
    • 2016-05-31
    相关资源
    最近更新 更多