Indy 10.6, использующий tcpserver в системе Linux с правами администратора, выдает Gtk-WARNING при отключении клиента

Я настраиваю новую систему, используя Indy 10.6 tcpserver на Raspberry PI с последней загруженной версией Raspbian. Я запускаю приложение с рабочего стола с графическим интерфейсом через скрипт терминала bash с помощью sudo. Все работает нормально, пока клиент не подключится, затем, когда он отключается, я получаю Gtk-WARNINGs, а иногда Gtk-CRITICALs, и я не знаю, почему. Вот мой код, он разрешает только одно клиентское соединение за раз, затем деактивирует сервер и перезапускает его после каждого соединения:

Procedure TFK20Elevator.ASpeedBtn1Click(Sender: TObject);
Begin //start the server
  Server.Active := False;
  Server.Bindings.Clear;
  Server.Bindings.Add.IPVersion := Id_IPv4;
  Server.Bindings.Add.IP := LIP;
  Server.Bindings.Add.Port := DefPort + StrToIntDef(UnitID, 0);
  Try
    Server.Active := True;
  Except
    On E: Exception Do
      Memo1.Lines.Add(E.Message);
  End;
  If Not Server.Active Then
    Exit;
  ASpeedBtn1.Enabled := False;
  ASpeedBtn2.Enabled := True;
  AStatus1.SimpleText := 'Server bound to ' + LIP + ':' + IntToStr(DefPort + StrToIntDef(UnitID, 0));
End;

Procedure TFK20Elevator.ServerConnect(AContext: TIdContext);
Begin
  If Connected Then
    Begin
      Abort();
      Exit;
    End;
  AStatus1.SimpleText := 'Connecting to> ' + AContext.Binding.PeerIP + ' - Authenticating...';
  Memo1.Lines.Clear;
  Manager := False;
  EncDecSIdx := 1;
  RetryTimer.Enabled := False;
  RetryTimer.Interval := 3000;
  Authenticating := True;
  AuthTimer.Enabled := True;
  StayAlive.Enabled := True;
End;

Procedure TFK20Elevator.ServerException(AContext: TIdContext; AException: Exception);
Begin
  If AnsiContainsText(AException.Message, 'Gracefully') Then
    AStatus1.SimpleText := 'Server bound to ' + LIP + ':' + IntToStr(DefPort + StrToIntDef(UnitID, 0)) //closed gracefully message
  Else
    Begin //show the exception
      Memo1.Lines.Add('An exception happend! - ' + AException.Message);
      RetryTimer.Enabled := True;
    End;
  Manager := False;
  Authenticating := False;
End;

Procedure TFK20Elevator.ServerExecute(AContext: TIdContext);
//EncStr and DecStr simply encode/decode, respectively, a standard
//  string into/from a key encrypted hex string, i.e. '00' to 'FF'
//  for each character in the string
Var
  S, UserName, Password: String;
  I, N: Integer;
Begin
  S := AContext.Connection.IOHandler.ReadLn(IndyTextEncoding_OSDefault, IndyTextEncoding_OSDefault); //get the data
  If S = Heart Then //if message is the client heart beat, return to client
    Begin //just a heart beat, reset timer
      StayAlive.Enabled := False;
      AContext.Connection.IOHandler.WriteLn(Heart, IndyTextEncoding_OSDefault, IndyTextEncoding_OSDefault);
      StayAlive.Enabled := True;
      Exit;
    End;
  S := PCommon.DecStr(S, EncDecStr, EncDecSIdx); //not heart beat, decompress
  If Authenticating Then
    Begin //test log in
      If Length(S) > 3 Then
        Begin
          I := Pos('|', S);
          If (I > 1) And (Length(S) > I) Then
            Begin
              UserName := Copy(S, 1, I - 1);
              Password := Copy(S, I + 1, Length(S) - I);
              If UserName = ManUser Then
                Begin
                  If Password = ManPass Then
                    Begin
                      AuthTimer.Enabled := False;
                      Manager := True;
                      Authenticating := False;
                      AContext.Connection.IOHandler.WriteLn(EncStr(AContext.Binding.PeerIP +
                                                            ':' + IntToStr(DefPort + StrToIntDef(UnitID, 0)) + 'M',
                                                            EncDecStr, EncDecSIdx), IndyTextEncoding_OSDefault,
                                                            IndyTextEncoding_OSDefault);
                      AStatus1.SimpleText := 'Connecting to> ' + AContext.Binding.PeerIP + ' as Manager';
                      Connected := True;
                    End
                  Else
                    AuthTimerTimer(Self);
                End
              Else If UserName = GenUser Then
                Begin
                  If Password = GenPass Then
                    Begin
                      AuthTimer.Enabled := False;
                      Authenticating := False;
                      AContext.Connection.IOHandler.WriteLn(EncStr(AContext.Binding.PeerIP +
                                                            ':' + IntToStr(DefPort + StrToIntDef(UnitID, 0)) + 'U',
                                                            EncDecStr, EncDecSIdx), IndyTextEncoding_OSDefault,
                                                            IndyTextEncoding_OSDefault);
                      AStatus1.SimpleText := 'Connecting to> ' + AContext.Binding.PeerIP + ' as General User';
                      Connected := True;
                    End
                  Else
                    AuthTimerTimer(Self);
                End
              Else
                AuthTimerTimer(Self);
            End
          Else
            AuthTimerTimer(Self);
        End
      Else
        AuthTimerTimer(Self);
    End
  Else
    Begin //test for commands
      If Copy(S, 1, Length(AssignID)) = AssignID Then
        Begin //command to assign a new unit id
          NewLoc := DefLocation;
          NewUnit := DefUnitNum;
          I := Pos('-', S, 1);
          If (I > 0) And (I < Length(S)) Then
            Begin
              N := Pos('-', S, I + 1);
              If (N > 0) And (N < Length(S)) Then
                Begin
                  NewLoc := Copy(S, I + 1, N - I - 1);
                  NewUnit := Copy(S, N + 1, Length(S) - N);
                End;
            End;
          Label15.Caption := NewLoc;
          Label16.Caption := NewUnit;
          FmtStr(LIP, '%.3d', [StrToInt(NewUnit)]);
          LIP := '192.168.6' + Copy(LIP, 1, 1) + '.' + Copy(LIP, 2, 2); //wifi ip
          Memo1.Lines.Add('--> ' + S + '-' + LIP);
          AContext.Connection.IOHandler.WriteLn(PCommon.EncStr(Rebooting, EncDecStr, EncDecSIdx),
                                                IndyTextEncoding_OSDefault, IndyTextEncoding_OSDefault);
          Memo1.Lines.Add('<-- ' + Rebooting);
          TestTimer.Enabled := True;
        End;
    End;
End;

Procedure TFK20Elevator.ASpeedBtn2Click(Sender: TObject);
Begin //shut down the server with optional restart if not rebooting
  AuthTimer.Enabled := False;
  RetryTimer.Enabled := False;
  StayAlive.Enabled := False;
  TestTimer.Enabled := False;
  DropClient;
  Try
    Server.Active := False;
  Except
    On E: Exception Do
      Memo1.Lines.Add('Error disconnecting server - ' + E.Message);
  End;
  If Server.Active Then
    Exit;
  ASpeedBtn1.Enabled := True;
  ASpeedBtn2.Enabled := False;
  AStatus1.SimpleText := 'Server not running...';
  Manager := False;
  Authenticating := False;
  Connected := False;
  RetryTimer.Enabled := Not SysReboot;
End;

Procedure TFK20Elevator.ServerDisconnect(AContext: TIdContext);
Begin
  StayAlive.Enabled := False;
  RetryTimer.Enabled := False;
  DropClient;
  AStatus1.SimpleText := 'Client disconnected...';
  Manager := False;
  Authenticating := False;
  Connected := False;
  RetryTimer.Enabled := Not SysReboot;
End;

Procedure TFK20Elevator.DropClient; //make sure buffers are cleared
Var
  I: Integer;
  SC: TIdContext;
Begin
  If Server.Active Then
    Begin
      Application.ProcessMessages;
      With Server.Contexts.LockList Do
        Try
          Memo1.Lines.Add('Disconnecting...');
          For I := Count - 1 DownTo 0 Do
            Begin
              SC := TIdContext(Items[I]);
              If SC = Nil Then
                Continue;
              SC.Connection.IOHandler.WriteBufferClear;
              SC.Connection.IOHandler.InputBuffer.Clear;
              SC.Connection.IOHandler.Close;
              If SC.Connection.Connected Then
                SC.Connection.Disconnect;
              Memo1.Lines.Add('Disconnecting client ' + IntToStr(I + 1) + ' of ' + IntToStr(Count));
            End;
        Finally
          Server.Contexts.UnlockList;
          Memo1.Lines.Add('Disconnected');
        End;
    End;
End;

Procedure TFK20Elevator.StayAliveTimer(Sender: TObject);
Begin //server reset timer if client stops sending heart beat
  StayAlive.Enabled := False;
  AStatus1.SimpleText := 'Client timed out!';
  If ASpeedBtn2.Enabled Then
    ASpeedBtn2Click(Self);
End;

Procedure TFK20Elevator.AuthTimerTimer(Sender: TObject);
Begin //login authorization timeout timer
  AuthTimer.Enabled := False;
  ASpeedBtn2Click(Self);
  Application.ProcessMessages;
  ASpeedBtn1Click(Self);
End;

person user7475089    schedule 26.01.2017    source источник
comment
Также получите много Pango-CRITICALs. Пробовал отключать DropClient в ServerDisconnect, но это не помогло.   -  person user7475089    schedule 26.01.2017
comment
Некоторые из сообщений:   -  person user7475089    schedule 26.01.2017
comment
Pango-CRITICAL **: pango_layout_get_context: утверждение 'layout != NULL' не удалось Pango-CRITICAL **: pango_context_get_language: утверждение 'context != NULL' не удалось Pango-CRITICAL **: pango_context_get_metrics: утверждение 'PANGO_IS_CONTEXT (контекст)' не удалось Pango- КРИТИЧЕСКИЙ **: pango_font_metrics_get_ приблизительно_char_width: утверждение «метрики != NULL» не удалось   -  person user7475089    schedule 26.01.2017
comment
Клиентская программа фактически работает под Windows 10, написана на Delphi 7, а также с использованием Indy 10.6. После подтверждения входа клиент отправляет простой код сердцебиения каждые 3 секунды и сбрасывает соединение, если сервер не возвращает код.   -  person user7475089    schedule 26.01.2017
comment
Так же 2-е условие, если клиент подключается, и резко отключается (сбрасываю программу в режим отладки), то сервер мне сигнализирует, что время ожидания клиента истекло, но потом программа зависает.   -  person user7475089    schedule 26.01.2017
comment
он разрешает только одно клиентское соединение за раз, затем деактивирует сервер и перезапускает его после каждого соединения - TIdTCPServer на самом деле не предназначен для использования таким образом. Если вы хотите, чтобы одновременно подключался только один клиент, установите для свойства TIdTCPServer.MaxConnections значение 1 или рассмотрите возможность использования вместо него TIdSimpleServer.   -  person Remy Lebeau    schedule 27.01.2017
comment
Спасибо, я рассмотрю возможность SimpleServer. Дайте мне знать, если у вас есть какие-либо идеи о причине проблем.   -  person user7475089    schedule 27.01.2017
comment
У меня много идей, я пишу ответ.   -  person Remy Lebeau    schedule 27.01.2017
comment
Спасибо, Реми, я очень ценю все ваши усилия, ваши ответы другим действительно помогли мне зайти так далеко. Проверил IdSimpleServer, но, похоже, нет никакой информации/практических примеров да :(   -  person user7475089    schedule 27.01.2017
comment
У меня нет конкретных примеров. Но в двух словах, это просто TIdTCPConnection с добавленными (Begin)Listen() методами. Вызовите Listen(), чтобы дождаться подключения клиента, а затем при необходимости используйте унаследованные TIdTCPConnection свойства/методы. В отличие от TIdTCPServer, TIdSimpleServer не является многопоточным, поэтому вам придется запускать его в своем собственном потоке. Но это делает одноклиентские серверы намного проще в использовании, чем TIdTCPServer.   -  person Remy Lebeau    schedule 27.01.2017


Ответы (1)


 Server.Bindings.Add.IPVersion := Id_IPv4;
 Server.Bindings.Add.IP := LIP;
 Server.Bindings.Add.Port := DefPort + StrToIntDef(UnitID, 0);

Это ошибка в вашем коде. Вы не открываете только 1 прослушивающий сокет, вы фактически открываете 3 прослушивающих сокета! Каждый вызов Bindings.Add() будет указывать TIdTCPServer на создание отдельного прослушивающего сокета, и каждый объект Binding имеет свои собственные настройки IP/порта.

Что вы действительно делаете с приведенным выше кодом:

  1. создание привязки IPv4 к IP 0.0.0.0 на порту TIdTCPServer.DefaultPort.

  2. создание еще одной привязки по IP LIP на порту TIdTCPServer.DefaultPort с использованием IP-версии Indy по умолчанию (которая оказывается IPv4, если только вы не перекомпилируете Indy с IdIPv6, определенным в IdCompilerDefines.inc).

  3. создание еще одной привязки по IP 0.0.0.0 или ::1 по порту DefPort+UnitID в зависимости от IP-версии Indy по умолчанию.

Для того, что вы пытаетесь сделать, вам нужно вызвать Bindings.Add() только один раз, например:

var
  Binding : TIdSocketHandle;

Binding := Server.Bindings.Add;
Binding.IPVersion := Id_IPv4;
Binding.IP := LIP;
Binding.Port := DefPort + StrToIntDef(UnitID, 0);

При этом TIdTCPServer является многопоточным компонентом. Его различные события (OnConnect, OnDisconnect, OnExecute, OnException и OnListenException) запускаются в контексте рабочих потоков, созданных внутри TIdTCPServer. Ваши обработчики событий получают прямой доступ к элементам управления пользовательского интерфейса вне контекста основного потока пользовательского интерфейса. Это вызывает всевозможные проблемы, и этого никогда нельзя делать.

Если вашим обработчикам событий требуется доступ к вашему пользовательскому интерфейсу, они должны синхронизироваться с основным потоком пользовательского интерфейса, например, с TThread.Synchronize() или TThread.Queue(), или собственным классом Indy TIdSync или TIdNotify, или любым другим механизмом синхронизации между потоками по вашему выбору.

Кроме того, когда вы вручную отбрасываете клиентов с помощью DropClient(), он делает с контекстами вещи, которые ему не нужны. В любом случае вам даже не нужно удалять клиентов вручную, так как TIdTCPServer сделает это за вас, пока он деактивируется.

С учетом всего сказанного попробуйте что-то еще вроде этого:

interface

uses
  Classes, Form, SysUtils, StdCtrls, ExtCtrls, Buttons, IdTCPServer, IdContext;

type
  TFK20Elevator = class(TForm)
    Server: TIdTCPServer;
    ASpeedBtn1: TSpeedButton;
    ASpeedBtn2: TSpeedButton;
    Memo1: TMemo;
    AStatus1: TStatusBar;
    AuthTimer: TTimer;
    RetryTimer: TTimer;
    StayAlive: TTimer;
    TestTimer: TTimer;
    ...
    procedure ASpeedBtn1Click(Sender: TObject);
    procedure ASpeedBtn2Click(Sender: TObject);
    procedure StayAliveTimer(Sender: TObject);
    procedure AuthTimerTimer(Sender: TObject);
    procedure ServerConnect(AContext: TIdContext);
    procedure ServerDisconnect(AContext: TIdContext);
    procedure ServerException(AContext: TIdContext; AException: Exception);
    procedure ServerExecute(AContext: TIdContext);
    ...
  private
    DefPort: Integer;
    UnitID: string;
    Manager: Boolean;
    Authenticating: Boolean;
    EncDecSIdx: Integer;
    ...
    procedure DropClient;
    procedure ConnectedNotify(const APeerIP: string);
    procedure DisconnectedNotify;
    procedure ErrorNotify(const AMessage: string);
    procedure HeartNotify;
    procedure ManagerLoggedInNotify(const APeerIP: string);
    procedure GeneralUserLoggedInNotify(const APeerIP: string);
    procedure FailedAuthNotify;
    procedure RebootNotify(const Data: string);
    ...
  end;

var
  FK20Elevator: TFK20Elevator;

implementation

uses
  IdGlobal, IdSync;

const
  Heart: string = ...;
  AssignID: string = ...;
  ...

procedure TFK20Elevator.ASpeedBtn1Click(Sender: TObject);
var
  Binding: TIdSocketHandle;
begin
  //start the server

  Server.Active := False;
  Server.Bindings.Clear;
  Binding := Server.Bindings.Add;
  Binding.IPVersion := Id_IPv4;
  Binding.IP := LIP;
  Binding.Port := DefPort + StrToIntDef(UnitID, 0);
  Server.MaxConnections := 1;

  try
    Server.Active := True;
  except
    on E: Exception do
    begin
      Memo1.Lines.Add('Error activating server - ' + E.Message);
      Exit;
    end;
  end;

  AStatus1.SimpleText := 'Server bound to ' + Binding.IP + ':' + IntToStr(Binding.Port);

  ASpeedBtn1.Enabled := False;
  ASpeedBtn2.Enabled := True;
end;

procedure TFK20Elevator.ASpeedBtn2Click(Sender: TObject);
begin
  //shut down the server with optional restart if not rebooting

  AuthTimer.Enabled := False;
  RetryTimer.Enabled := False;
  StayAlive.Enabled := False;
  TestTimer.Enabled := False;

  try
    Server.Active := False;
  except
    on E: Exception do
    begin
      Memo1.Lines.Add('Error deactivating server - ' + E.Message);
      Exit;
    end;
  end;

  Manager := False;
  Authenticating := False;

  AStatus1.SimpleText := 'Server not running...';
  ASpeedBtn1.Enabled := True;
  ASpeedBtn2.Enabled := False;

  RetryTimer.Enabled := not SysReboot;
end;

procedure TFK20Elevator.StayAliveTimer(Sender: TObject);
begin
  //client stopped sending heart beats
  StayAlive.Enabled := False;
  Memo1.Lines.Add('Client timed out!');
  DropClient;
end;

procedure TFK20Elevator.AuthTimerTimer(Sender: TObject);
begin
  //login authorization timeout
  AuthTimer.Enabled := False;
  Memo1.Lines.Add('Authentication timed out!');
  DropClient;
end;

procedure TFK20Elevator.DropClient;
begin
  with Server.Contexts.LockList do
  try
    if Count > 0 then
      TIdContext(Items[0]).Connection.Disconnect;        
  finally
    Server.Contexts.UnlockList;
  end;
end;

type
  TMyNotifyMethod = procedure(const AStr: string) of object;

  TMyNotify = class(TIdNotify)
  protected
    FMethod: TMyNotifyMethod;
    FStr: string;
    procedure DoNotify; override;
  public
    class procedure NotifyStr(AMethod: TMyNotifyMethod; const AStr: string);
  end;

procedure TMyNotify.DoNotify;
begin
  FMethod(FStr);
end;

class procedure TMyNotify.NotifyStr(AMethod: TMyNotifyMethod; const AStr: string);
begin
  with Create do
  begin
    FMethod := AMethod;
    FStr := AStr;
    Notify;
  end;
end;

procedure TFK20Elevator.ConnectedNotify(const APeerIP: string);
begin
  if not Server.Active then Exit;
  AStatus1.SimpleText := 'Connecting to> ' + APeerIP + ' - Authenticating...';
  Memo1.Lines.Clear;
  RetryTimer.Enabled := False;
  RetryTimer.Interval := 3000;
  AuthTimer.Enabled := True;
  StayAlive.Enabled := True;
end;

procedure TFK20Elevator.DisconnectedNotify;
begin
  StayAlive.Enabled := False;
  RetryTimer.Enabled := False;

  if Server.Active then
  begin
    with Server.Bindings[0] do
      AStatus1.SimpleText := 'Client Disconnected. Server bound to ' + IP + ':' + IntToStr(Port);
  end;

  RetryTimer.Enabled := Not SysReboot;
end;

procedure TFK20Elevator.ErrorNotify(const AMessage: string);
begin
  Memo1.Lines.Add('An exception happened! - ' + AMessage);
  RetryTimer.Enabled := True;
end;

procedure TFK20Elevator.HeartNotify;
begin
  StayAlive.Enabled := False;
  StayAlive.Enabled := True;
end;

procedure TFK20Elevator.ManagerLoggedInNotify(const APeerIP: string);
begin
  AuthTimer.Enabled := False;
  AStatus1.SimpleText := 'Connecting to> ' + APeerIP + ' as Manager';
end;

procedure TFK20Elevator.GeneralUserLoggedInNotify(const APeerIP: string);
begin
  AuthTimer.Enabled := False;
  AStatus1.SimpleText := 'Connecting to> ' + APeerIP + ' as General User';
end;

procedure TFK20Elevator.FailedAuthNotify;
begin
  //login authorization failed
  AuthTimer.Enabled := False;
end;

procedure TFK20Elevator.RebootNotify(const Data: string);
var
  Tmp, S, NewLoc, NewUnit, LIP: string;
begin
  Tmp := Data;

  S := Fetch(Tmp, #10);
  NewLoc := Fetch(Tmp, #10);
  NewUnit := Tmp;

  Label15.Caption := NewLoc;
  Label16.Caption := NewUnit;

  FmtStr(LIP, '%.3d', [StrToInt(NewUnit)]);
  LIP := '192.168.6' + Copy(LIP, 1, 1) + '.' + Copy(LIP, 2, 2); //wifi ip

  Memo1.Lines.Add('--> ' + S + '-' + LIP);
  Memo1.Lines.Add('<-- ' + Rebooting);

  TestTimer.Enabled := True;
end;

procedure TFK20Elevator.ServerConnect(AContext: TIdContext);
begin
  Manager := False;
  Authenticating := True;
  EncDecSIdx := 1;

  TMyNotify.NotifyStr(@ConnectedNotify, AContext.Binding.PeerIP);

  // Note: OSDefault is platform-specific. On Linux, it is UTF-8, so
  // you should use UTF-8 explicitly instead, so as to provide
  // better compatibility across platforms, especially if you ever
  // move this server code to another platform in the future...
  //
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_OSDefault; // IndyTextEncoding_UTF8
  AContext.Connection.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault; // IndyTextEncoding_UTF8
end;

procedure TFK20Elevator.ServerDisconnect(AContext: TIdContext);
begin
  Manager := False;
  Authenticating := False;
  TIdNotify.NotifyMethod(@DisconnectedNotify);
end;

procedure TFK20Elevator.ServerException(AContext: TIdContext; AException: Exception);
begin
  if not (AException is EIdConnClosedGracefully) then
    TMyNotify.NotifyStr(@ErrorNotify, AException.Message);
end;

procedure TFK20Elevator.ServerExecute(AContext: TIdContext);
var
  S, Tmp, UserName, Password: String;
begin
  S := AContext.Connection.IOHandler.ReadLn; //get the data
  if S = Heart then
  begin
    //just a heart beat, return to client and reset timer
    AContext.Connection.IOHandler.WriteLn(Heart);
    TIdNotify.NotifyMethod(@HeartNotify);
    Exit;
  end;

  //not heart beat, decompress
  //EncStr and DecStr simply encode/decode, respectively, a standard
  //  string into/from a key encrypted hex string, i.e. '00' to 'FF'
  //  for each character in the string

  S := PCommon.DecStr(S, EncDecStr, EncDecSIdx);

  if Authenticating then
  begin
    //test log in
    UserName := Fetch(S, '|');
    Password := S;

    if (UserName = ManUser) and (Password = ManPass) then
    begin
      Authenticating := False;
      Manager := True;
      AContext.Connection.IOHandler.WriteLn(EncStr(AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.Port) + 'M', EncDecStr, EncDecSIdx));
      TMyNotify.NotifyStr(@ManagerLoggedInNotify, AContext.Binding.PeerIP);
    end
    else if (UserName = GenUser) and (Password = GenPass) then
    begin
      Authenticating := False;
      AContext.Connection.IOHandler.WriteLn(EncStr(AContext.Binding.PeerIP + ':' + IntToStr(AContext.Binding.Port) + 'U', EncDecStr, EncDecSIdx));
      TMyNotify.NotifyStr(@GeneralUserLoggedInNotify, AContext.Binding.PeerIP);
    end else
    begin
      TIdNotify.NotifyMethod(@FailedAuthNotify);
      AContext.Connection.Disconnect;
    end;
    Exit;
  end;

  //test for commands
  if TextStartsWith(S, AssignID) then
  begin
    //command to assign a new unit id

    Tmp := S;
    Fetch(Tmp, '-');
    NewLoc := Fetch(Tmp, '-');
    NewUnit := Tmp;

    if (NewLoc = '') or (NewUnit = '') then
    begin
      NewLoc := DefLocation;
      NewUnit := DefUnitNum;
    end;

    AContext.Connection.IOHandler.WriteLn(PCommon.EncStr(Rebooting, EncDecStr, EncDecSIdx));

    TMyNotify.NotifyStr(@RebootNotify, S + #10 + NewLoc + #10 + NewUnit);
  end;
end;

Наконец, я бы посоветовал вам избавиться от всех ваших глобальных переменных и таймеров сердцебиения/аутентификации из основного потока пользовательского интерфейса. Вместо этого обработайте тайм-аут внутри самого события OnExecute, например, используя свойство клиента ReadTimeout и/или метод CheckForDataOnSource(). Используйте свойство TIdContext.Data (или создайте новый класс из TIdServerContext и назначьте его свойству TIdTCPServer.ContextClass), чтобы отслеживать значения для каждого соединения, например, когда в последний раз было получено сердцебиение или выполняется ли аутентификация клиента (на самом деле вам следует обрабатывать аутентификацию в OnConnect еще до того, как OnExecute начнет работать), или если клиент вошел в систему как менеджер, и т. д. Это уменьшит количество вещей, которые необходимо синхронизировать с основным потоком пользовательского интерфейса, и избежит любых проблем с синхронизацией, вызванных задержки в обработке синхронизации.

person Remy Lebeau    schedule 27.01.2017
comment
Отлично, спасибо, Реми, завтра первым делом применю и дам вам знать. - person user7475089; 27.01.2017
comment
Я изменил код и многому научился, но я не знаком с использованием подобных классов для уведомлений и не знаю, как их описать для компилятора, поэтому при компиляции он сообщает мне об ошибке: ожидается идентификатор класса на каждой из процедур уведомления. - person user7475089; 27.01.2017
comment
@ user7475089 у вас есть класс TFK20Elevator. Вы добавили дополнительные методы в объявление этого класса в interface модуля? - person Remy Lebeau; 27.01.2017
comment
Я добавил это в интерфейс после использования: Type TMyNotifyMethod = procedure(Const AStr: String) Of Object; TMyNotify = Class(TIdNotify) Защищенный метод FM: TMyNotifyMethod; FStr: Строка; Процедура DoNotify; переопределить; Процедура открытого класса NotifyStr(AMethod: TMyNotifyMethod; Const AStr: String); Конец; { TFK20Elevator } TFK20Elevator = Класс (TForm) - person user7475089; 27.01.2017
comment
Затем это в частном порядке: Процедура ConnectedNotify (Const APeerIP: String); Процедура DisconnectedNotify; Процедура ErrorNotify(Const AMessage: String); Процедура HeartNotify; Процедура ManagerLoggedInNotify(Const APeerIP: String); Процедура GeneralUserLoggedInNotify(Const APeerIP: String); Процедура FailedAuthNotify; Процедура RebootNotify(Const Data: String); - person user7475089; 27.01.2017
comment
Я обновил свой ответ, чтобы показать, как он должен выглядеть. - person Remy Lebeau; 27.01.2017
comment
Нашел ошибку, в ConnectedNotify нет буквы T перед FK20Elevator, идем дальше... - person user7475089; 27.01.2017
comment
Теперь он выдает ошибку компилятора для слишком большого количества параметров для операторов NotifyStr, то есть TMyNotify.NotifyStr(ConnectedNotify, AContext.Binding.PeerIP); выделяет ConnectedNotify с этой ошибкой: Ошибка: неправильное количество параметров, указанных для ConnectedNotify - person user7475089; 27.01.2017
comment
Поскольку я компилирую для Linux под Lazarus, я думаю, что перед именами процедур (@ConnectedNotify) нужен @. - person user7475089; 27.01.2017
comment
Это сделало это, Реми, ты молодец, спасибо за всю помощь :) - person user7475089; 27.01.2017