我使用了多端口tcp服务器来接收一些连接
像这样
procedure TForm2.IdTCPServer1Execute(AContext: TIdContext);
var
aByte: Byte;
i,j , tmBodyFrameLength:integer;
myThread : tthread;
begin
if not Assigned( allOfflineStringList ) then
begin
allOfflineStringList := TStringlist.Create;
end;
allOfflineStringList.Clear;
case AContext.Binding.Port of
55000: begin {offline and image}
AContext.Connection.IOHandler.ReadBytes(data, 1099, False);
rowFrame :='';
for I := 0 to length(data)-1 do
begin
rowFrame := rowFrame + (data[i].ToHexString);
end;
newFrame := copy( rowFrame , 9 , maxInt );
allOfflineStringList.Append( newFrame );
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
Label985.caption := 'Offline : ' + allOfflineStringList.Count.ToString ;
//Memo14.Lines.Add( datetimetostr(now) +':'+ newFrame );
form2.AbLED601.Tag := DateTimeToUnix(now);
form2.AbLED601.Checked := true;
end);
end;
55001: begin {tm online}
repeat
aByte := AContext.Connection.IOHandler.ReadByte;
if aByte=$C0 then
begin
SDRtmOnlineRowFrame2 := SDRtmOnlineRowFrame;
SDRtmOnlineRowFrame := '';
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
form2.Memo14.Lines.Add('tm:'+ SDRtmOnlineRowFrame2 );
end);
end
else
begin
SDRtmOnlineRowFrame := SDRtmOnlineRowFrame + aByte.ToHexString;
end;
until true;
end;
55003: begin {beacon online}
repeat
aByte := AContext.Connection.IOHandler.ReadByte;
if aByte=$C0 then
begin
SDRtmOnlineBeaconRowFrame2 := SDRtmOnlineBeaconRowFrame;
SDRtmOnlineBeaconRowFrame := '';
TThread.Synchronize (TThread.CurrentThread,
procedure ()
begin
form2.Memo14.Lines.Add('beacon:'+ SDRtmOnlineBeaconRowFrame2 );
end);
end
else
begin
SDRtmOnlineBeaconRowFrame := SDRtmOnlineBeaconRowFrame + aByte.ToHexString;
end;
until true;
end;
end;
end;
一切正常 但是当我关闭连接时收到数据
应用将被hange,并且不再响应!
启用和禁用是这样的:
procedure TForm2.CheckBox6Click(Sender: TObject);
var
ic:integer;
allIpList : TStringList;
begin
AbLED412.Checked := CheckBox6.Checked;
if CheckBox6.Checked=true then
begin
IdTCPServer1.Active := False;
IdTCPServer1.Bindings.Clear;
with IdTCPServer1.Bindings.Add do
begin
//IP := '192.168.1.5';
Port := 55000;
end;
with IdTCPServer1.Bindings.Add do
begin
//IP := '192.168.1.5';
Port := 55001;
end;
with IdTCPServer1.Bindings.Add do
begin
//IP := '192.168.1.5';
Port := 55003;
end;
IdTCPServer1.Active := True;
IdTCPServer1.StartListening;
TIdStack.IncUsage;
try
allIpList := TStringList.Create;
GStack.AddLocalAddressesToList( allIpList );
memo14.lines.clear;
for ic := 0 to allIpList.Count-1 do
begin
memo14.lines.Add('Create tcp connection on ip : '+allIpList[ic]+' and port : 55000');
memo14.lines.Add('Create tcp connection on ip : '+allIpList[ic]+' and port : 55001');
memo14.lines.Add('Create tcp connection on ip : '+allIpList[ic]+' and port : 55003');
end;
finally
TIdStack.DecUsage;
end;
end
else
begin
IdTCPServer1.StopListening;
IdTCPServer1.Active := False;
IdTCPServer1.Bindings.Clear;
memo14.lines.clear;
end;
end;
当我关闭应用程序时也收到数据时,它再次挂起 但是当发件人断开连接时,关闭应用程序不会造成任何问题
我该如何解决?
答案 0 :(得分:1)
您的TIdTCPServer.OnExecute
处理程序正在以 thread-unsafe 方式使用多个变量。您不能保护它们免受同时访问它们的多个线程的侵害,从而导致它们的数据出现竞争状况。
但是,更重要的是,您使用TThread.Synchronize()
是TIdTCPServer
死锁的常见原因,因为它是一个多线程组件。它的OnConnect
,OnDisconnect
,OnExecute
和OnError
事件是在客户端工作程序线程的上下文中而不是在主UI线程中调用的。 TThread.Synchronize()
阻塞调用线程,直到主UI线程处理请求为止。停用TIdTCPServer
将终止所有正在运行的客户端线程,并等待它们完全终止。因此,如果您在客户端线程 中调用TThread.Synchronize()
时,则主UI线程被阻止以停用服务器,那么当主UI线程正在等待时,客户端线程正在主UI线程上等待在客户端线程上-死锁!
您可以通过以下几种方法来解决此问题:
在停用服务器时避免调用TThread.Synchronize()
。说起来容易做起来难,因为您决定停用TThread.Synchronize()
时可能已经在待处理的TIdTCPServer
中。决定是否呼叫TThread.Synchronize()
时,这是一种竞争条件。
在单独的工作线程中停用TIdTCPServer
,使主UI线程可以自由处理TThread.Synchronize()
和TThread.Queue()
请求。如果您使用TThread
进行停用,则在等待线程终止时,在主UI线程中调用TThread.WaitFor()
方法将处理Synchronize()
/ Queue()
请求。
使用TThread.Queue()
而不是TThread.Synchronize()
,特别是在执行客户端线程实际上不需要等待的操作(例如UI更新)时。
在旁注中,在您的CheckBox6Click()
中:
您根本不应该呼叫TIdTCPServer.StartListening()
或TIdTCPServer.StopListening()
。 TIdTCPServer.Active
属性设置器会在内部为您调用它们。
您也不需要调用TIdStack.IncUsage()
或TIdStack.DecUsage()
,因为TIdTCPServer
的构造函数和析构函数会为您调用它们。
您正在泄漏allIpList
,因为您没有Free()
。而且无论如何,TIdStack.AddLocalAddressesToList()
已过时,您应该改用TIdStack.GetLocalAddressList()
。
尝试一下:
procedure TForm2.CheckBox6Click(Sender: TObject);
var
ic: integer;
allIpList : TIdStackLocalAddressList;
begin
AbLED412.Checked := CheckBox6.Checked;
if CheckBox6.Checked then
begin
IdTCPServer1.Active := False;
IdTCPServer1.Bindings.Clear;
with IdTCPServer1.Bindings.Add do
begin
//IP := '192.168.1.5';
Port := 55000;
end;
with IdTCPServer1.Bindings.Add do
begin
//IP := '192.168.1.5';
Port := 55001;
end;
with IdTCPServer1.Bindings.Add do
begin
//IP := '192.168.1.5';
Port := 55003;
end;
IdTCPServer1.Active := True;
allIpList := TIdStackLocalAddressList.Create;
try
GStack.GetLocalAddressesList( allIpList );
Memo14.Lines.Clear;
{
for ic := 0 to IdTCPServer1.Bindings.Count-1 do
begin
Memo14.Lines.Add('Create tcp connection on ip : ' + IdTCPServer1.Bindings[ic].IP + ' and port : ' + IntToStr(IdTCPServer1.Bindings[ic].Port));
end;
}
for ic := 0 to allIpList.Count-1 do
begin
if allIpList[ic].IPVersion = ID_DEFAULT_IP_VERSION then
begin
Memo14.Lines.Add('Create tcp connection on ip : ' + allIpList[ic].IPAddress + ' and port : 55000');
Memo14.Lines.Add('Create tcp connection on ip : ' + allIpList[ic].IPAddress + ' and port : 55001');
Memo14.Lines.Add('Create tcp connection on ip : ' + allIpList[ic].IPAddress + ' and port : 55003');
end;
end;
finally
allIpList.Free;
end;
end
else
begin
IdTCPServer1.Active := False;
IdTCPServer1.Bindings.Clear;
Memo14.Lines.Clear;
end;
end;