Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Delphi: Сетевое программирование > IdSNTPClient странная работа


Автор: Prince 23.09.22, 15:45
Мир вам. Подскажите, может кто сталкивался.
На форму кидаю TidSNTP и таймер.
Устанавливаю свойства:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    idsntp.Host           := '192.168.0.73';
    idsntp.Port           := 1234;
    idsntp.ReciveTimeout  := 500; //1000;

Таймер срабатывает с периодом в 10 секунд.
В обработчике таймера происходит следующее:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    if idsntp.datetime <> 0 then
     begin
    //caption := inttostr(idsntp.sendtime) + ' '+inttostr(idsntp.rectime);
      statuslabel.Caption        := 'Ok';
      rounddelaylabel.Caption    := inttostr(round (idsntp.RoundTripDelay * 24 * 3600 * 1000)) + ' ms';
      adjustingtimelabel.Caption := inttostr(round (idsntp.AdjustmentTime * 24 * 3600 * 1000)) + ' ms';

Так вот, как правило, idsntp.datetime отрабатывает корректно и тогда значения RoundTripDelay лежат в интервале 4..300 мс.
Но иногда/периодически возникает странная ситуация: idsntp.datetime отрабатывает вроде бы штатно, но RoundTripDelay составляет секунды, до 10 и более, и примерно на столько же увеличивается и AdjustmentTime. Обычно начинается с того, что первый при запуске программы вызов datetime отрабатывает с ошибкой(почему-то), а вслед за этим начинает возвращать некорректные значения RoundTripDelay и AdjustmentTime. Но такая ситуация иногда возникает и в процессе...
И если datetime начинает выдавать неправильные значения, то так и продолжает. До перезапуска программы(как будто). Но иногда начинает работать как положено.
Такой вот нестабильный глюк.
Залез в компонент TidSNTPClient.
Привожу часть метода datetime:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Begin
    Result := 0.0;
     
      SetLength(LBuffer, SizeOf(TNTPGram));
      FillBytes(LBuffer, SizeOf(TNTPGram), $00);
     
      LBuffer[0] := $1B;
      DateTimeToNTP(Now, LNTPDataGram.Xmit1, LNTPDataGram.Xmit2);
      CopyTIdUInt32(GStack.HostToNetwork(LNTPDataGram.Xmit1), LBuffer, 40);
      CopyTIdUInt32(GStack.HostToNetwork(LNTPDataGram.Xmit2), LBuffer, 44);
      //sendtime:=gettickcount;
      SendBuffer(LBuffer);
      //sendtime:=gettickcount-sendtime;
      //rectime:=gettickcount;
      ReceiveBuffer(LBuffer);
      //rectime:=gettickcount-rectime;
     
    // ...
     
    // corrected as per RFC 2030 errata
        FRoundTripDelay := (FDestinationTimestamp - FOriginateTimestamp) -
          (FTransmitTimestamp - FReceiveTimestamp);
     
        FLocalClockOffset := ((FReceiveTimestamp - FOriginateTimestamp) +
          (FTransmitTimestamp - FDestinationTimestamp)) / 2;

Выяснил, что в случае правильной работы компонента, метод SendBuffer отрабатывает "мгновенно", а ReciveBuffer в течении 20..300 мс и фактически RoundTripDelay - это "время срабатывания" ReciveBuffer. Когда происходит глюк, вижу что ReciveBuffer начинает отрабатывать так же "мгновенно", как и SendBuffer. Ощущение, что где-то в приемном буфере остаётся мусор от предыдущего вызова ReciveBuffer и именно этот "хвост" ReciveBuffer читает. Но при этом возвращает ненулевое (и корректное) значение времени, то есть прочитанный пакет NTP проходит все проверки. :scratch:
Uлюк происходит где-то в недрах родительского класса. Что это и как от него избавиться. :-?

Я пока не пробовал создавать и уничтожать экземпляр TidSNTP в рантайме. Может быть это исправит ситуацию, но я хочу понять. Может, я что-то не так делаю.

Автор: macomics 23.09.22, 16:04
Тогда попробуйте в этот метод добавить дополнительный локальный буфер, в который скопируете значения исходные, а получение уже может сработать на сохраняемом глобальном LBuffer предварительно обнуленном.

И еще: А ничего, что длина у этих объектов разная?
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      CopyTIdUInt32(GStack.HostToNetwork(LNTPDataGram.Xmit1), LBuffer, 40);
      CopyTIdUInt32(GStack.HostToNetwork(LNTPDataGram.Xmit2), LBuffer, 44);

Автор: Prince 23.09.22, 16:41
Это не размер, а индекс в буфере пакета по логике.
Не понял про локальный буфер.
Попробую просто обнулить буфер перед вызовом ReciveBuffer. Хотя в случае глюка тогда я скорее всего буду получать ошибку datetime.
Сейчас попробую.

Добавлено
Обнуление ничего не даёт. Глюк где-то в недрах родительских классов. Точнее, даёт, глюк стал появляться чаще.

Автор: macomics 23.09.22, 16:48
В локальных переменных объявите еще один SBuffer, обнулите его и используйте для отправки через SendBuffer. А для ReceiveBuffer оставьте этот LBuffer.

Добавлено
У меня просто нету под рукой сейчас этого класса в Lazarus. Я бы сам проверил в чем дело.

Автор: Prince 23.09.22, 17:09
Кажется, он вылечился. Перед вызовом datetime устанавливаю IdSNTP.Active := true, а после сбрасываю. Таким образом, сокет инициализируется при каждом вызове datetime где-то там внутри. Так вроде работает. Тогда и создание TIdSNTP в рантайме будет иметь такой же эффект, по идее.
Увеличил таймаут по приему до 2000 мс, теперь и первый вызов отрабатывает как будто нормально. Будем посмотреть.

Да, глюк пропал. А таймаут лучше увеличить секунд до 5.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)