.NET Tcp Stream завершается сбоем при простое более трех минут

У меня есть система сбора данных, которая передает данные с собирающего компьютера (сервера) на компьютер для построения графиков (клиент) через TCP-соединение. Код отлично работает при первом запуске коллекции. Код продолжает нормально работать при последующих запусках, если система не простаивает более трех минут. Если система бездействует более трех минут, передача зависает в течение первых 12 секунд или около того (достаточно долго, чтобы вызвать переполнение буферов сбора).

В начале каждой коллекции клиент входит в цикл на 30 секунд, где он проверяет каждую 1 мс, чтобы убедиться, что _clientStream.DataAvailable = true. Этот цикл выглядит примерно так (удален некоторый код проверки ошибок):

    public bool WaitForData(int maxWaitInMs)
    {
       DateTime start_time = DateTime.Now;

        while (!_clientStream.DataAvailable )
        {
            Thread.Sleep(DATA_WAIT_DELAY); //1 ms
            TimeSpan elapsed_time = DateTime.Now - start_time;
            if (elapsed_time.TotalMilliseconds > maxWaitInMs)
            {
                return false;
            }
        }
        return true;
    }

Серверная сторона просто выполняет простую запись данных фиксированной длины.

 _dataTcpServer.SendData(packet_buffer, OFFSET, packet.Size());

Когда возникает проблема, я могу сказать от отладчика, что сервер вызывает SendData один раз, возвращает, получает следующий пакет данных, снова вызывает метод отправки, а затем зависает примерно на 12 секунд при вызове SendData. В то же время клиент никогда не видит, что DataAvailable становится истинным, пока не истечет 12 секунд. Тайм-аут отправки был оставлен по умолчанию, поэтому он должен быть бесконечным. 12-секундное время, похоже, несколько связано с объемом отправленных данных.

Одна вещь, которую я сделал по-другому в этой системе, заключалась в том, что вместо создания TcpClient при запуске клиентского приложения я обновлял TcpClient, когда клиенту нужно подключиться. При отключении я избавляюсь от потока, полученного через TcpCLient.GetStream, и закрываю TcpClient.

Я просто надеюсь на некоторое понимание того, почему трехминутное бездействие имеет такой эффект (обе системы работают под управлением XP).

РЕДАКТИРОВАНИЕ – проблема решена, но понять ее не удается

На сервере я использовал отдельный поток, который использовал TcpListener.AcceptTcpClient(), чтобы позволить клиенту подключиться. AcceptTcpClient() возвращает объект TcpClient. Я периодически проверял свойство IsConnected TcpClient, чтобы знать, отключился ли клиент (т. е. закончилось ли измерение), а затем снова откладывал вызов AcceptTcpClient() и ждал начала следующего измерения. Проблема заключалась в том, что, хотя TcpClient клиента отключился, TcpClient.IsConnected сервера всегда возвращал значение true.

Я решил проблему, заставив клиента отправить сообщение (не по TCP) на сервер, а затем сервер принудительно отключился на своем конце. Теперь все работает, но я убежден, что неправильно использую этот API. Хотя я вижу методы асинхронного принятия, я не понимаю, как сервер должен узнавать, когда клиент отключается, и я не понимаю, почему свойство сервера TcpClient.IsConnected возвращает значение true, когда больше нет действующее соединение.

Также я опоздал, чтобы заметить свойство ExclusiveAddressUse, поэтому никогда не устанавливал для него значение true. Система действовала так, как если бы в течение нескольких минут было установлено другое клиентское соединение, повторно использовалось то же соединение, и сервер мог отправлять данные клиенту (т. е. как если бы клиент никогда не отключался). Если с момента отключения прошло более трех минут, новое клиентское соединение было принято (хотя, насколько я могу судить, мой код так и не вернулся к строке AcceptTcpClient()), но это уже было не то же соединение, что и сервер. отправка дальше.


person Tod    schedule 27.09.2011    source источник


Ответы (2)


Thread.Sleep является ЗЛОМ при обработке ввода-вывода. Вместо этого используйте socket.BeginReceive и вернитесь, если это все еще не работает.

person jgauffin    schedule 28.09.2011
comment
Почему Thread.Sleep по своей сути является ЗЛОМ? Это отдельный поток от пользовательского интерфейса, остальная часть системы делает очень мало. Вы предполагаете, что использование Thread.Sleep делает систему нестабильной или влияет на работу стека TCP/IP? Если это так, возможно, это объясняет кое-что о фактической проблеме, которую я обнаружил. Я собираюсь обновить свой вопрос с сегодняшними результатами. - person Tod; 29.09.2011
comment
Опрос никогда не является хорошей идеей, поскольку он занимает новый поток без причины. Конечно. Отлично работает в клиентском приложении, но не в серверном. - person jgauffin; 29.09.2011
comment
Несмотря на то, что это не было причиной моей проблемы, я ценю ваш пост. Я больше читал об асинхронных подходах. Похоже, что они действительно просто используют пул потоков, и, насколько я могу судить, вероятно, делают эквивалент Thread.Sleep(0). Я вижу, как их использование может сделать мой код чище (и более эффективным). Кроме того, ваш пост побудил меня узнать о ManualResetEvent и использовать его вместо метода опроса. Следующий шаг — начать использовать ThreadPool напрямую. - person Tod; 30.09.2011

Хотя я до сих пор не понимаю, почему мой клиент мог подключиться, когда мой сервер не ожидал AcceptTcpClient, я думаю, что понимаю, почему Connected возвращает true, даже если клиент отключился. Я копался в основных документах Socket в MSDN и увидел это:

Значение свойства Connected отражает состояние соединения на момент последней операции. Если вам нужно определить текущее состояние соединения, сделайте неблокирующий вызов Send с нулевым байтом.

Поскольку клиент знает, что нужно выйти, потому что сервер прекратил отправку данных, последняя отправка перед выходом клиента была успешной. Я думаю, что Connected — это ужасное название для этого свойства. Это должно быть WasConnected или WasConnectedAtLastSend, в то время как более подробный это спасло бы меня от многих душевных страданий (и поэтому я бы быстрее прочитал документы MSDN).

person Tod    schedule 29.09.2011