这是一个我尚未能解决的有趣问题。
我正在编写一个通过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;
答案 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,感谢您的建议。