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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM