如何像HttpWebRequest一样用TcpClient做HTTPS?

我有一个基于TcpClient的通信系统,除了它对特定IP做HTTPS之外,它工作得很好。 然后它开始失败。

通过使用浏览器或HttpWebRequest,我可以毫无问题地为该IP做HTTPS。

我已经创建了一个测试程序来将我的问题缩小到基本的本质,如果你需要,你可以在这里查看它: 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? 有什么方法可以获得我在使用HttpWebRequest进行HTTPS时可以看到的“始终有效”行为吗?

应该有一些我可以用TcpClient做的事情让它像HttpWebRequest一样行动和反应,但我还没有。 有任何想法吗?

注意:我正在与之通信的服务器可以配置它侦听的端口以及它所期望的协议,但是否则完全不可修改。

另请注意:我已经读过.net 3.5在SP1之前遇到了SslStream的这个特殊问题,但是我有SP1并且我的程序是针对3.5构建的,所以我假设这不是一个’已知错误’我’我跑到这里。

难道你不知道, 我花时间形成问题之后,我偶然发现了答案。

这是相关文档: jpsanders博客条目

重要的是这个:

如果exception中的堆栈包含类似于此的内容:System.IO.IOException:身份validation失败,因为远程方已关闭传输流。 服务器可能是较旧的服务器,不了解TLS,因此您需要将915599 kb中指定的更改为以下内容:ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;在应用程序中进行任何HTTPS调用之前。

所以我将我接受的协议更改为:(删除TLS的可能性)

 SslProtocols protocol = SslProtocols.Ssl2 | SslProtocols.Ssl3; 

一切都很好。
我允许TLS,所以它首先尝试,服务器不支持它,因此流被关闭。 下次它使用Ssl2或Ssl3,一切都很好。 或类似的东西。 它有效,我是一个快乐的pandas。