TIdNotify.Notify() не работает при вызове с TIdTCPServer

#delphi #indy #lazarus #indy10

#delphi #indy #лазарь #indy10

Вопрос:

У меня есть консольное приложение Lazarus, где у меня есть простой TIdTCPServer . Чтобы быть потокобезопасным приложением, я добавил TLog.LogMsg() (который использует TIdNotify ).

Проблема в том, что когда я вызываю эту функцию из основного потока, на консоли появляется сообщение, но когда оно вызывается из OnExecute OnConnect события или TIdTCPServer , сообщение не отображается.

Можете ли вы помочь мне с этой проблемой?

 program Srv;

{$I Synopse.inc}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, SysUtils, CustApp, Generics.Collections, IdTCPServer, IdCustomTCPServer, IdContext, IdGlobal, Db, mORMot, mORMotSQLite3, IdSync, functions, SynCommons, SynSQLite3Static;

type

  { TMyApplication }
  TMyApplication = class(TCustomApplication)

   var IdTCPServer: TIdTCPServer;

   protected
    procedure DoRun; override;
    procedure ServerOnConnect(AContext: TIdContext);
    procedure ServerOnExecute(AContext: TIdContext);

  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
  end;

  type
    TLog = class(TIdNotify)
    protected
      FMsg: string;
      procedure DoNotify; override;
    public
      class procedure LogMsg(const AMsg: string);
    end;

{ TMyApplication }

    procedure TLog.DoNotify;
    var i:integer;
    begin
     writeln(FMsg);
    end;

    class procedure TLog.LogMsg(const AMsg: string);
    begin
      with TLog.Create do
      try
        FMsg := AMsg;
        Notify;
      except
        Free;
        raise;
      end;
    end;

procedure TMyApplication.ServerOnExecute(AContext: TIdContext);
begin
  TLog.LogMsg('test OnExecute'); // the message is not displayed
end;

procedure TMyApplication.ServerOnConnect(AContext: TIdContext);
begin
 TLog.LogMsg('connect');        // the message is not displayed
end;

procedure TMyApplication.DoRun;
begin

  TLog.LogMsg('test main 1'); //the message is displayed
  IdTCPServer := TIdTCPServer.Create;
  try
    //Server.Name := 'Server';
    IdTCPServer.ListenQueue := 15;
    IdTCPServer.MaxConnections := 0;
    IdTCPServer.TerminateWaitTime := 5000;
    IdTCPServer.Bindings.Add.IP   := '0.0.0.0';
    IdTCPServer.Bindings.Add.Port := 80;
    IdTCPServer.Bindings.Add.IPVersion:=Id_IPv4;
    IdTCPServer.OnConnect := ServerOnConnect;
  //  IdTCPServer.OnDisconnect := ServerOnDiconnect;
    //Server.OnException := IdTCPServer1Exception;
    IdTCPServer.OnExecute := ServerOnExecute;
    IdTCPServer.Active := True;
    TLog.LogMsg('test main 2'); //the message is displayed

  finally
   // IdTCPServerCmd.Free;
  end;
  readln;

  // stop program loop
  Terminate;
end;

constructor TMyApplication.Create(TheOwner: TComponent);
begin

  inherited Create(TheOwner);
  StopOnException := True;

end;


destructor TMyApplication.Destroy;
begin
  IdTCPServer.Free;
  inherited Destroy;

end;

var
  Application: TMyApplication;
begin
  Application := TMyApplication.Create(nil);
  Application.Title := 'My Application';
  Application.Run;
  Application.Free;
end.
 

Ответ №1:

В вашем основном потоке нет цикла сообщений для обработки TThread.Synchronize() / TThread.Queue() запросов. Он заблокирован Readln() . Поскольку вы находитесь в консольном приложении, а не в приложении с графическим интерфейсом, вам необходимо периодически вручную вызывать Classes.CheckSynchronize() основной поток.


Кстати, вы звоните IdTCPServer.Bindings.Add() слишком много раз. В вашем примере вам нужно вызвать его только 1 раз, создав 1 привязку, которой присвоены 3 значения свойства. Но вместо этого вы вызываете его 3 раза, создавая 3 отдельные привязки с 3 отдельными настройками свойств. Вместо этого это должно выглядеть примерно так:

 with IdTCPServer.Bindings.Add do
begin
  IP := '0.0.0.0';
  Port := 80;
  IPVersion := Id_IPv4;
end;
 

Которые можно упростить, поскольку 0.0.0.0 и Id_IPv4 уже являются значениями по умолчанию, поэтому их можно исключить из вашего кода. И TIdTCPServer имеет DefaultPort свойство, которое вы можете использовать вместо этого. Установщик TIdTCPServer.Active свойств создаст свои собственные привязки по умолчанию, если они не определены явно.