如何像HttpWebRequest一样用TcpClient做HTTPS?

时间:2010-06-29 17:07:00

标签: c# httpwebrequest tcpclient ioexception

我有一个基于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构建的,所以我假设这不是一个'已知的bug “我跑到这里。

1 个答案:

答案 0 :(得分:3)

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

以下是相关文档:jpsanders blog entry

重要的是这个:

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

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

SslProtocols protocol = SslProtocols.Ssl2 | SslProtocols.Ssl3;

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