在 Indy TIdTCPServer 和 TIdTCPClient 中管理异常

Posted

技术标签:

【中文标题】在 Indy TIdTCPServer 和 TIdTCPClient 中管理异常【英文标题】:Managing Exceptions in Indy TIdTCPServer and TIdTCPClient 【发布时间】:2022-01-23 17:27:27 【问题描述】:

我在本论坛的其他帖子和其他地方读到,在 Indy 组件中,异常(来自EIdException)是内部管理的。 因此,在 TCPServer Execute 方法中,不需要使用 try - catch 块。 但是如果在 TCPServer Execute 方法中出现另一种异常会怎样呢?

在我的服务器应用程序中,我在主窗体上放置了一个TIdTCPServer 组件,如果在它的Execute 方法中发生了一些不好的事情并且我不管理它,服务器就会停止。 显然我需要服务器运行,所以我使用try - catch 块,如果有异常(任何类型的异常),我重新启动服务器。 我不知道这是否是最好的做法。

有时,在 IDE 中运行服务器应用程序时,我得到了错误

Project CallMonitor.exe raised exception class EIdSocketError with message 'Socket Error # 10054 - Connection reset by peer.'.

可能是由在同一台计算机上运行的客户端(而不是在 IDE 中)引起的。 应用程序执行被阻止,如果我中断,代码会在 IdStack 文件中停止,那里有大的 READ ME!!!关于异常 10038。 我按 F9,应用程序继续运行。

对我来说,这不是很清楚会发生什么。我的try - catch 块在这种情况下有效吗?还是无用或有害? 我必须过滤 Indy 异常和其他异常?

在客户端,在我的应用程序中,主窗体中有一个 TIdTCPClient 对象,与服务器的连接在主线程中进行管理。然后有另一个线程来管理与服务器的通信。在通信线程Execute 方法中,我有一个try - catch 块和一个循环,在该循环中,我每2 秒或当用户请求数据并解码服务器答案时向服务器发送请求。 如果有EIdReadTimeout 异常,我会终止线程并重新启动它。对于其他异常,我终止线程,断开/连接 TIdTCPClient 并重新启动线程。

我认为这是一种不同的情况,因为在服务器中,异常是在 TIdTCPServer 线程中管理的,而在客户端中,异常是在仅使用 TCPClient->Socket 与服务器通信的线程中管理的,对吗?

感谢任何答案/评论/建议。

【问题讨论】:

【参考方案1】:

在 TCPServer Execute 方法中,不需要使用 try - catch 块。但是如果在 TCPServer Execute 方法中出现另一种异常会怎样呢?

ANY 未被捕获的异常被允许从OnConnectOnExecute 事件逃回TIdTCPServer 将导致调用TIdContext 线程停止运行。它将在清理期间关闭其关联的客户端Connection,触发OnDisconnect 事件。然后OnException 事件将在之后被触发。

在我的服务器应用程序中,我在主窗体上放置了一个TIdTCPServer 组件,如果在它的Execute 方法中发生了不好的事情并且我不管理它,服务器就会停止。

整个服务器不会停止。仅停止调用TIdContext 线程。

显然我需要服务器运行,所以我使用try - catch 块,如果出现异常(任何类型的异常),我会重新启动服务器。

没有必要在任何异常情况下重新启动整个服务器。仅适用于使您的应用正常运行所需的内容无效/损坏的异常。

有时,在 IDE 中运行服务器应用程序时,我得到了错误

Project CallMonitor.exe raised exception class EIdSocketError with message 'Socket Error # 10054 - Connection reset by peer.'.

这是一个完全正常的套接字错误。这不会杀死你的整个服务器。

如果您进入 IDE 的调试器设置,则可以选择忽略 Indy “静默”异常(源自 EIdSilentException,例如 EIdConnClosedGracefully)。或者,您也可以告诉调试器忽略特定的异常类型,例如EIdSocketError

应用程序执行被阻止...我按 F9 并且应用程序继续运行。

没错。不是致命错误。阻塞仅在调试器中,它让您检查异常并决定如何处理它。

对我来说,这不是很清楚会发生什么。我的try - catch 屏蔽在这种情况下有效吗?

IDE 调试器会在您的代码之前捕获异常。当你按下 F9 继续执行时,异常会被传回你的代码,合适的catch会正常处理。

或者它是无用的还是有害的?

没有。

我必须过滤 Indy 异常和其他异常?

如果您完全捕获异常,处理您需要的异常,然后您应该重新抛出您捕获的任何 Indy 特定异常(所有 Indy 异常都是派生的)来自EIdException 以便于识别),让服务器处理它们。如果您不这样做,那么您应该自己断开调用Connection 并优雅地退出事件处理程序。无论哪种方式,服务器都会根据需要清理其余部分。

在客户端,在我的应用程序中,主窗体中有一个TIdTCPClient 对象,与服务器的连接在主线程中进行管理。然后有另一个线程来管理与服务器的通信。

我也会将连接管理移到工作线程中。如果需要,连接、通信、断开连接、重复,都应该在一个线程中。

在通信线程 Execute 方法中,我有一个 try - catch 块和一个循环,在该循环中,我每 2 秒或当用户请求数据并解码服务器答案时向服务器发送请求。如果出现EIdReadTimeout 异常,我会终止线程并重新启动它。

如果实际读取操作超时,则通信状态未知且可能无法恢复,因为您不知道哪个字节是超时的。因此,您应该断开连接并重新连接,而不仅仅是重新启动线程。唯一不需要完全重新连接的情况是,如果您正在处理消息之间的超时等待,并且知道通信没有损坏。在这种情况下,简单地重新启动线程而不完全重新连接不太可能解决超时条件。重试等待操作会更简单。

对于其他异常,我终止线程,断开/连接TIdTCPClient 并重新启动线程。

如果将连接/断开逻辑移到线程中,它们可以在循环中完成,则无需重新启动线程本身。创建/销毁线程的成本很高(从操作系统的角度来看),因此请尽可能重用线程。

我认为这是一种不同的情况,因为在服务器中,异常是在 TIdTCPServer 线程中管理的,而在客户端中,异常是在仅使用 TCPClient->Socket 与服务器通信的线程中管理的,是吗?对吧?

是的。

【讨论】:

感谢 Remy 的回答和建议,总是非常有用。我会努力改进我的代码。

以上是关于在 Indy TIdTCPServer 和 TIdTCPClient 中管理异常的主要内容,如果未能解决你的问题,请参考以下文章

Delphi TIdTCPServer组件

更新后Indy不再工作

Delphi中Indy 10的安装和老版本的卸载

Delphi中Indy 10的安装和老版本的卸载

Hyperledger Indy笔记和翻译

使用 indy 10.5.8 在 Delphi 2010 中设置 KeepAlive 超时