多端口上的delphi TCP服务器挂起关闭

时间:2019-12-24 07:04:19

标签: delphi tcp indy

我使用了多端口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;

当我关闭应用程序时也收到数据时,它再次挂起 但是当发件人断开连接时,关闭应用程序不会造成任何问题

我该如何解决?

1 个答案:

答案 0 :(得分:1)

您的TIdTCPServer.OnExecute处理程序正在以 thread-unsafe 方式使用多个变量。您不能保护它们免受同时访问它们的多个线程的侵害,从而导致它们的数据出现竞争状况。

但是,更重要的是,您使用TThread.Synchronize()TIdTCPServer死锁的常见原因,因为它是一个多线程组件。它的OnConnectOnDisconnectOnExecuteOnError事件是在客户端工作程序线程的上下文中而不是在主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;