| Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум на Исходниках.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 проходит все проверки. 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. |