Alo*_*mer 5 delphi android http indy delphi-2006
我有一个 Android 应用程序使用 Indy 10 TIdHttpServer(Delphi 2006 附带)与 Delphi 2006 Web 服务应用程序进行通信。Delphi 应用程序生成一个大的 XML 文件并为其提供服务。XML 生成可能持续超过 5 分钟。
如果持续时间GenerateXml()超过大约 5 分钟 (*),TIdHTTPResponseInfo.WriteContent如果在 Delphi IDE 中运行,我会检测到错误 10053:
Socket Error # 10053 Software caused connection abort.
Run Code Online (Sandbox Code Playgroud)
然而,在 android 端没有检测到任何内容,并且HttpGet-call 会永远持续。
我的问题是:
1.) 为什么会收到错误 10053 以及如何避免它?看起来像android超时连接,但http.socket.timeout设置为无限。
和
2.) 我能做些什么来检测客户端的此类错误(除了设置超时,该超时必须太大而无用)?我可以在 TIdHttpServer.OnException 中做一些事情吗?
这是我的代码。Android - 下载函数,在 AsyncTask 内运行:
protected static HttpEntity downloadEntity(String url) throws IOException {
HttpClient client = new DefaultHttpClient();
//Check because of Error 10053: but timeout is null -> infinite
Log.d("TAG", "http.socket.timeout: " + client.getParams().getParameter("http.socket.timeout"));
HttpGet get = new HttpGet(url);
HttpResponse response;
try {
//in case of Error 10053 the following call seems to last forever (in PlainSocketImpl.read)
response = client.execute(get);
} catch (ClientProtocolException e) {
//...
}
//...
return response.getEntity();
}
Run Code Online (Sandbox Code Playgroud)
TIdHttpServer.OnCommandGet的Delphi实现:
procedure ServeXmlDoc(XmlDoc: IXMLDocument; ResponseInfo: TIdHTTPResponseInfo);
var
TempStream: TMemoryStream;
begin
ResponseInfo.ContentType := 'text/xml';
TempStream := TMemoryStream.Create;
XMLDoc.SaveToStream(TempStream);
ResponseInfo.FreeContentStream := True;
ResponseInfo.ContentStream := TempStream;
end;
procedure TMyService.HTTPServerCommandGet(AContext: TIdContext; RequestInfo: TIdHTTPRequestInfo;
ResponseInfo: TIdHTTPResponseInfo);
begin
Coinitialize(nil);
try
//...
ServeXmlDoc(GenerateXml(), ResponseInfo);
finally
CoUninitialize;
end;
end;
Run Code Online (Sandbox Code Playgroud)
编辑: (*) 我做了进一步的测试,即使整个过程持续时间不到 2 分钟,也遇到了错误。
Android 和服务器之间的某些东西(例如防火墙/路由器)可能会在闲置太长时间后切断连接。您应该尝试启用 TCP keep-alive 以避免这种情况。
另一方面,HTTP 1.1 的分块传输编码旨在处理这种情况(假设您一开始就使用 HTTP 1.1)。您应该在生成 XML 时将其分段发送,而不是等待 5 分钟完整生成整个 XML,然后再将其发送到客户端。这不仅可以保持连接处于活动状态,还可以减少服务器的内存占用,因为它不必一次性将整个 XML 存储在内存中。
TIdHTTPServer(尚)本身不支持发送分块响应(但TIdHTTP支持接收分块响应),但是手动实现并不困难。编写自定义TStream派生类并覆盖其虚拟Write()方法(或使用 Indy 的类),以使用RFC 2616 第 3.6.1 节TIdEventStream中概述的格式将数据写入 HTTP 客户端。这样,您就可以将属性设置为并调用该方法,而无需设置 或属性,然后将自定义流传递给,以便它将在标头之后完成响应数据的写入。例如:ServeXmlDoc()ResponseInfo.TransferEncoding'chunked'ResponseInfo.WriteHeader()ResponseInfo.ContentTextResponseInfo.ContentStreamIXMLDocument.SaveToStream()
type
TMyChunkedStream = class(TStream)
private
fIO: TIdIOHandler;
public
constructor Create(AIO: TIdIOHandler);
function Write(const Buffer; Count: Longint): Longint; override;
procedure Finished;
...
end;
constructor TMyChunkedStream.Create(AIO: TIdIOHandler);
begin
inherited Create;
fIO := AIO;
end;
function TMyChunkedStream.Write(const Buffer; Count: Longint): Longint; override;
begin
if Count > 0 then
begin
fIO.WriteLn(IntToHex(Count, 1));
fIO.Write(RawToBytes(Buffer, Count));
fIO.WriteLn;
end;
Result := Count;
end;
procedure TMyChunkedStream.Finished;
begin
fIO.WriteLn('0');
fIO.WriteLn;
end;
Run Code Online (Sandbox Code Playgroud)
procedure ServeXmlDoc(XmlDoc: IXMLDocument; ResponseInfo: TIdHTTPResponseInfo);
var
TempStream: TMyChunkedStream;
begin
ResponseInfo.ContentType := 'text/xml';
ResponseInfo.TransferEncoding := 'chunked';
ResponseInfo.WriteHeader;
TempStream := TMyChunkedStream.Create(ResponseInfo.Connection.IOHandler);
try
XMLDoc.SaveToStream(TempStream);
TempStream.Finished;
finally
TempStream.Free;
end;
end;
Run Code Online (Sandbox Code Playgroud)
另一方面,如果您的大部分等待时间都在 内部GenerateXml()而不是在 中XmlDoc.SaveToStream(),那么您需要重新考虑您的服务器设计,并找出一种加快速度的方法GenerateXml(),或者只是摆脱IXMLDocument并手动创建 XML,以便您可以ResponseInfo.Connection.IOHandler在创建 XML 内容时使用 发送它。
| 归档时间: |
|
| 查看次数: |
8253 次 |
| 最近记录: |