2016-08-01 4 views
0

Я хотел бы ожидать медленного ответа от клиента с TcpClient, но получить тайм-аут примерно через 20 секунд, независимо от того, как я его настрою. Это моя попытка:TcpClient SocketException с тайм-аутом после 20-х годов независимо от того, что

using (var client = new TcpClient { ReceiveTimeout = 9999999, SendTimeout = 9999999 }) 
{ 
    await client.ConnectAsync(ip, port); 
    using (var stream = client.GetStream()) 
    { 
     // Some quick read/writes happen here via the stream with stream.Write() and stream.Read(), successfully. 

     // Now the remote host is calculating something long and will reply if finished. This throws the below exception however instead of waiting for >20s. 
     var bytesRead = await stream.ReadAsync(new byte[8], 0, 8); 
    } 
} 

Исключение является IOException:

Невозможно прочитать данные из транспортного соединения: Попытка подключения не удалось, потому подключенная сторона не ответила после периода времени или установленное соединение не удалось, так как подключенный хост не смог ответить.

... который содержит SocketException внутри:

Попытка подключения не удалась, поскольку подключенная сторона не должным образом реагировать после определенного периода времени, или установленное соединение не удалось, поскольку подключенный хост не удалось для ответа

SocketErrorCode является TimedOut.

20s seems to be an OS default on Windows, но нельзя ли переопределить его из управляемого кода, взаимодействуя с TcpClient? Или как я могу ждать ответа в противном случае?

Я также пробовал стиль BeginRead - EndRead, и это происходит на EndRead. Проблема также не вызвана брандмауэром Windows или защитником.

+0

Обновление: если я попытаюсь запустить ReadAsync() снова после сбоя, я получу «Существующее соединение было принудительно закрыто удаленным хостом». Это мое подозрение, что удаленный хост вызывает всю эту проблему, но даже до этого я не мог найти никаких признаков того, что он закроет соединение. – Piedone

ответ

0

Оказалось, что удаленный хост не отвечал своевременно, следовательно, проблема. Позвольте мне уточнить, и хотя это будет решение, очень специфичное для моего дела, возможно, оно будет полезно и для других.

Реальный вопрос не был тайм-аут сам по себе, как исключение указано, а то, что исключения, брошенные на последующих Read() вызовов показали: «Существующее соединение было принудительно закрыто удаленным узлом»

Пульт дистанционного управления хост не намеренно закрывал соединение. Скорее, произошло то, что, когда он медленно реагировал, он был настолько занят, что не обрабатывал ни один трафик TCP. Хотя локальный хост явно не посылал ничего, ожидая ответа, это все еще было проблемой: локальный хост попытался отправить ACK для предыдущих передач удаленного хоста.Поскольку они не могли быть доставлены, местный хост определил, что удаленный хост «принудительно закрыл» соединение.

У меня есть ключ к просмотру трафика с помощью Wireshark (всегда хорошо, чтобы попытаться посмотреть, что находится ниже поверхности, а не гадать): было очевидно, что, когда удаленный хост был занят, он показал полную радиомолчанию. В то же время Wireshark продемонстрировал попытки повторной передачи, выполненные местным хостом, что указывает на то, что это связано с проблемой.

Таким образом, решение не может быть реализовано на локальном хосте, поведение удаленного хоста необходимо изменить.

1

Я хотел бы ждать медленного ответа от клиента

Важно отметить, что это соединение, что не удается. Тайм-аут соединения предназначен только для установления соединения, которое всегда должно быть очень быстрым. Фактически, ОС будет принимать соединения от имени приложения, поэтому вы буквально просто говорите о пакете в оба конца. 21 секунда должно быть много.

Как только соединение установлено, вы можете просто удалить ReceiveTimeout/SendTimeout и использовать асинхронные чтения для ожидания.

+0

Забавно, я только что прочитал ваш блог-адрес: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html :-) (я пытаюсь использовать TCP keep-alive или пользовательский протокол «keep-alive»). Проблема в том, что это асинхронное чтение, которое терпит неудачу. Соединение успешно завершено, и, хотя я сократил этот код для краткости, также существует некоторое сообщение о записи/чтении между соединением и приведенным выше медленным ReadAsync(). Итак, все работает нормально, пока удаленный хост не примет> 20s для отправки данных снова. – Piedone

+0

@Piedone: Ненавижу говорить об этом, но вы абсолютно уверены, что это то, что вы видите? Я прошу, потому что * * * время ожидания при асинхронных чтениях сокетов (для объектов сокета .NET). 'ReceiveTimeout' игнорируется для асинхронных чтений. Если сервер никогда ничего не отправляет, то клиент должен просто сидеть там в ожидании. –

+0

Я уверен, что это то, что происходит из того, что я вижу. После некоторых успешных вызовов stream.Write() и stream.Read(), если ReadAsync() быстро получает данные с удаленного компьютера, то он работает; если нет, тайм-аут (весь код работает правильно, если этот ответ выполняется быстрее, чем около 1 с). Также предположительно соединение открыто в момент времени ожидания: FWIW TcpClient.Connected == true. Также я только заметил, что я не включил вызов GetStream() в образец, пояснил его. – Piedone