• delphi idtcpclient和idtcpserver的心跳包


    delphi idtcpclient和idtcpserver的心跳包


    最近有个项目需要用到socket通信,对于socket的网络异常处理(程序异常退出或者网络掉了)及重连纠结了好久,网上虽然有很多资料,但是都是从一个地方转过来的,不够详细,查了很久的资料才弄出来的,原来的出处给忘了。

    环境:delphi7+indy控件(dephi7自带)     工作需要才用delphi7,建议使用delphi2007及以上版本,delphi2007里面带的indy控件版本是10.0,修复了很多以前的bug

    使用心跳包的理由:(ps:以下理由是抄的,感觉意思和我当初的初衷是一样的)
    有开发网络应用经历的人都知道,网络中的接收和发送数据都是使用WINDOWS中的SOCKET进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?
      有人一定想到使用Send函数中的返回结果来进行判断。如果返回的长度和自己发送出去的长度一致,那就说明这个套接字是可用的,否则此套接字一定出现了问题。但是我们并不是无时无刻的发送数据呀。如何解决呢?
      其实TCP中已经为我们实现了一个叫做心跳的机制。如果你设置了心跳,那TCP就会在一定的时间(比如你设置的是3秒钟)内发送你设置的次数的心跳(比如说2次),并且此信息不会影响你自己定义的协议。
     
    原理网上都有,就是使用setsockopt函数和WSAIoctl函数,网上光介绍了怎么使用心跳机制,没有说处理办法。设置好心跳后,TCP就会在一定的时间(比如你设置的是3秒钟)内发送你设置的次数的心跳(比如说2次),如果网络异常(程序异常退出或者网络掉了),心跳就会抛出一个错误异常,我们只要知道在哪儿捕获异常,就能知道网络情况以及何时进行重连了。
     
    服务端的心跳处理(idtcpserver)
      //定义心跳常量
    Const
      IOC_IN = $80000000;
      IOC_VENDOR = $18000000;
      IOC_out = $40000000;
      SIO_KEEPALIVE_VALS = IOC_IN or IOC_VENDOR or 4;
      DATA_BUFSIZE = 8192;
     
    //定义 KeepAlive 数据结构
    Type
      TTCP_KEEPALIVE = packed record
        onoff: integer;
        keepalivetime: integer;
        keepaliveinterval: integer;
      end;
    在idtcpserver的OnConnect事件里面加入以下代码:
    var
      opt:Integer;
      klive, outKlive: TTCP_KEEPALIVE;
    begin
      opt := 1;
        if setsockopt(AThread.Connection.Socket.Binding.Handle,SOL_SOCKET, SO_KEEPALIVE, @opt, SizeOf(opt)) <> 0 then
        begin
          Showmessage('setsockopt KeepAlive Error!');
        end;
        klive.onoff := 1;
        klive.keepalivetime := 3000;
        klive.keepaliveinterval := 1;
        if WSAIoctl(AThread.Connection.Socket.Binding.Handle, SIO_KEEPALIVE_VALS, @klive,
                 SizeOf(TTCP_KEEPALIVE), @outKlive,
                 SizeOf(TTCP_KEEPALIVE), @opt,0,nil) = SOCKET_ERROR then
        begin
          Showmessage('WSAIoctl KeepAlive Error!');
        end
    end
    在idtcpserver的OnException事件里面就能捕获到这个异常,并进行异常处理了
    procedure Tfrmmain.IdTCPServer1Exception(AThread: TIdPeerThread;
      AException: Exception);
    begin
      suiMemo1.Lines.Add('客户端'+inttostr(athread.handle)+'异常断开');
      if AThread.Connection.Connected then AThread.Connection.Disconnect;
    end;
     
     
    客户端的心跳处理(idtcpclient)
    procedure Tfrmmain.IdTCPClient1Connected(Sender: TObject);
    var
        opt:Integer;
      klive, outKlive: TTCP_KEEPALIVE;
    begin
        opt := 1;
        if setsockopt(IdTCPClient1.Socket.Binding.Handle,SOL_SOCKET, SO_KEEPALIVE, @opt, SizeOf(opt)) <> 0 then
        begin
          Showmessage('setsockopt KeepAlive Error!');
        end;
        klive.onoff := 1;
        klive.keepalivetime := 3000;
        klive.keepaliveinterval := 1;
        if WSAIoctl(IdTCPClient1.Socket.Binding.Handle, SIO_KEEPALIVE_VALS, @klive,
                 SizeOf(TTCP_KEEPALIVE), @outKlive,
                 SizeOf(TTCP_KEEPALIVE), @opt,0,nil) = SOCKET_ERROR then
        begin
          Showmessage('WSAIoctl KeepAlive Error!');
        end;
    end;
     
    客户端的异常捕捉,我是放在客户端读取服务端发送过来数据的线程里面
    procedure TReadThread.Execute;
    var
      TestBuffer:TSendDataNet;
    begin
      { Place thread code here }
      frmmain.log('Client Begin reading...');
      while permit do
      begin
        try
          frmmain.IdTCPClient1.ReadBuffer(TestBuffer,SizeOf(TestBuffer));
          sleep(100);
        except
          on e:Exception do
          begin
            permit:=False;
            if frmmain.IdTCPClient1.Connected then frmmain.IdTCPClient1.Disconnect;
            frmmain.RzStatusPane1.Caption:='断开;
            frmmain.LinkTimer.Enabled:=True;//这儿启动定时器重连服务端
          end;
        end;
      end;
      ThreadDestroy;
    end;

  • 相关阅读:
    HDU 4644 BWT (KMP)
    常数的值类型问题
    HDU 1395 2^x mod n = 1 (欧拉函数)
    HDU 5384 Danganronpa(AC自动机)
    9.自己实现linux中的tree
    8.底层文件库
    7.标准文件库
    7.gcc的使用
    5.文件I/O
    4.vim操作
  • 原文地址:https://www.cnblogs.com/moonwind/p/4434608.html
Copyright © 2020-2023  润新知