开发者

Indy TCPClient OnDisconnect event not working

type
  TForm8 = class(TForm)
    idtcpclnt1: TIdTCPClient;
    idtcpsrvr1: TIdTCPServer;
    procedure FormCreate(Sender: TObject);
    procedure idtcpsrvr1Execute(AContext: TIdContext);
    procedure idtcpclnt1Disconnected(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form8: TForm8;

implementation

{$R *.dfm}

procedure TForm8.FormCreate(Sender: TObject);
begin
  idtcpclnt1.Connect;
end;

procedure TForm8.idtcpsrvr1Execute(AContext: TIdContext);
begin
  AContext.Connection.Disconnect(true); //this 开发者_如何学Pythongets called
end;

procedure TForm8.idtcpclnt1Disconnected(Sender: TObject);
begin
  ShowMessage('true'); //but this does not
end;

The OnDC never gets handled. Why?


Indy client components are not event-driven (with a couple of exceptions, such as TIdTelnet). The TIdTCPClient.OnDisconnect event is NOT triggered when the server disconnnects on its end, like you are assuming. This is by design. TIdTCPClient will not know about the disconnection until it tries to access the socket again, at which time it will raise an exception, such as EIdConnClosedGracefully. The TIdTCPClient.OnDisconnect event is only triggered when the TIdTCPClient.Disconnect() method is called on the client side, which you are not doing.

In order to detect server-side disconnects with TIdTCPClient, you have to read from the socket periodically, such as in a timer or a separate thread.


Ok, my mistake. You code does not work, but that is correct. Let me explain why:

The AContext.Connection.Disconnect(true) method calls DisconnectNotifyPeer wich is not implemented in TCPServer. Why? Because it should not.

When you disconnect in the server, what indy does is invalidate the socket and close it. The client will only notice that the server disconnected when it tries to send some request. And you code does not do that. This is a default behavior of indy.

In order to notify the client that the server disconnected, indy and any other suites should implement what we call heartbeat. Heartbeat is a technique that from time to time tries to send small packets to an socket in order to detect that it is still alive. The only way of detect a socket disconnection is trying to write something in that socket. Google about heartbeat and you will understand what I mean.

EDIT

Check this out.


You can make the client POLL for disconnection by adding a timer routine to the client - this is the SIMPLEST way.

procedure TForm1.Timer1Timer(Sender: TObject);
   begin                    
      idTCPClient1.Connected;  // Works in Indy for Delphi XE4
   // Be aware this is a property read with side effects
   // It shouldn't get optimized out, but if it does, 
   // then add the appropriate directives to prevent that.
   end;                    

This should make the code behave just like the old TClientSocket used to (and like the TidTelnet does). It produces a hsDisconnected flag to the OnStatus event if the server suddenly disappears (i.e. as soon as the firing timer routine detects this). However, this special case of server-loss causing the disconnect does NOT fire the OnDisconnect event - just the OnStatus. So it is probably better to always use OnStatus to trap all disconnections, be they client or server induced. I used a timer set at 100ms, but I guess you can make it as frequent or slow as you want - it doesn't appear to do any harm.

NOTE: FOR DELPHI 7 (and possibly other versions between D7 and XE4), you will have to do this slightly differently:

procedure TForm1.Timer1Timer(Sender: TObject);
   begin                    
   // This no longer works this way in Indy for XE4, but works in Indy for D7 ... 
      idTCPClient1.CheckForGracefulDisconnect(FALSE);  
   end; 

By the way - if you're using Delphi 6, forget Indy, it was just way too buggy back then.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜