Indy TIdTCPClient 组件偶尔不会超时并且没有接收到数据
我正在使用 Internet Direct TIdTCPClient 组件与远程服务进行通信,以检索通常大小约为 5k 的消息。在典型的操作中,我会向服务发送大约 400 个请求,每个请求大约需要 1 秒才能完成。大多数时候一切都很完美。然而,大约有百分之一的时间请求花费了 189 秒,而我根本没有收到任何数据。为了方便讨论,我将其称为失败。
我特别有兴趣了解故障发生时到底发生了什么,以便我可以向服务发布者提供证据。首先,失败是不可重现的。如果我重新发送失败的请求,则很有可能(也许 99%)它会起作用。
我还捕获发生故障时发送的请求,因此我能够确认请求的格式正确。
我假设在故障期间我得到了一些数据,但不是全部。这就是原因。我的 IdTCPClient 有 30 秒超时(我什至将其设置为 5 秒,但这没有什么区别)。发生故障时,总是在189秒(加上大约500毫秒)后失败。
因此,我认为在故障期间我的组件正在接收少量数据,这就是我的客户端没有超时的原因。而且,我假设断开连接发生在服务上,因为我的超时值都没有设置为 189 秒。另一方面,读取 IOHandler.AllData 不会引发异常(甚至不会引发 EIdConnClosedGraceously 异常)。我对这些证据的解释正确吗?
我想要做的是在服务终止连接之前确认我正在获取一些数据,而不是全部数据。此外,我想知道部分数据是什么样的,因为我相信它可以帮助识别故障的根源。
目前,我的请求类似于以下内容:
//ExceptionName is a temporary global variable
//that I am using while trying to solve this issue
ExceptionName = 'no exception';
try
s := GetRequest(id);
IdTcpClient1.Host := Host;
IdTcpClient1.Port := StrToInt(Port);
IdTcpClient1.ReadTimeout := ReadTimeout;
try
IdTcpClient1.Connect;
except
on e: exception do
begin
ExceptionName := e.ClassName;
raise EConnectionFailure.Create('Connection refused: ' + e.Message)
end;
end;
IdTcpClient1.IOHandler.Writeln(s);
try
Result := IdTcpClient1.IOHandler.AllData;
except
on E: EIdConnClosedGracefully do
begin
ExceptionName := e.ClassName;
//eat this exception
end;
on e: Exception do
begin
ExceptionName := e.ClassName;
raise;
end;
end;
finally
if IdTcpClient1.Connected then
IdTcpClient1.Disconnect;
end;
使用 IOHandler.AllData 读取数据非常方便,但失败后无法检索任何数据(AllData 返回空字符串)。我在失败后测试了 IOHandler.InputBufferIsEmpty,它返回 True。
我还尝试了其他方法来读取数据,例如 IOHandler.ReadStream (这产生与读取 AllData 相同的结果)。我还使用了 IOHandler.ReadBytes 和 IOHandler.ReadByte(与 IOHandler.CheckForDataOnSource 结合使用)。没有任何效果。
我对部分数据传输的理解有误吗?如果是这样,为什么我会在失败之前看到一致的 189.nnnn 秒延迟。
如果可以进行部分数据传输,我应该采取什么方法来捕获故障之前接收到的每个数据字节。
我在这个项目中使用 Delphi 2009 和 Indy 10,但我认为版本与之没有任何关系。我不认为这是印地问题。
编辑:我已经使用 WireShark 检查了 Indy 客户端和服务器之间的通信。当其中一个故障发生时,在发送我的请求后,服务器发送了两个 [ACK] 数据包,然后沉默了 189 秒多一点。延迟之后,响应包括 [FIN、PSH、ACK],但没有应用程序数据。
当通信正常时,服务器响应我的请求返回的两个ACK包后面跟着一个应用程序数据包。
编辑:已向 Web 服务的发布者报告了该问题,正在等待回复。
编辑:好的,Web 服务的发布者已回复。他们最终承认存在问题并解决了其中一些问题。我们不再有超时。大多数回复大约在 2 秒内收到,少数回复时间稍长。出版商正在努力解决剩余问题。
感谢大家的意见。
I am using the Internet Direct TIdTCPClient component to communicate with a remote service to retrieve a message that is normally about 5k is size. During a typical operation I will send about 400 requests to the service, each one taking about 1 second to complete. Most of the time everything works perfectly. However, about one percent of the time the request takes 189 seconds and I receive no data at all. For ease of discussion, I'll call this a failure.
I am particularly interested in understanding exactly what is happening when the failure occurs so that I can take my evidence to the publisher of the service. First of all, the failure is not reproducible. If I re-send a failed request there is a very high probability (maybe 99 percent) that it will work.
I also capture the request that I send when a failure occurs, so I am able to confirm that the request is well formed.
I am assuming that during a failure that I am getting some data, just not all of it. Here is why. My IdTCPClient has a 30 second timeout (I've even set it to 5 seconds, but that didn't make a difference). When the failure occurs, it always fails after 189 seconds (plus about 500 milliseconds).
As a result, I am thinking that during a failure my component is receiving a trickle of data, which is why my client is not timing out. And, I am assuming that the disconnection is happening at the service since none of my timeout values are ever set to 189 seconds. On the other hand, reading IOHandler.AllData does not raise an exception (not even an EIdConnClosedGracefully exception). Am I interpreting this evidence correctly?
What I want to do is to confirm that I am getting some data, just not all of it, before the service terminates the connection. Furthermore, I want to know what that partial data looks like as I believe it can help identify the source of the failure.
Currently, my request is similar to the following:
//ExceptionName is a temporary global variable
//that I am using while trying to solve this issue
ExceptionName = 'no exception';
try
s := GetRequest(id);
IdTcpClient1.Host := Host;
IdTcpClient1.Port := StrToInt(Port);
IdTcpClient1.ReadTimeout := ReadTimeout;
try
IdTcpClient1.Connect;
except
on e: exception do
begin
ExceptionName := e.ClassName;
raise EConnectionFailure.Create('Connection refused: ' + e.Message)
end;
end;
IdTcpClient1.IOHandler.Writeln(s);
try
Result := IdTcpClient1.IOHandler.AllData;
except
on E: EIdConnClosedGracefully do
begin
ExceptionName := e.ClassName;
//eat this exception
end;
on e: Exception do
begin
ExceptionName := e.ClassName;
raise;
end;
end;
finally
if IdTcpClient1.Connected then
IdTcpClient1.Disconnect;
end;
Using IOHandler.AllData to read the data is very convenient, but I cannot retrieve any data following a failure (AllData returns an empty string). I've tested IOHandler.InputBufferIsEmpty after a failure, and it returns True.
I've also tried other methods to read the data, such as IOHandler.ReadStream (this produced the same result as reading AllData). I also used IOHandler.ReadBytes and IOHandler.ReadByte (in conjunction with IOHandler.CheckForDataOnSource). Nothing has worked.
Am I wrong about partial data transmission? If so, why would I see a consistent 189.nnnn seconds delay before the failure.
If partial data transmission is a possibility, what approach should I take to capture every byte of data received before the failure.
I am using Delphi 2009 for this project and Indy 10, but I don't think the version has anything to do with it. I don't think this is an Indy issue.
Edit: I have inspected the communication between my Indy client and the server using WireShark. When one of these failures occur, after sending my request the server sent two [ACK] packets followed by silence for just over 189 seconds. After that delay, the response included [FIN, PSH, ACK] but no application data.
When the communication worked normally, the two ACK packets returned by the server in response to my request was followed by an application data packet.
Edit: Have reported the issue to the publisher of the Web service, and am waiting for a reply.
Edit: Ok, the publisher of the Web service has responded. They acknowledged problems on their end and have addressed some of those. We are no longer getting timeouts. Most responses are received in about 2 seconds, with a few taking slightly longer. The publisher is working to fix the remaining issues.
Thank you everyone for your input.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您需要运行 Fiddler2 并观察流量。它将自身作为代理插入并嗅探使用 WinInet 堆栈的所有信息。
然后您就知道是否收到了任何数据,以及您正在发送和接收的数据。
http://www.fiddler2.com/fiddler2/
You need to run Fiddler2 and watch the traffic. It inserts itself as a proxy and sniffs evertying that uses the WinInet stack.
Then you know whether you've gotten any data or not, and exactly what you're sending and receiving.
http://www.fiddler2.com/fiddler2/