Индийское событие TCPClient OnDisconnect не работает - PullRequest
3 голосов
/ 17 августа 2011
type
  TForm8 = class(TForm)
    idtcpclnt1: TIdTCPClient;
    idtcpsrvr1: TIdTCPServer;
    procedure FormCreate(Sender: TObject);
    procedure idtcpsrvr1Execute(AContext: TIdContext);
    procedure idtcpclnt1Disconnected(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form8: TForm8;

implementation

{$R *.dfm}

procedure TForm8.FormCreate(Sender: TObject);
begin
  idtcpclnt1.Connect;
end;

procedure TForm8.idtcpsrvr1Execute(AContext: TIdContext);
begin
  AContext.Connection.Disconnect(true); //this gets called
end;

procedure TForm8.idtcpclnt1Disconnected(Sender: TObject);
begin
  ShowMessage('true'); //but this does not
end;

OnDC никогда не обрабатывается.Зачем?

Ответы [ 3 ]

11 голосов
/ 19 августа 2011

Клиентские компоненты Indy не управляются событиями (с несколькими исключениями, такими как TIdTelnet).Событие TIdTCPClient.OnDisconnect НЕ запускается, когда сервер отключается на своем конце, как вы и предполагали.Это по замыслу.TIdTCPClient не будет знать об отключении до тех пор, пока не попытается снова получить доступ к сокету, в это время произойдет исключение, такое как EIdConnClosedGracefully.Событие TIdTCPClient.OnDisconnect вызывается только при вызове метода TIdTCPClient.Disconnect() на стороне клиента, чего вы не делаете.

Чтобы обнаружить разрывы на стороне сервера с помощью TIdTCPClient, необходимо прочитатьпериодически из сокета, например в таймере или отдельном потоке.

1 голос
/ 07 августа 2013

Вы можете сделать клиент ОПРОС для отключения, добавив подпрограмму таймера к клиенту - это ПРОСТОЙ способ.

procedure TForm1.Timer1Timer(Sender: TObject);
   begin                    
      idTCPClient1.Connected;  // Works in Indy for Delphi XE4
   // Be aware this is a property read with side effects
   // It shouldn't get optimized out, but if it does, 
   // then add the appropriate directives to prevent that.
   end;                    

Это должно привести к тому, что код будет вести себя так же, как и старый TClientSocket (и как это делает TidTelnet). Он генерирует флаг hsDisconnected для события OnStatus, если сервер внезапно исчезает (т. Е. Как только процедура таймера запуска обнаруживает это). Однако этот особый случай потери сервера, приводящей к отключению, НЕ запускает событие OnDisconnect - только OnStatus. Поэтому, вероятно, лучше всегда использовать OnStatus для перехвата всех отключений, будь то клиент или сервер. Я использовал таймер, установленный на 100 мс, но я думаю, что вы можете сделать его так часто или медленно, как вы хотите - он, похоже, не приносит никакого вреда.

ПРИМЕЧАНИЕ: ДЛЯ DELPHI 7 (и, возможно, других версий от D7 до XE4) вам придется сделать это немного по-другому:

procedure TForm1.Timer1Timer(Sender: TObject);
   begin                    
   // This no longer works this way in Indy for XE4, but works in Indy for D7 ... 
      idTCPClient1.CheckForGracefulDisconnect(FALSE);  
   end; 

Кстати - если вы используете Delphi 6, забудьте о Indy, тогда он был слишком глючным.

1 голос
/ 17 августа 2011

Хорошо, моя ошибка.Ваш код не работает, но это правильно.Позвольте мне объяснить, почему:

AContext.Connection.Disconnect(true) вызывает метод DisconnectNotifyPeer, который не реализован в TCPServer.Зачем?Потому что это не должно.

Когда вы отключаетесь от сервера, indy делает недействительным сокет и закрывает его.Клиент заметит, что сервер отключен только при попытке отправить какой-либо запрос.И ваш код не делает этого.Это стандартное поведение indy.

Чтобы уведомить клиента о том, что сервер отключен, indy и любые другие пакеты должны реализовать то, что мы называем heartbeat.Heartbeat - это метод, который время от времени пытается отправить небольшие пакеты в сокет, чтобы обнаружить, что он все еще жив.Единственный способ обнаружить отключение сокета - это попытаться записать что-то в этот сокет.Гугл про heartbeat и ты поймешь о чем я.

РЕДАКТИРОВАТЬ

Проверьте этот out.

...