Как сделать HTTPS с TcpClient, как это делает HttpWebRequest? - PullRequest
2 голосов
/ 29 июня 2010

У меня есть система связи, основанная на 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, поэтому я предполагаю, что это не «известная ошибка» «Я сталкиваюсь здесь.

1 Ответ

3 голосов
/ 29 июня 2010

Разве вы не знаете, после Я трачу время на формирование вопроса, когда сталкиваюсь с ответом.

Вот соответствующая документация: Запись в блоге jpsanders

Важной частью было следующее:

Если в стеке исключения есть что-то похожее на следующее: System.IO.IOException: Аутентификация не удалась, потому что удаленная сторона закрылатранспортный поток.Вполне возможно, что сервер более старого сервера не понимает TLS, поэтому вам необходимо изменить его, как указано в 915599 КБ, на что-то вроде этого: ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; Прежде чем выполнять какие-либо вызовы HTTPS в вашем приложении.

Поэтому я изменяю свои принятые протоколы следующим образом: (исключая возможность TLS)

SslProtocols protocol = SslProtocols.Ssl2 | SslProtocols.Ssl3;

И все прекрасно работает.
Я разрешил TLS, поэтому он пытаетсяво-первых, и сервер не поддерживает его, поэтому поток закрыт.В следующий раз он использует Ssl2 или Ssl3, и все в порядке.Или что-то типа того.Это работает, я счастливая панда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...