如果Android中的HttpGet操作持续时间过长,如何避免出现错误10053(WSAECONNABORTED)?
作者:互联网
我有一个使用Indy 10 TIdHttpServer(与Delphi 2006一起提供)与Delphi 2006 Web服务应用程序进行通信的Android应用程序. Delphi应用程序生成一个大的XML文件并提供服务. XML生成可能会持续5分钟以上.
如果GenerateXml()的持续时间超过大约5分钟(*),如果在Delphi IDE中运行,我将在TIdHTTPResponseInfo.WriteContent中检测到错误10053:
Socket Error # 10053 Software caused connection abort.
但是,在android方面,什么也没有检测到,并且HttpGet调用将永远持续下去.
我的问题是:
1.)为什么会出现错误10053,如何避免该错误?似乎android超时了,但http.socket.timeout设置为infinite.
和
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();
}
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;
编辑:(*)即使整个过程持续时间不到2分钟,我也进行了进一步的测试并遇到了错误.
解决方法:
闲置时间过长后,Android和服务器之间的某些内容(例如防火墙/路由器)可能会切断连接.您应该尝试启用TCP保持活动以避免这种情况.
另一方面,这种情况是HTTP 1.1的chunked transfer encoding设计用来处理的(假设您首先使用HTTP 1.1).您不必等待5分钟才能完整生成完整的XML,然后再将其发送给客户端,而应该在生成XML的过程中将其分段发送.这不仅使连接保持活动状态,而且还减少了服务器的内存占用量,因为它不必一次将整个XML存储在内存中.
TIdHTTPServer本身还不支持发送分块的响应(但TIdHTTP确实支持接收分块的响应),但是手动实现并不是很困难.编写一个自定义的TStream派生类并覆盖其虚拟Write()方法(或使用Indy的TIdEventStream类)以使用RFC 2616 Section 3.6.1中概述的格式将数据写入HTTP客户端.这样,您可以让ServeXmlDoc()设置ResponseInfo.TransferEncoding属性设置为“分块”并调用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()中,那么您需要重新考虑服务器设计,并找出加速GenerateXml()的方法,或者摆脱IXMLDocument并手动创建XML,以便在创建XML内容时可以使用ResponseInfo.Connection.IOHandler将其发送.
标签:delphi-2006,http,indy,android,delphi 来源: https://codeday.me/bug/20191122/2062740.html