支持者
*需要自定义 Indy 版本。
Windows 下的 IOCP 是一种 API,允许使用有限的线程池处理数千个连接,而不是像 Indy 默认那样每个连接使用一个线程。
要为 Indy 服务器启用 IOCP,请转到 IOHandlerOptions 属性并选择 iohIOCP 作为 IOHandler 类型。
Server.IOHandlerOptions.IOHandlerType := iohIOCP;
Server.IOHandlerOptions.IOCP.IOCPThreads := 0;
Server.IOHandlerOptions.IOCP.WorkOpThreads := 0;
Server.IOHandlerOptions.IOCP.PartialRequestTimeout := 0;
Server.IOHandlerOptions.IOCP.TCPKeepAlive.Enabled := True;
Server.IOHandlerOptions.KeepAliveTimeout := 120;
IOCPThreads 是用于 IOCP 异步请求(重叠操作)的线程。默认值为零,表示线程数根据处理器数量计算(Delphi 7 和 2007 除外,因为这些版本不支持 cpucount 函数,线程数固定为 32)。
WorkOpThreads 用于设置为处理请求而保持就绪的工作线程的最小数量,默认值为 0。IOCP 线程只负责取走已完成的操作,并把实际工作交给这个线程池,因此绝不会被速度缓慢的客户端阻塞。当所有工作线程都已在忙碌时,该线程池会按需增长,直至达到固定的上限,因此无需事先规划它的大小。只有当您希望在第一个请求到达之前线程池就已准备就绪时,才需要调大这个值,这样可以避免在连接集中涌入的初期支付线程创建的开销。
2026.9.0 中的变更。 以前 WorkOpThreads 会把每个连接固定到其中一个线程上,因此同一连接上的所有请求都在同一线程中运行,而 WorkOpRoundRobin 则决定新连接如何分配到这些线程上。这种固定绑定已被移除,因为一个正在等待请求剩余部分而停住的连接会阻塞分配到同一线程上的其他所有连接,从而导致服务器停滞。现在同一连接上的请求可以在不同线程中运行,因此不能再假定按线程保存的内容(例如数据库连接或线程本地缓存)会跟随某个连接。WorkOpRoundRobin 已不复存在。请将它从代码中删除。如果您曾在 Object Inspector 中启用过它,还需要从窗体的 .dfm 中删除 WorkOpRoundRobin 那一行,否则窗体将无法加载。
PartialRequestTimeout 用于让服务器等待接收长度已知的 HTTP 请求正文的剩余部分到达,单位为毫秒,默认值为 0,表示禁用此功能。如果未启用此选项,当请求正文未能在单次完成操作内全部到达时(例如一个大的 POST 请求被拆分为多个数据包发送),该请求会被静默丢弃,连接也会在不作任何响应的情况下关闭。这个等待过程不会运行在需要服务大量连接、绝不能被阻塞的 IOCP 线程上,而是运行在上文所述的工作线程池中,该线程池会按需增长,直至达到固定的上限,因此速度缓慢或无响应的客户端不会耗尽该线程池,也不会因此拖累其他连接。
TCPKeepAlive 允许操作系统探测空闲连接,检测已悄然消失的对端(默认禁用)。当 TCPKeepAlive.Enabled 设置为 True 时,操作系统会在连接空闲达到 TCPKeepAlive.Time 秒(默认 60 秒)后开始探测该连接,此后每隔 TCPKeepAlive.Interval 秒(默认 10 秒)重复探测一次,直到对端响应或连接被断开为止。
KeepAliveTimeout 会关闭空闲时间超过指定秒数的任何连接(默认值为 0,表示禁用)。此超时仅影响空闲连接,正在读取的请求或正在写入的响应不会被此超时中断。这与默认的每连接一线程 IOHandler 释放静默连接的方式最为接近,并且不依赖操作系统,也不需要对端的任何配合。
当需要处理数千个连接时,建议为 Windows 服务器启用 IOCP。如果您的服务器最多只需处理 100 个并发连接,则可以沿用默认的 Indy 线程模型。
OnDisconnect 事件未触发
IOCP 的工作方式与默认的 Indy IOHandler 不同。对于默认的 Indy IOHandler,每个连接都在一个线程中运行,这些线程一直在运行,检查连接是否处于活跃状态,因此如果断开连接,会在短时间内得到通知。
IOCP 工作方式不同,它使用线程池处理所有连接,而非像 Indy 默认的每连接一个线程。在 IOCP 中,检测连接是否存活的唯一方式是尝试向套接字写入数据。若发生任何错误,则表示连接已关闭。有 3 种检测断连的方式:
1. 如果使用 TsgcWebSocketClient,可以在 Options 属性中启用它,CleanDisconnect := True(默认禁用)。启用后,客户端断开连接之前会发送一条通知服务器断开的消息,服务器将收到此消息并触发 OnDisconnect 事件。
2. 您可以在服务器端启用心跳,例如每 60 秒向所有已连接的客户端发送一次 ping,若有客户端断开,则调用 OnDisconnect。
3. 您可以在服务器的 IOHandlerOptions 上启用 TCPKeepAlive 和/或设置 KeepAliveTimeout。这样服务器就能自行检测到已静默的对端(例如客户端崩溃,或网络连接中断而未发送关闭消息),并关闭该连接,而无需依赖客户端发送任何数据。