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;
|
请发表评论