TcpClient продолжает отправлять какое-то время, даже если нет соединения

Предыстория/платформа:

.NET 4/С#

У меня есть C# TcpClient, который подключается к встроенному устройству через Ethernet. Я использую два таймера: один для хранения полученных данных в локальном Queue<MyMessage>, а другой для отправки данных из другого Queue<MyMessage> на встроенное устройство по TCP. Оба они запускаются каждые 200 ms и отправляют/читают все, что им нужно отправить/прочитать.

Есть еще один таймер, который отправляет пакет проверки активности every second (в настоящее время только в целях отладки).

Сценарий/проблема

После установления соединения встроенное устройство начинает отправлять какие-то данные на мой TcpClient. Это работает как задумано (см. в журнале сообщений ниже). Однако затем я выключаю устройство (чтобы оно вообще не работало). Это означает, что он даже не может корректно закрыть TCP. Но это нормально. Я хочу проверить, что произойдет, если что-то подобное произойдет в реальной ситуации.

Однако TcpClient по-прежнему продолжает отправлять данные, даже если сервер (устройство) больше не находится в сети.

Вот код для отправки (используя NetworkStream.Write):

while (messagesToSend.Count > 0)
{
    MyMessage msg = messagesToSend.Dequeue();
    clientStream.Write(msg.Data, 0, msg.Data.Length);

    Debug.WriteLine(DateTime.Now.ToString() + " Sent: " + msg.MessageID);
}

Отправка идет еще секунд 45-50 и наконец обрывается. Это параметры, установленные для TcpClient и NetworkStream.

client.LingerState = new LingerOption(true, 0);
client.NoDelay = true;
client.SendTimeout = 3000;
clientStream.WriteTimeout = 3000;

И вот вывод отладки:

16:32:02 Connecting
16:32:02 Authorizing
16:32:02 Sent: 255
16:32:02 Sent: 0
16:32:02 Authorized
16:32:02 Connected
16:32:02 Received: 255
16:32:02 Received: 226
...
... some regular communication here
...
16:32:06 Received: 251
16:32:06 Sent: 0
16:32:07 Received: 251
16:32:07 Sent: 0            // At this point I have turned off the device
16:32:08 Sent: 0
16:32:09 Sent: 0
...
... every second the same message
...
16:32:54 Sent: 0
16:32:55 Sent: 0
16:32:56 Sent: 0
16:32:57 Unable to write data to the transport connection: An established connection was aborted by the software in your host machine.
16:32:57 CommunicationError

Почему соединение так долго понимает, что оно закрыто? Разве NetworkStream.Write не должен немедленно выйти из строя, если хост не отвечает?

Как обнаружить, что питание устройства отключено и что соединение больше не действует?


person Kornelije Petak    schedule 13.07.2011    source источник


Ответы (1)


Вы не можете. Как бы вы это обнаружили, если бы между вами был коммутатор/маршрутизатор? Ваш компьютер по-прежнему полностью подключен.

TCP/IP работает таким образом. Есть несколько повторных отправок (обычно с увеличением времени ожидания) любого пакета, который не подтвержден получателем. Значения времени ожидания и количество повторных отправок зависят от реализации вашего стека/операционной системы tcp.

Свойство TcpClient.SendTimeout управляет только время, необходимое для отправки данных дальше по потоку.

После вызова метода Write базовый Socket возвращает количество байтов, фактически отправленных на узел. Свойство SendTimeout определяет количество времени, в течение которого TcpClient будет ждать, прежде чем получит количество возвращенных байтов. Если тайм-аут истекает до того, как метод Send успешно завершится, TcpClient выдаст исключение SocketException. По умолчанию тайм-аута нет.

person sisve    schedule 13.07.2011
comment
Таким образом, единственный способ узнать, действительно ли соединение по-прежнему, состоит в том, что сервер (устройство в моем случае) отправляет мне какое-то сообщение пользовательского протокола каждые XY секунд, а затем, если я не получаю его для XY (или еще немного) секунд я знаю, что что-то пошло не так? Я знаю, что операция чтения вернет 0 (поскольку было получено 0 байтов) при сбое соединения, но, похоже, она работает только при чтении, а не при записи. - person Kornelije Petak; 13.07.2011
comment
Не совсем. Пакеты все еще могут буферизоваться где-то между там и здесь. Единственное, что вы можете измерить, это rtt (время приема-передачи). Если вы действительно хотите создать то, что описываете, переключитесь на udp, который не реализует повторную отправку (среди прочего). - person sisve; 13.07.2011