如果另一端没有从套接字读取数据,如何避免在 Indy 中写入套接字时冻结

jac*_*ate 2 delphi indy winsock2

我有一个使用 Indy 的客户端和服务器套接字应用程序,用 Delphi 10.2 编译。

应用程序有一个工作线程,用于处理来自不同端口的请求,并使用对以下内容的调用在同一线程中写入响应:

procedure TMyCommManager.WriteResponse(AHandler: TIdIOHandler; SomeData: SomeType);
var 
  idBytes: TidBytes;
  PacketSize: Integer;
begin
  SomeData.GetBytes(idBytes, PacketSize);
  AHandler.Write(DataBuffer, PacketSize);
end;
Run Code Online (Sandbox Code Playgroud)

几乎所有时间一切都按预期工作,但我们注意到工作线程在生产中不时冻结。经过多次迭代,我们终于明白所有这些都发生在对 的调用中TidIOHandler.Write(),我很确定它正在发生,因为单个端口的另一端没有从套接字读取响应。

从另一端重置到端口后,工作线程解冻并按预期继续工作。

我从 Remy Lebeau找到了这个问题的答案Delphi (Indy) Server Freezing on Write,他在评论中提到(强调我的):

Indy 使用阻塞套接字,因此如果客户端没有在其端读取入站数据,最终套接字的内部发送缓冲区将填满,并且套接字将在服务器端阻塞,等待客户端清空缓冲区。在这种情况下避免死锁的唯一方法是直接使用套接字 API 设置套接字级发送超时。Indy 没有在其逻辑中实现发送超时。

我正在寻找设置超时的正确方法,通过 INDY 或通过 Windows 中的直接 API 调用,但我陷入了困境,所以我来这里寻求帮助。

如果这是不可能的,我可以在辅助线程上实现超时机制,但我不确定从我端的辅助线程重置连接以让工作线程继续工作的正确方法是什么。

Rem*_*eau 5

我正在寻找设置超时的正确方法,通过 INDY 或通过 Windows 中的直接 API 调用

在我之前的评论中,当我说“直接使用套接字 API 设置套接字级发送超时”时,我是SO_SNDTIMEO通过setsockopt()函数引用套接字选项。

在 Indy 方面,您可以setsockopt()使用该TIdSocketHandle.SetSockOpt()方法进行调用,例如:

// Windows

SomeConnection.Socket.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, TimeoutInMS);
Run Code Online (Sandbox Code Playgroud)

// 'Nix

var tv: timeval;
...
SomeConnection.Socket.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, Integer(@timeval));

or:

GBSDStack.SetSocketOption(SomeConnection.Socket.Binding.Handle, Id_SOL_SOCKET, Id_SO_SNDTIMEO, timeval, sizeof(timeval));
Run Code Online (Sandbox Code Playgroud)

(该TIdTCPConnection.Socket属性是在分配了 a或后代TIdIOHandlerSocket时访问的简写)。TIdTCPConnection.IOHandlerTIdIOHandlerSocket

只要知道如果超时确实发生,您就无法确切知道哪些数据实际传输到对等方,因此在大多数协议中通常无法恢复。您真正能做的只是关闭连接并重新连接。