zoukankan      html  css  js  c++  java
  • C# Socket keeplive 心跳检测实例

    版权声明:本文为CSDN博主「b哈利路亚d」的原创文章,重新编辑发布,请尊重原作者的劳动成果,转载的时候附上原文链接:https://blog.csdn.net/lanwilliam/article/details/51698807

    Socket本身无法很好的捕获连接断开事件,或者说根本没这功能。总不能每次发生数据通讯时,通过异常来判断吧。

    所以经过了各种测试及查询(这里还是要感谢国外的友人们,鄙视一下国人),总算找到一种相对稳定的方法。

    该方法利用了tcp/ip协议本省的keep-alive规则。

    keep-alive简单来说,就是tcp协议中制定的心跳检测,用来判断连接是否存活。默认是不启动的,需要进行设置。

    serverFullAddr = new IPEndPoint(IPAddress.Any, portNo);//设置IP,端口
    server = new TcpListener(serverFullAddr);
    server.Start();
    // 启用keep-alive
    server.Server.IOControl(IOControlCode.KeepAliveValues, GetKeepAliveData(), null);
    server.Server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

    上面是server端演示代码,用的是tcplistener,毕竟比较方便。而且用来和DTU通讯的时候,使用的NetworkStream,这个相对好用

    client = server.AcceptTcpClient();
    // 启用keep-alive
    client.Client.IOControl(IOControlCode.KeepAliveValues, GetKeepAliveData(), null);
    client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

    上面是客户端启用的设置。

    按照查询到的理论,应该任意一方面设置了就可以,不过我这里都启用了,姑且算保险吧。

    然后就是IOControl设置的数据了。

    private byte[] GetKeepAliveData()
    {
    uint dummy = 0;
    byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
    BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
    BitConverter.GetBytes((uint)3000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//keep-alive间隔
    BitConverter.GetBytes((uint)500).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);// 尝试间隔
    return inOptionValues;
    }

    keep-alive如果使用windows默认,可能2个小时才会发送一次心跳,我这里检测设备在线,肯定不能这么长时间,我的读数频率都是以分钟作为单位的,出于各方面考虑
    我这里设置的keep-alive每3秒发送一次。如果对方没有响应,每0.5秒后发送一次确认,如果连续3次没有回应,连接会自动变成TcpState.Established。

    这里说一下,查询过程中发现很多人使用socket去poll来进行判断,在测试中,发现不好用,响应不及时,后来多方查找资料并测试,发现通过系统本身的连接来进行判断比较准确,方法如下:

    /// <summary>
    /// THIS FUNCTION WILL CHECK IF CLIENT IS STILL CONNECTED WITH SERVER.
    /// </summary>
    /// <returns>FALSE IF NOT CONNECTED ELSE TRUE</returns>
    public bool isClientConnected(TcpClient ClientSocket)
    {
    IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();

    TcpConnectionInformation[] tcpConnections = ipProperties.GetActiveTcpConnections();

    foreach (TcpConnectionInformation c in tcpConnections)
    {
    TcpState stateOfConnection = c.State;

    if (c.LocalEndPoint.Equals(ClientSocket.Client.LocalEndPoint) && c.RemoteEndPoint.Equals(ClientSocket.Client.RemoteEndPoint))
    {
    if (stateOfConnection == TcpState.Established)
    {
    return true;
    }
    else
    {
    return false;
    }

    }

    }

    return false;
    }

    这样解决办法就简单了。

    单独写一个CheckAlive的线程进行检测,然后抛出事件并移除连接就ok。

    public void StartCheckAlive()
    {
    Thread th = new Thread(new ThreadStart(CheckAlive));
    th.IsBackground = true;
    th.Start();
    TCPLogger.Log("CheckAlive线程已启动");
    }

    private void CheckAlive()
    {
    Thread.Sleep(10000);
    while(isListen)
    {
    try
    {
    lock (ClientList)
    {
    foreach (ClientItem item in ClientList)
    {
    //if (item.Client.Client.Poll(500, System.Net.Sockets.SelectMode.SelectRead) && (item.Client.Client.Available == 0))

    if (!isClientConnected(item.Client))
    {
    removeQueue.Enqueue(item);
    continue;
    }
    }
    while (removeQueue.Count > 0)
    {
    ClientItem item = removeQueue.Dequeue();
    clientList.Remove(item);
    try
    {
    TCPLogger.Log("关闭客户端连接");
    item.Client.Close();


    }
    catch (Exception ex)
    {
    TCPLogger.Log("关闭客户端连接", ex);
    }
    TCPLogger.Log("CheckAlive移除链接:" + item.RegCode);
    if (OnClientRemoved != null)
    OnClientRemoved(item.RegCode);
    }
    }

    }catch(Exception e)
    {
    TCPLogger.Log("CheckAlive异常.", e);
    }

    Thread.Sleep(500);
    }
    }

  • 相关阅读:
    $digest / $apply digest in progress报错
    get与post请求
    面试题(北京)
    Docker监控平台prometheus和grafana,监控redis,mysql,docker,服务器信息
    Zabbix系列优秀博文
    Docker安装Zabbix
    使用Docker部署监控系统,Prometheus,Grafana,监控服务器信息及Mysql
    docker镜像加速,docker更换为国内镜像
    记一次,Docker镜像1G多精简至300+M的过程
    docker通过dockerfile构建JDK最小镜像,Docker导出导入镜像
  • 原文地址:https://www.cnblogs.com/chenhuadan/p/11615607.html
Copyright © 2011-2022 走看看