Indy TCPClient OnDisconnect 事件不起作用

发布于 2024-11-30 05:36:40 字数 747 浏览 0 评论 0原文

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 gets called
end;

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

OnDC 永远不会被处理。为什么?

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 gets called
end;

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

The OnDC never gets handled. Why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

给妤﹃绝世温柔 2024-12-07 05:36:40

Indy 客户端组件不是事件驱动的(有几个例外,例如 TIdTelnet)。当服务器断开连接时,不会触发 TIdTCPClient.OnDisconnect 事件,就像您假设的那样。这是设计使然。 TIdTCPClient 在尝试再次访问套接字之前不会知道断开连接,此时它会引发异常,例如 EIdConnClosedGraceously。仅当在客户端调用 TIdTCPClient.Disconnect() 方法时才会触发 TIdTCPClient.OnDisconnect 事件,而您并未执行此操作。

为了检测服务器端与 TIdTCPClient 的断开连接,您必须定期从套接字读取数据,例如在计时器或单独的线程中。

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.

醉酒的小男人 2024-12-07 05:36:40

好吧,我的错。你的代码不起作用,但这是正确的。让我解释一下原因:

AContext.Connection.Disconnect(true) 方法调用 TCPServer 中未实现的 DisconnectNotifyPeer 。为什么?因为不应该。

当您在服务器中断开连接时,indy 所做的就是使套接字无效并关闭它。客户端只有在尝试发送某些请求时才会注意到服务器已断开连接。而你的代码并没有这样做。这是 indy 的默认行为。

为了通知客户端服务器已断开连接,indy 和任何其他套件应该实现我们所说的heartbeat。心跳是一种时不时地尝试向套接字发送小数据包以检测它是否仍然存在的技术。检测套接字断开连接的唯一方法是尝试在该套接字中写入内容。谷歌一下heartbeat,你就会明白我的意思。

编辑

检查

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.

不再让梦枯萎 2024-12-07 05:36:40

您可以通过向客户端添加计时器例程来使客户端轮询断开连接 - 这是最简单的方法。

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;                    

这应该使代码的行为就像旧的 TClientSocket 一样(并且像 TidTelnet 一样)。如果服务器突然消失(即一旦触发定时器例程检测到这种情况),它会为 OnStatus 事件生成一个 hsDisconnected 标志。但是,这种导致断开连接的服务器丢失的特殊情况不会触发 OnDisconnect 事件 - 只是触发 OnStatus。因此,最好始终使用 OnStatus 来捕获所有断开连接,无论是客户端还是服务器引起的。我使用了一个设置为 100 毫秒的计时器,但我想你可以根据需要将其设置为频繁或缓慢 - 它似乎不会造成任何损害。

注意:对于 DELPHI 7(可能还有 D7 和 XE4 之间的其他版本),您必须以稍微不同的方式执行此操作:

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; 

顺便说一句 - 如果您使用 Delphi 6,请忘记 Indy,它当时的 bug 太多了。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文