Как сделать HTTPS с TcpClient, как это делает HttpWebRequest?
У меня есть система связи, основанная на TcpClient, и она прекрасно работает, за исключением случаев, когда она делает HTTPS для определенного IP. Тогда это начинает терпеть неудачу.
Используя браузер или HttpWebRequest, у меня нет проблем, делая HTTPS с этим IP.
Я создал тестовую программу, чтобы сузить мою проблему до ее сути, вы можете взглянуть на нее здесь, если хотите: TestViaTcp
Эта тестовая программа отлично работает для базового HTTP на тот же IP, она всегда выдает успешный ответ на запрос. Я помещаю это в цикл, запускаю его нажатием клавиши, и оно будет продолжаться весь день. Как только я переключаю HTTPS, я получаю повторяющийся шаблон. Это сработает, а затем и не сработает, за успехом последуют неудачи, а затем успехи в течение всего дня.
Конкретная ошибка, которую я продолжаю получать, такова:
{"Authentication failed because the remote party has closed the transport stream."}
[System.IO.IOException]: {"Authentication failed because the remote party has closed the transport stream."}
Data: {System.Collections.ListDictionaryInternal}
HelpLink: null
InnerException: null
Message: "Authentication failed because the remote party has closed the transport stream."
Source: "System"
TargetSite: {Void StartReadFrame(Byte[], Int32, System.Net.AsyncProtocolRequest)}
И вот трассировка стека, прикрепленная к этому:
at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
at DeriveClassNameSpace.Services.Web.TcpMessaging.TestViaTcp(IPEndPoint endpoint, String auth, Boolean useSSL)
HttpWebRequest и браузер (IIRC) используют библиотеки Win32 для обработки обратной связи, в то время как TcpClient (AFAIK) использует управляемый класс.net Socket, так что я уверен, что между ними есть большая разница. Мне нужно сделать это с TcpClient, поэтому, к сожалению, я не могу просто "использовать HttpWebRequest, так как знаю, что могу заставить его работать".
Самый большой намек на то, в чем здесь проблема, вероятно, это паттерн "работает, не работает, не делает", что вызывает это? Что я могу сделать, чтобы избежать IOException, которое я получаю? Есть ли какой-нибудь способ получить поведение "всегда работает", которое я вижу при выполнении HTTPS с HttpWebRequest?
Должно быть что-то, что я могу сделать с TcpClient, чтобы заставить его действовать и реагировать так же, как это делает HttpWebRequest, но я еще не там. Есть идеи?
Примечание. Сервер, с которым я общаюсь, настраивается в зависимости от того, какой порт он слушает, и какого протокола он ожидает, но в остальном он совершенно не поддается изменению.
Также обратите внимание: я читал, что в.net 3.5 была эта конкретная проблема с SslStream до SP1, но у меня есть SP1, и моя программа построена для 3.5, поэтому я предполагаю, что это не "известная ошибка" I Я сталкиваюсь здесь.
1 ответ
Разве вы не знаете, после того, как я потрачу время на формирование вопроса, я наткнусь на ответ.
Вот соответствующая документация: запись в блоге jpsanders
Важной частью было это:
Если в стеке исключения есть что-то похожее на это: System.IO.IOException: аутентификация не удалась, потому что удаленная сторона закрыла транспортный поток. Возможно, это более старый сервер, который не понимает TLS, поэтому вам необходимо изменить его, как указано в 915599 КБ, на что-то вроде этого: ServicePointManager.SecurityProtocol= SecurityProtocolType.Ssl3; Перед тем, как выполнять какие-либо HTTPS-вызовы в приложении.
Поэтому я изменяю свои принятые протоколы на это: (исключая возможность TLS)
SslProtocols protocol = SslProtocols.Ssl2 | SslProtocols.Ssl3;
И все отлично работает.
Я разрешил TLS, поэтому сначала он пытается это сделать, а сервер его не поддерживает, поэтому поток закрывается. В следующий раз он использует Ssl2 или Ssl3, и все в порядке. Или что-то типа того. Это работает, я счастливая панда.