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

How can I avoid freezing on Write to socket in Indy if the other end is not reading data from the Socket

我有一个使用 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;

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

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

我发现 this answer from Remy Lebeau to the question Delphi (Indy) Server Freezing on Write,他在评论中提到(强调我的):

Indy uses blocking sockets, so if the client is not reading inbound data on its end, eventually the socket's internal send buffer will fill up and the socket will become blocked on the server side waiting for the client to empty the buffer. The only way to avoid a deadlock in that scenario would be to set a socket-level send timeout using the socket API directly. Indy does not implement send timeouts in its logic.

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

如果那不可能,我可以在辅助线程上实现超时机制,但我不确定重置来自该辅助线程的连接的正确方法是什么我结束了让工作线程继续它的工作。

I'm looking for the correct way to set that timeout, via INDY or via a direct API call in Windows

在我的 previous comment, when I said "set a socket-level send timeout using the socket API directly", I was refering to the SO_SNDTIMEO socket option via the setsockopt() 函数中。

在Indy方面,可以通过TIdSocketHandle.SetSockOpt()方法调用setsockopt(),例如:

// Windows

SomeConnection.Socket.Binding.SetSockOpt(Id_SOL_SOCKET, Id_SO_SNDTIMEO, TimeoutInMS);

// '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));

TIdTCPConnection.Socket 属性 是一个 shorthand 用于访问 TIdIOHandlerSocketTIdTCPConnection.IOHandler 被分配了一个 TIdIOHandlerSocket 或后代) .

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