Android 如何为下一个客户端连接正确重新启动Indy TCP服务器?
我有一个Indy TCP服务器,客户端连接到它,发送信息,服务器接收,创建一个巨大的TStringList并发送回客户端。这种情况每天发生数千次,因此我决定将六台服务器放在不同的.exe上,在不同的端口上执行相同的操作,并将客户端应用程序每次连接到一个随机端口上 发生了什么: 1) 从客户第一次尝试连接到他收到所有他需要的信息的那一刻是相当高的,就像他做了两次工作一样 2) 大约10%的情况下,客户机尝试连接,而服务器似乎忽略了尝试,客户机除了重试之外,没有启动,并且卡住了 服务器:Android 如何为下一个客户端连接正确重新启动Indy TCP服务器?,android,delphi,tcp,indy,delphi-10.1-berlin,Android,Delphi,Tcp,Indy,Delphi 10.1 Berlin,我有一个Indy TCP服务器,客户端连接到它,发送信息,服务器接收,创建一个巨大的TStringList并发送回客户端。这种情况每天发生数千次,因此我决定将六台服务器放在不同的.exe上,在不同的端口上执行相同的操作,并将客户端应用程序每次连接到一个随机端口上 发生了什么: 1) 从客户第一次尝试连接到他收到所有他需要的信息的那一刻是相当高的,就像他做了两次工作一样 2) 大约10%的情况下,客户机尝试连接,而服务器似乎忽略了尝试,客户机除了重试之外,没有启动,并且卡住了 服务器: MaxC
设置为1MaxConnections
0ListenQueue
至5000终止等待时间
在客户端和服务器上设置为FalseReuseSocket
:=在客户端和服务器上为TrueUsaNagle
计时器
:
IdDownloadClient.IOHandler.MaxLineLength := MaxInt;
IdDownloadClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
// Choose a random port
IdDownloadClient.Connect;
IdDownloadClient.IOHandler.WriteLn(lat+','+long);
Timer6.Enabled := True;
客户端定时器6
(25毫秒):
服务器OnExecute
事件:
begin
try
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
LatLong := AContext.Connection.IOHandler.ReadLn;
if LatLong <> '' then begin
latF := StrToFloat(StringReplace(Copy(LatLong,0,ansipos(',',LatLong)-1),'.',',',[rfIgnoreCase, rfReplaceAll]));
lonF := StrToFloat(StringReplace(Copy(LatLong,ansipos(',',LatLong)+1,11),'.',',',[rfIgnoreCase, rfReplaceAll]));
// Creates a TStringList from a Memo to send (around half a sec of proccessing)
bufferlist := TStringList.Create;
bufferlist.Add('h-023.64086400000,-046.57425900000 99999999 0300 0301 0001 test|123 test');
for J := 0 to Memo1.Lines.Count-2 do
begin
if ((abs(latF-StrToFloat(StringReplace(Copy(Memo1.Lines[J],2,16),'.',',',[rfIgnoreCase, rfReplaceAll]))) < 0.1) and (abs(lonF-StrToFloat(StringReplace(Copy(Memo1.Lines[J],19,16),'.',',',[rfIgnoreCase, rfReplaceAll]))) < 0.1)) then
bufferlist.Add(Memo1.Lines[J]);
end;
///////// Start to send
for i := 0 to bufferlist.Count-1 do
begin
AContext.Connection.IOHandler.WriteLn(bufferlist[i]);
end;
AContext.Connection.IOHandler.WriteLn('@'); // Send '@' to the client to say the list is over
bufferlist.Free;
end;
except
if Assigned(bufferlist) then bufferlist.Free;
Exit;
end;
end;
开始
尝试
AContext.Connection.IOHandler.DefStringEncoding:=IndyTextEncoding\u UTF8;
LatLong:=AContext.Connection.IOHandler.ReadLn;
如果LatLong“”则开始
latF:=StrToFloat(StringReplace(复制(LatLong,0,ansipos(',',',,LatLong)-1),,,,,,,,[rfIgnoreCase,rfReplaceAll]);
lonF:=StrToFloat(StringReplace(复制(LatLong,ansipos(',',',LatLong)+1,11),,,,,,,,[rfIgnoreCase,rfReplaceAll]);
//从要发送的备忘录创建一个TStringList(大约半秒的处理时间)
bufferlist:=TStringList.Create;
缓冲区列表。添加('h-023.64086400000,-046.57425900000 9999990300 0301 0001试验| 123试验');
对于J:=0到1.Lines.Count-2 do
开始
如果((abs(latF StrToFloat(StringReplace)(复制(备忘录1.行[J],2,16),“,”,“,[rfIgnoreCase,rfReplaceAll]))小于0.1)和(abs(lonF StrToFloat(复制(备忘录1.行[J],19,16),“,”,[rfIgnoreCase,rfReplaceAll]))小于0.1),则
bufferlist.Add(备忘录1.行[J]);
结束;
/////////开始发送
对于i:=0的bufferlist.Count-1 do
开始
AContext.Connection.IOHandler.WriteLn(bufferlist[i]);
结束;
AContext.Connection.IOHandler.WriteLn('@');//将“@”发送到客户端以说明列表已结束
bufferlist.Free;
结束;
除了
如果已分配(bufferlist),则bufferlist.Free;
出口
结束;
结束;
由于所有的连接都来自3G/4G手机,我假设其中一些手机的终端坏了,这就是问题的原因,那么我在代码中做错了什么
我可以做些什么来解决这个问题,或者至少改进它?您的服务器的
OnExecute
正在吞噬所有引发的异常。当客户端断开连接时,在该客户端的套接字上执行的下一个套接字I/O将引发异常,您将捕获并丢弃该异常。因此,服务器将不知道客户端已断开连接,并将继续触发OnExecute
事件。而且,由于您的服务器设置为MaxConnections=1
,因此在前一个客户端完全释放之前,新客户端无法连接到服务器。您的OnExecute
代码必须直接调用AContext.Connection.Disconnect()
,或引发未捕获的异常,以释放客户端线程
简单的经验法则-不要接受例外情况!如果捕获到不知道如何处理的异常,则应重新引发它,因为它可能在调用堆栈的更高层进行处理。对于TIdTCPServer
,永远不要吞下从eideexception
派生的Indy异常,让服务器来处理它。您使用try/except
来释放bufferlist
应该改为try/finally
事实上,我建议不要使用TStringList
来收集响应数据,因为它实际上并没有帮助您,而是妨碍了服务器的性能。在完整构建整个TStringList
之前,客户端不会收到来自服务器的任何响应,这可能会导致客户端超时。最好在创建文本时将每行文本发送给客户机,这样客户机就知道服务器实际上在做什么,而不是死机/冻结
我在OnExecute
代码中看到的另一个问题是,它直接访问UI控件(aTMemo
),而不与UI线程同步TIdTCPServer
是一个多线程组件,其事件在工作线程中激发。在工作线程中访问UI控件时,必须与UI线程同步
最后,您对Copy()
和StringReplace()
的过度使用是一个严重的问题,并且通常使代码难以维护
请尝试类似以下内容:
procedure IdTCPServer1Connect(AContext: TIdContext);
begin
// do this assignment one time, not on every OnExecute loop iteration
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
end;
procedure IdTCPServer1Execute(AContext: TIdContext);
var
s: string;
latF, lonF: Double;
fmt: TFormatSettings;
lines: TStringList;
j: Integer;
begin
s := AContext.Connection.IOHandler.ReadLn;
if s = '' then Exit;
fmt := TFormatSettings.Create;
fmt.DecimalSeparator := '.';
latF := StrToFloat(Fetch(s, ','), fmt);
lonF := StrToFloat(s, fmt);
AContext.Connection.IOHandler.WriteLn('h-023.64086400000,-046.57425900000 99999999 0300 0301 0001 test|123 test');
lines := TStringList.Create;
try
TThread.Synchronize(nil,
procedure
begin
lines.Assign(Memo1.Lines);
end
);
for J := 0 to lines.Count-2 do
begin
s := lines[J];
if (abs(latF-StrToFloat(Copy(s, 2, 16), fmt)) < 0.1) and (abs(lonF-StrToFloat(Copy(s, 19, 16), fmt)) < 0.1) then
AContext.Connection.IOHandler.WriteLn(s);
end;
finally
lines.Free;
end;
AContext.Connection.IOHandler.WriteLn('@'); // Send '@' to the client to say the list is over
end;
服务器的
OnExecute
正在吞咽所有引发的异常。当客户端断开连接时,在该客户端的套接字上执行的下一个套接字I/O将引发异常,您将捕获并丢弃该异常。因此,服务器将不知道客户端已断开连接,并将继续触发OnExecute
事件。而且,由于您的服务器设置为MaxConnections=1
,因此在前一个客户端完全释放之前,新客户端无法连接到服务器。您的OnExecute
代码必须直接调用AContext.Connection.Disconnect()
,或引发未捕获的异常,以释放客户端线程
简单的经验法则-不要接受例外情况!如果捕获到不知道如何处理的异常,则应重新引发它,因为它可能在调用堆栈的更高层进行处理。对于TIdTCPServer
,永远不要接受派生的Indy异常
procedure IdTCPServer1Connect(AContext: TIdContext);
begin
// do this assignment one time, not on every OnExecute loop iteration
AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
end;
procedure IdTCPServer1Execute(AContext: TIdContext);
var
s: string;
latF, lonF: Double;
fmt: TFormatSettings;
lines: TStringList;
j: Integer;
begin
s := AContext.Connection.IOHandler.ReadLn;
if s = '' then Exit;
fmt := TFormatSettings.Create;
fmt.DecimalSeparator := '.';
latF := StrToFloat(Fetch(s, ','), fmt);
lonF := StrToFloat(s, fmt);
AContext.Connection.IOHandler.WriteLn('h-023.64086400000,-046.57425900000 99999999 0300 0301 0001 test|123 test');
lines := TStringList.Create;
try
TThread.Synchronize(nil,
procedure
begin
lines.Assign(Memo1.Lines);
end
);
for J := 0 to lines.Count-2 do
begin
s := lines[J];
if (abs(latF-StrToFloat(Copy(s, 2, 16), fmt)) < 0.1) and (abs(lonF-StrToFloat(Copy(s, 19, 16), fmt)) < 0.1) then
AContext.Connection.IOHandler.WriteLn(s);
end;
finally
lines.Free;
end;
AContext.Connection.IOHandler.WriteLn('@'); // Send '@' to the client to say the list is over
end;
procedure Timer6Timer(Sender: TObject);
var
response: TStringList;
begin
with IdDownloadClient do
begin
try
if IOHandler.InputBufferIsEmpty then
begin
IOHandler.CheckForDataOnSource(0);
IOHandler.CheckForDisconnect;
if IOHandler.InputBufferIsEmpty then Exit;
end;
response := TStringList.Create;
try
// The server send an '@' to say the complete response has been sent
IOHandler.Capture(response, '@', False);
// use response as needed...
finally
response.Free;
end;
except
IOHandler.InputBuffer.Clear;
end;
Disconnect;
end;
Timer6.Enabled := False;
end;