我有一个Android应用程序使用Indy 10 TIdHttpServer(随Delphi 2006推出)与Delphi 2006 Web服务应用程序通信。 Delphi应用程序生成一个大的XML文件并为此提供服务。 XML生成可能持续超过5分钟。
如果GenerateXml()
的持续时间超过约5分钟(*),如果在Delphi IDE中运行,我在TIdHTTPResponseInfo.WriteContent
中检测到错误10053:
Socket Error # 10053 Software caused connection abort.
但是,在android方面没有检测到任何内容,HttpGet
- 调用将永远持续。
我的问题是:
1。)为什么我得到错误10053,我该如何避免?似乎安卓会超时连接,但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();
}
Delphi实现TIdHttpServer.OnCommandGet:
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;
编辑:(*)即使在整个过程持续时间不到2分钟的情况下,我已经做了进一步的测试并遇到了错误。
答案 0 :(得分:3)
Android和您的服务器之间的某些东西,例如防火墙/路由器,可能会在闲置太久后切断连接。您应该尝试启用TCP keep-alives以避免这种情况。
另一方面,这是HTTP 1.1的chunked transfer encoding旨在处理的情况(假设您使用HTTP 1.1开始)。在将XML发送到客户端之前,不应等待5分钟才能完整生成整个XML,而是应该在生成XML时将XML发送出去。这不仅可以使连接保持活动状态,而且还可以减少服务器的内存占用,因为它不必一次将整个XML存储在内存中。
TIdHTTPServer
本身不支持发送分块响应(但TIdHTTP
确实支持接收分块响应),但手动实现并不是很困难。编写自定义TStream
派生类并覆盖其虚拟Write()
方法(或使用Indy的TIdEventStream
类),使用RFC 2616 Section 3.6.1中概述的格式将数据写入HTTP客户端。有了这个,您可以ServeXmlDoc()
将ResponseInfo.TransferEncoding
属性设置为'chunked'
并调用ResponseInfo.WriteHeader()
方法,而无需设置ResponseInfo.ContentText
或ResponseInfo.ContentStream
属性,然后将您的自定义流传递给IXMLDocument.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;
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;
另一方面,如果您的大部分等待时间都在GenerateXml()
内,而不在XmlDoc.SaveToStream()
内,那么您需要重新考虑您的服务器设计,并找出加快速度的方法{ {1}},或者只是删除GenerateXml()
并手动创建XML,以便在创建XML内容时使用IXMLDocument
发送它。