Delphi Indy IdTcpClient 读取操作返回一个特定请求的截断数据

发布于 2024-08-02 02:38:48 字数 1790 浏览 2 评论 0原文

这是一个有趣的问题,我还没有解决。

我正在编写一个通过互联网与服务器进行通信的客户端。 我正在 Indy 10 中使用 RAD Studio 2007 本机个性使用 TIdTcpClient Internet Direct (Indy) 组件。

为了从服务器获取数据,我使用 SSL 通过端口 443 发出 HTTP 请求,其中我的请求详细信息包含在 HTTP 消息正文中。 到目前为止,一切都很好。 该代码的工作方式就像魅力一样,但有一个例外。

我正在提交一个请求,该请求应从服务器生成约 336 KB 的响应(HTTP 响应标头包含内容长度:344795)。 问题是我只得到了 320KB。 XML 格式的响应在 XML 元素的中间明显被截断。

就其价值而言,XML 是简单的文本。 没有可以解释截断的特殊字符。 我的 TIdTcpClient 组件只是报告,在收到部分响应后,服务器优雅地关闭了连接(这是每个响应预期完成的方式,即使是那些没有被截断的响应,所以这不是问题)。

我可以对同一台服务器进行几乎相同的调用,其中响应也超过几 K 字节,并且所有这些都工作得很好。 我发出的一个请求返回大约 850 KB,另一个请求返回大约 300 KB,依此类推。

简而言之,我仅在一个特定请求时遇到此问题。 所有其他请求(其中有很多)都会收到完整的响应。

我已经与该服务的创建者进行了交谈,并提供了我的请求的示例。 他报告说,该要求是正确的。 他还告诉我,当他向他的服务器发出相同的请求时,他会得到完整的响应。

我不知所措。 要么服务的创建者弄错了,并且该端的响应实际上存在问题,要么我的请求有一些特殊之处。

这里有我缺少的解决方案吗? 请注意,我还使用了许多其他读取机制(ReadString、ReadStrings、ReadBytes 等),它们都产生相同的结果,即在 320KB 标记处截断这一特定响应。

该代码可能不相关,但无论如何我都会包含它。 抱歉,我无法包含 XML 请求,因为它包含专有信息。 (ReadTimeout设置为20秒,但是请求大约1秒就返回了,所以不是超时问题。)

function TClient.GetResponse(PayloadCDS: TClientDataSet): String;
var
  s: String;
begin
  try
    try
      s := GetBody(PayloadCDS);
      IdTcpClient1.Host := Host;
      IdTcpClient1.Port := StrToInt(Port);
      IdTcpClient1.ReadTimeout := ReadTimeout;
      IdTcpClient1.Connect;
      IdTcpClient1.IOHandler.LargeStream := True;
      //IdTcpClient1.IOHandler.RecvBufferSize := 2000000;
      IdTcpClient1.IOHandler.Write(s);
      Result := IdTcpClient1.IOHandler.AllData;
    except
      on E: EIdConnClosedGracefully do
      begin
         //eat the exception
      end;
      on e: Exception do
      begin
        raise;
      end;
    end;
  finally
    if IdTcpClient1.Connected then
      IdTcpClient1.Disconnect;
  end;
end;

This is an interesting problem that I’ve not been able to solve yet.

I am writing a client that communicates across the Internet to a server. I am using the TIdTcpClient Internet Direct (Indy) component in Indy 10 using RAD Studio 2007 native personality.

To get data from the server, I issue an HTTP request using SSL over port 443 where my request details are contained in the HTTP message body. So far, so good. The code works like charm, with one exception.

There is one request that I am submitting that should produce a response of about 336 KB from the server (the HTTP response header contains Content-Length: 344795). The problem is that I am getting only 320KB back. The response, which is in XML, is clearly truncated in the middle of an XML element.

For what it’s worth, the XML is simple text. There are no special characters that can account for the truncation. My TIdTcpClient component is simply reporting that, after receiving the partial response, that the server closed the connection gracefully (which is how every response is expected to be completed, even those that are not truncated, so this is not a problem).

I can make nearly identical calls to the same server where the response is also more than a few K bytes, and all of these work just fine. One request I make returns about 850 KB, another returns about 300 KB, and so on.

In short, I encounter this problem only with the one specific request. All other requests, of which there are many, receive a complete response.

I have talked to the creator of the service, and have supplied examples of my request. He reported that the request is correct. He also told me that when he issues my same request to his server that he gets a complete response.

I’m at a loss. Either the creator of the service is mistaken, and there is actually a problem with the response on that end, or there is something peculiar about my request.

Is there a solution here that I'm missing? Note that I’ve also used a number of other read mechanisms (ReadString, ReadStrings, ReadBytes, etc) and all produce the same result, a truncation of this one specific response at the 320KB mark.

The code is probably not relevant, but I’ll include it anyway. Sorry, but I cannot include the XML request, as it includes proprietary information. (ReadTimeout is set to 20 seconds, but the request returns in about 1 second, so it's not a timeout issue.)

function TClient.GetResponse(PayloadCDS: TClientDataSet): String;
var
  s: String;
begin
  try
    try
      s := GetBody(PayloadCDS);
      IdTcpClient1.Host := Host;
      IdTcpClient1.Port := StrToInt(Port);
      IdTcpClient1.ReadTimeout := ReadTimeout;
      IdTcpClient1.Connect;
      IdTcpClient1.IOHandler.LargeStream := True;
      //IdTcpClient1.IOHandler.RecvBufferSize := 2000000;
      IdTcpClient1.IOHandler.Write(s);
      Result := IdTcpClient1.IOHandler.AllData;
    except
      on E: EIdConnClosedGracefully do
      begin
         //eat the exception
      end;
      on e: Exception do
      begin
        raise;
      end;
    end;
  finally
    if IdTcpClient1.Connected then
      IdTcpClient1.Disconnect;
  end;
end;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

深海夜未眠 2024-08-09 02:38:48

由于您正在发送 HTTP 请求,因此您应该直接使用 TIdHTTP 组件而不是 TIdTCPClient 组件。 TIdHTTP 为您管理的 HTTP 协议有很多详细信息,如果您继续直接使用 TIdTCPClient,则必须手动处理这些详细信息。

如果您打算继续直接使用 TIdTCPClient,那么您至少应该停止使用 TIdIOHandler.AllData() 方法。 提取“Content-Length”回复标头(您可以将标头捕获到 TStringList 或 TIdHeaderList,然后使用其 Values[] 属性),然后将实际报告的字节计数传递给 TIdIOHandler.ReadString() 或 TIdIOHandler.ReadStream ()。 这将有助于确保读取 I/O 不会因为服务器在回复结束时断开连接而过早停止读取。

Since you are sending an HTTP request, you should be using the TIdHTTP component instead of the TIdTCPClient component directly. There are a lot of details about the HTTP protocol that TIdHTTP manages for you that you would have to handle manually if you continue using TIdTCPClient directly.

If you are going to continue using TIdTCPClient directly, then you should at least stop using the TIdIOHandler.AllData() method. Extract the 'Content-Length' reply header (you can Capture() the headers into a TStringList or TIdHeaderList and then use its Values[] property) and then pass the actual reported byte count to either TIdIOHandler.ReadString() or TIdIOHandler.ReadStream(). That will help ensure that the reading I/O does not stop reading prematurely because of the server's disconnect at the end of the reply.

任性一次 2024-08-09 02:38:48

正如我在原来的问题中提到的,此连接使用 SSL。 这要求您使用 IdSSLIOHandlerSocketOpenSSL 组件,并将其分配给 IdTcpClient 组件的 IOHandler 属性。 所有这些都是我最初的设计,正如我所提到的,我的许多请求都得到了正确的响应。

周末我发现另一个请求返回不完整的响应。 我捕获了原始 HTTP 请求,并使用名为 SOAP UI 的工具将该请求提交到服务器。 使用 SOAP UI,服务器返回完整的答案。 显然,我的客户端出了问题,而不是服务器出了问题。

我终于通过摆弄各种属性找到了解决方案。 最终纠正该问题的属性位于 IdSSLIOHandlerSocketOpenSSL 类的 SSLOptions 属性中。 SSLOptions.Method 的默认值为 sslvSSLv2。 当我将方法更改为 sslvSSLv23 时,所有响应都完整返回。

为什么我能够在做出此更改之前检索到一些完整的回复,而不是全部,这对我来说仍然是一个谜。 尽管如此,将 IdSSLIOHandlerSocketOpenSSL.SSLOptions.Method 设置为 sslvSSLv23 解决了我的问题。

谢谢雷米·勒博的建议。

As I mentioned in my original question, this connection uses SSL. This requires that you use an IdSSLIOHandlerSocketOpenSSL component, which you assign to the IOHandler property of the IdTcpClient component. All of this was in my original design, and as I mentioned, many of my requests were being responded to correctly.

Over the weekend I discovered another request which was returning an incomplete response. I captured that original HTTP request, and using a tool called SOAP UI, I submitted that request to the server. Using SOAP UI, the server returned a complete answer. Clearly, something was wrong with my client, and not the server.

I finally found the solution by just fiddling around with various properties. The property that finally corrrected the problem was in the SSLOptions property of the IdSSLIOHandlerSocketOpenSSL class. The default value of SSLOptions.Method is sslvSSLv2. When I changed Method to sslvSSLv23, all responses returned complete.

Why I was able to retrieve some responses in full and not all before I made this change is still a mystery to me. Nonetheless, setting IdSSLIOHandlerSocketOpenSSL.SSLOptions.Method to sslvSSLv23 solved my problem.

Thank you Remy Lebeau, for your suggestions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文