Основная проблема
У меня проблема с метками времени в моем приложении С#. Я получаю данные из удаленного TCP-соединения асинхронно. Каждый раз, когда я получаю данные, я обновляю переменную метки времени до DateTime.Now
. В отдельном потоке, один раз в секунду, я проверяю, было ли это больше, чем предопределенный период таймаута с момента моего последнего приема, и если это так, отключите. Этот метод работает уже много лет, но теперь у меня есть ситуация, когда приложение устанавливается на машине с неустойчивым источником времени. Каждые несколько дней машинное время "автоматически корректирует", и я преждевременно отказываюсь от подключения. Код в основном выглядит следующим образом:
Процесс получения
void OnReceiveComplete(IAsyncResult ar) {
...
mySocket.EndReceive(ar);
lastRxTime = DateTime.Now;
...
}
Проверить процесс
void CheckConnection() {
TimeSpan ts = DateTime.Now.Subtract(lastRxTime);
if(ts.TotalMilliseconds > timeout) {
Disconnect(string.Format("No packet received from the server for over {0} seconds.", timeout / 1000));
}
}
У меня есть достоверные записи Wireshark во время проблемы, и прямо перед отключением я вижу NTP-трафик, который заканчивается тем, что выглядит как исправление не менее 1 минуты. Это, очевидно, приводит к сбою процесса проверки.
Технические детали/ответы на ожидаемые вопросы
- У меня есть контроль над обоими концами соединения, но не между физическим уровнем между ними (часто спутниковая связь низкого качества). Вот почему эта проверка таймаута находится на месте.
- Поскольку данные являются асинхронными, есть условие для отправки небольшого биения, если данные не были отправлены с сервера в течение периода времени, равного половине таймаута.
- Может быть несколько экземпляров этого процесса (например, при подключении к нескольким серверам).
- Все коммуникации используют асинхронные методы (которые предположительно используют порты завершения).
- Процесс проверки запускается в отдельном потоке, который используется всеми клиентами на одной машине.
Завершенные исследования (т.е. возможные решения)
Мои поисковые запросы Google в этом месте привели к следующему:
- Я понимаю, что для повышения производительности я использовал
DateTime.UtcNow
вместоDateTime.Now
. Это не повлияет на сам вопрос. - Реализация, зависящая от Ticks, будет лучшим решением.
- Есть два варианта получения тиков -
Environment.TickCount
иStopwatch.GetTimestamp()
- Согласно моим исследованиям,
Environment.TickCount
может быть восприимчив к корректировкам времени, но я не уверен, при каких обстоятельствах. Кроме того, поскольку я использую эту же методологию в других условиях более высокой производительности, разрешение 10-16 мсек может быть проблемой (хотя и не в конкретном случае, который я представляю здесь). -
Stopwatch.GetTimestamp()
может вернуться кDateTime.Now.Ticks
, когда часы высокой производительности недоступны. Я не уверен, как часто это произойдет (какие-либо машины НЕ поставляются с часами с высокой производительностью), но я уверен, что если он прибегнет к Ticks, будет возникать одна и та же проблема. - Я также прочитал, что
Stopwatch.GetTimestamp()
будет использовать вызов APIQueryPerformanceCounter()
, и это может быть нестабильным при вызове из нескольких потоков.
Окончательный вопрос
Мне любопытно, какой лучший способ генерировать метку времени lastRxTime
будет? Я слишком беспокоюсь о проблемах с низким правдоподобием в функциях Environment.TickCount
и Stopwatch.GetTimestamp()
? Я открыт для альтернативных реализаций, если они учитывают многопоточный характер приложения, а также проблемы с качеством ссылок.
ОБНОВЛЕНИЕ 7/17/2013 (Развернуто решение!)
Я развернул решение и хочу сообщить всем о деталях. В общем, не может быть одного принятого ответа, но, пройдя этот опыт, я могу сказать, что исходное решение было, безусловно, проблемой. Я постараюсь предоставить как можно больше деталей:
Во-первых, проблема NTP на самом деле была симптомом другой проблемы. В сети, которая обнаруживает проблему, есть домен AD с двумя серверами, на которых работает мой код, настроенный как контроллеры домена. Оказывается, DC - это источники времени для домена. Также выясняется, что системное время сбрасывается с часов реального времени в этих системах на минуту примерно на 11 дней, после чего Windows исправляет проскальзывание. Как только он исправляет проскальзывание на первом DC, второй DC синхронизирует свое время и оба сталкиваются с проблемой, описанной выше.
Основываясь на обратной связи и моем первоначальном исследовании, я создал тестовую программу для запуска во время отключения для регистрации значений для DateTime.Now, Environment.TickCount и Stopwatch.GetTimestamp(). Я обнаружил, что во время коррекции ни Environment.TickCount, ни StopWatch.GetTimeStamp() вообще не соскользнули, а это значит, что они были хорошими кандидатами для замены в DateTime.Now(). Я пошел с TickCount, потому что он гарантированно будет на всех моих развернутых серверах (тогда как секундомер может вернуться к объекту DateTime на некоторых машинах, которые мне еще предстоит найти). Он работает до сих пор без проблем. Я сделал должную уловку в вопросе о переходе, чтобы предотвратить возникновение этой формы, но мне нужно будет дождаться, когда моя система будет так долго.
Я хочу отметить, что если кто-либо другой сталкивается с подобными проблемами, они не должны отказываться от использования каких-либо других представленных решений в приведенном ниже списке. Каждый из них имеет свой собственный merrit, на самом деле простой счетчик, вероятно, является лучшим решением для большинства обстоятельств. Причина, по которой я не пошел на это решение, заключалась в том, что у меня есть аналогичный код в отдельной области, который сильно зависит от жестких таймингов. я cna обрабатывает разрешение в размере 16 мсек или около того, но не может справиться с тем временем, с которым сталкиваются встречные решения (я использовал код, подобный этому, в отдельном продукте, который заканчивался дрейфом более чем через секунду в час и приносил мне из спецификации для проекта).
Опять же, спасибо всем, и если еще не придет, я обязательно обновлю вопрос.