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

Delphi 如果另一端没有从套接字读取数据,如何避免在Indy中写入套接字时冻结,delphi,indy,winsock2,Delphi,Indy,Winsock2,我有一个使用Indy的客户端和服务器套接字的应用程序,它是用Delphi10.2编译的 应用程序有一个工作线程,该线程处理来自不同端口的请求,并使用对以下内容的调用在同一线程中写入响应: procedure TMyCommManager.WriteResponse(AHandler: TIdIOHandler; SomeData: SomeType); var idBytes: TidBytes; PacketSize: Integer; begin SomeData.GetByt

我有一个使用Indy的客户端和服务器套接字的应用程序,它是用Delphi10.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()
的调用中,我很确定这是因为单个端口的另一端没有从套接字读取响应

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

我从Remy Lebeau到问题,他在评论中提到(我的重点):

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

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

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

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

在我的示例中,当我说“直接使用套接字API设置套接字级别的发送超时”时,我指的是通过函数的
SO\SNDTIMEO
socket选项

对于Indy,您可以使用
TIdSocketHandle.setsockopt()
方法调用
setsockopt()
,例如:

// Windows

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

(当
TIdTCPConnection.IOHandler
被分配了
TIdIOHandlerSocket
或后代时,
TIdTCPConnection.Socket
属性是访问
TIdIOHandlerSocket
的缩写)


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

确保套接字的另一端读取不是正确的解决方案吗?@David,当然,但我们无法控制所有其他端。最后,我试图使我们的软件尽可能地健壮和稳定,并让其他人来处理它自己的应用程序。我可以重新设计我们的应用程序,让专用线程写入每个连接的端口,但这需要我付出相当大的努力,所以我在寻找一个更好的解决方案。如果你这样做了,你会得到一个被阻塞的线程。这难道不会让你处于和现在一样糟糕的境地吗?David,这个应用程序,目前只有有限数量的工作线程,每个线程都在处理多个连接的请求。我认为writer线程更好,因为所有连接都可以继续处理请求和响应,唯一冻结的线程将是向特定连接写入的线程。该连接最终将终止或在另一端重置,因此线程将再次响应。现在,当一个线程冻结时,绑定到该线程的所有连接都处于无人值守状态。现在不可能改变这种设计。
// '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));