Сообщение об ошибке
Received an unexpected EOF or 0 bytes from the transport stream.
Также выводится как
- System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
HttpClient оборачивает её в «The SSL connection could not be established, see inner exception.» В логах приложений на .NET Framework та же ситуация обычно выглядит как «Authentication failed because the remote party has closed the transport stream.»
.NET SslStream / HttpClient — IOException: the peer closed the connection during the TLS handshake
Что это означает
Удалённая сторона (или промежуточное устройство) закрыла TCP-соединение посреди TLS-рукопожатия SslStream, не отправив оповещение TLS. Кроме закрытого потока, .NET сообщить не о чем. Обычно сервер молча отказался от рукопожатия или его оборвал прокси либо межсетевой экран.
Почему это происходит
Сервер закрывает рукопожатия, которые не принимает
частоНет общей версии протокола или шифра, имя SNI отсутствует или неизвестно серверу либо сервер требует того, чего клиент не предложил, — и при этом сервер не отправляет оповещение, а просто закрывает соединение.
Прокси или межсетевой экран обрывает соединение
частоСети, где обязателен прокси, межсетевые экраны с фильтрацией TLS и VPN-клиенты молча завершают заблокированные соединения.
Приложения .NET Framework предлагают только старые версии TLS
иногдаПриложения для .NET Framework 4.5 и более ранних версий без системных настроек TLS по умолчанию могут предлагать только TLS 1.0, а современные серверы такие соединения обрывают.
Как исправить
- 1.
Проверьте рукопожатие с той же машины
Если openssl s_client тоже обрывается, проблема в сети или на сервере; если рукопожатие проходит, сравните версии протокола и SNI с клиентом .NET.
bash # Does a plain TLS handshake from the same host work? openssl s_client -connect api.example.com:443 -servername api.example.com -brief </dev/null openssl s_client -connect api.example.com:443 -servername api.example.com -tls1_2 -brief </dev/null - 2.
Используйте обязательный прокси
Настройте прокси в обработчике (или через HTTPS_PROXY в Linux), если сеть не разрешает прямые соединения.
csharp // Use the corporate proxy explicitly (HttpClient also honours HTTPS_PROXY / system settings) var handler = new SocketsHttpHandler { Proxy = new WebProxy("http://proxy.example.net:3128"), UseProxy = true }; using var client = new HttpClient(handler); - 3.
.NET Framework: включите TLS 1.2
Смените целевую платформу на 4.7 или новее, чтобы протокол выбирала ОС, или, пока это невозможно, добавьте TLS 1.2 в ServicePointManager.SecurityProtocol.
csharp // .NET Framework 4.x apps compiled for old targets may still offer only TLS 1.0: // target 4.7+ (OS defaults) or, as a stop-gap, enable TLS 1.2 explicitly System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;