開發環境:Delphi7
測試環境:WinXP,Win7 32bit,Win7 64bit
使用TClientSocket和TServerSocket實現TCP長連接通訊,經常因為斷電斷網等原因導致出現一些死連接。
解決方法是客戶端和服務端都定時發送心跳包。
具體實現如下:
TClientSocket和TServerSocket均使用非阻塞模式
1、定義心跳常量和KeepAlive 數據結構
//定義心跳常量 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;
2、引用WinSock,WinSock2
3、寫一個方法來使用setsockopt,WSAIoctl設置心跳包參數
procedure TForm1.setKeepAlive(Socket: TCustomWinSocket); var opt:Integer; klive, outKlive: TTCP_KEEPALIVE; i,j:integer; OptVal: DWORD; begin opt := 1; //需引用WinSock.pas if setsockopt(Socket.SocketHandle,SOL_SOCKET, SO_KEEPALIVE, PAnsiChar(@opt), SizeOf(opt)) = SOCKET_ERROR then begin showInfo(Format('WinSock Error %d', [WSAGetLastError()])); end; klive.onoff := 1; klive.keepalivetime := 5000; klive.keepaliveinterval := 1; //需引用WinSock2.pas if WSAIoctl(Socket.SocketHandle, SIO_KEEPALIVE_VALS, PAnsiChar(@klive), SizeOf(TTCP_KEEPALIVE), PAnsiChar(@outKlive), SizeOf(TTCP_KEEPALIVE), @opt,0,nil) = SOCKET_ERROR then begin showInfo(Format('WinSock Error %d', [WSAGetLastError()])); end; end;
4、在TClientSocket和TServerSocket的連接事件中調用設置心跳
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin showInfo('ServerSocket1ClientConnect-' + Socket.RemoteAddress + ':' + IntToStr(Socket.RemotePort)); setKeepAlive(Socket); //設置socket心跳,以便清除死鏈接 end;
經測試,采用以上方法,可以檢測到把網線斷網情況
測試效果圖: