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

时间:2019-05-14 06:27:27

标签: 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;

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

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

我从雷米·勒博(Remy Lebeau)找到this answer到问题Delphi (Indy) Server Freezing on Write,他在评论中提到了我(强调我的意思):

  

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

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

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

1 个答案:

答案 0 :(得分:4)

  

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

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

就印地语而言,您可以使用setsockopt()方法来调用TIdSocketHandle.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属性是在TIdIOHandlerSocket被分配了TIdTCPConnection.IOHandler或其后代时访问TIdIOHandlerSocket的简写)。

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