zoukankan      html  css  js  c++  java
  • datasnap 2010 利用心跳包清除TCP死连接

    Delphi2010中DataSnap,如果客户端异常掉线或拔掉网线,那么在服务端会留下一个TCP连接,这个连接会变成死连接(经过测试,如果windows的TCP保持连接禁用的话,三个小时该死连接还不消失)。如果大量客户端并发,出现的死TCP连接过多,服务器内存和端口将会增加,直到占满服务器的端口和耗尽内存为止。如果这样的话,服务器无法健壮稳定的运行。

    大家可以另开线程来监控客户端连接,但是今天要给大家讲解的不是这个方法,而是使用TCP协议自带的心跳包功能解决这个问题。

    大家先了解一下 TCP keep-alive原理

    一个TCP keep-alive 包是一个简单的ACK,该ACK包内容为一个比当前连接sequence number 小于一的包。主机接受到这些ACKs会返

    回一个包含当前sequence number 的ACK包。
    Keep-alives一般被用来验证远端连接是否有效。如果该连接上没有其他数据被传输,或者更高level 的 keep-alives被传送,keep-alives 在每个KeepAliveTime被发送。(默认是 7,200,000 milliseconds ,也就是2个小时)。

    如果没有收到 keep-alive 应答,keep-alive 将在每 KeepAliveInterval 秒重发一次。KeepAliveInterval 默认为1秒。如 Microsoft 网络功能中很多部分中采用的 NETBT 连接,更常见的是发送 NETBios keep-alives,所以,在 NetBios 连接中通常不发送TCP keep-alives。
    TCP保持连接默认被禁用,但是微软Sockets应用程序可以使用SetSockOpt函数去启用他们。


    请看下面的类

    type
      TCP_KeepAlive = record
        OnOff: Cardinal;
        KeepAliveTime: Cardinal; // 多长时间(ms)没有数据就开始send心跳包 
        KeepAliveInterval: Cardinal // 每隔多长时间(ms)send一个心跳包,发5次(系统值)
    end;

    KeepAliveTime: TCP连接多长时间(毫秒)没有数据就开始发送心跳包,有数据传递的时候不发送心跳包
    KeepAliveInterval: 每隔多长时间(毫秒)发送一个心跳包,发5次(系统默认值)

    如果客户端网络中断,服务器系统发送心跳包后,服务器会自动解除TCP连接。这一点,大家可以使用 netstat -p -tcp 命令查看

    接下来我们将结合Delphi2010 DataSnap技术使用心跳包功能!

    为了能让我们的服务程序更加稳定,有些细节问题必须解决。就如上一讲中提到的客户端拔掉网线,造成服务器上TCP变成死连接,如果死连接数量过多,对服务器能长期稳定运行是一个巨大的威胁。

    另外,经过测试,如果服务器上有TCP死连接,那么服务程序连接数据库,也会产生那个一个死连接。这样的话,给数据库服务器也造成威胁。所以,服务器程序编写的好坏,直接影响系统的稳定性!

    如何解决TCP死连接的问题,有多种方法,其中最有效的就是心跳包技术。

    我们在DSServer的OnConnect事件中加入心跳包代码

    uses IdTCPConnection,IdWinsock2

    ........

    type
      TCP_KeepAlive = record
        OnOff: Cardinal;
        KeepAliveTime: Cardinal;
        KeepAliveInterval: Cardinal;
    end;

    ........

    procedure TServerContainer1.DSServer1Connect
      (DSConnectEventObject: TDSConnectEventObject);
    var
      Val: TCP_KeepAlive;
      Ret: DWord;
      ClientConnection: TIdTCPConnection;
    begin
      ClientConnection := TIdTCPConnection(DSConnectEventObject.ChannelInfo.Id);
      Val.OnOff := 1;
      Val.KeepAliveTime := 5000;
      Val.KeepAliveInterval := 3000;
      WSAIoctl(ClientConnection.Socket.Binding.Handle, IOC_IN or IOC_VENDOR or 4,
        @Val, SizeOf(Val), nil, 0, @Ret, nil, nil);
    end;

    观察上述代码,我们把心跳包放到服务端上执行,如果服务器的某个TCP连接在5秒钟没有收到数据,将会发送向对端发送心跳包,间隔3秒钟,连续发送5次(参数详解见上一讲高级技术4)。如果5次以后对端还没有应答,服务器将结束该TCP连接。TCP的连接可以使用 netstat -p tcp 命令查看。

    当该TCP结束后,delphi编写的服务程序会自动结束和数据库的连接。我用的是FireBird数据库,大家可以使用命令查看 SELECT MON$USER, MON$REMOTE_ADDRESS,
      MON$REMOTE_PID,
      MON$TIMESTAMP
     FROM MON$ATTACHMENTS

    现在服务器的tcp死连接和数据库的死连接都清除了,我们的系统将能长期稳定的运行。

  • 相关阅读:
    Docker容器启动时初始化Mysql数据库
    使用Buildpacks高效构建Docker镜像
    Mybatis 强大的结果集映射器resultMap
    Java 集合排序策略接口 Comparator
    Spring MVC 函数式编程进阶
    换一种方式编写 Spring MVC 接口
    【asp.net core 系列】6 实战之 一个项目的完整结构
    【asp.net core 系列】5 布局页和静态资源
    【asp.net core 系列】4. 更高更强的路由
    【Java Spring Cloud 实战之路】- 使用Nacos和网关中心的创建
  • 原文地址:https://www.cnblogs.com/hnxxcxg/p/2940926.html
Copyright © 2011-2022 走看看