在 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
块。但是如果在 TCPServerExecute
方法中出现另一种异常会怎样呢?
ANY 未被捕获的异常被允许从OnConnect
或OnExecute
事件逃回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 中管理异常的主要内容,如果未能解决你的问题,请参考以下文章