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

时间:2009-08-14 14:51:44

标签: delphi delphi-2007 indy

这是一个我尚未能解决的有趣问题。

我正在编写一个通过Internet与服务器通信的客户端。我使用RAD Studio 2007原生个性在Indy 10中使用TIdTcpClient Internet Direct(Indy)组件。

要从服务器获取数据,我使用SSL通过端口443发出HTTP请求,其中我的请求详细信息包含在HTTP消息正文中。到现在为止还挺好。代码就像魅力一样,只有一个例外。

我提交的一个请求应该从服务器产生大约336 KB的响应(HTTP响应头包含Content-Length: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;

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:0)

正如我在原始问题中提到的,此连接使用SSL。这要求您使用IdSSLIOHandlerSocketOpenSSL组件,该组件分配给IdTcpClient组件的IOHandler属性。所有这一切都在我的原始设计中,正如我所提到的,我的许多请求都得到了正确的回应。

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

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

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

感谢Remy Lebeau,感谢您的建议。