CommunicationException:向第三方WebService发出HTTP请求(HTTP.SYS)时,客户端发生错误

时间:2018-07-27 19:30:35

标签: c# wcf

我来自巴西,我们的政府发布了一个WebService,旨在通知员工工资,称为eSocial。我正在开发一种与该服务进行通信的解决方案,已有大约一年的时间,在尝试将信息发送到政府WebService时,我在某些客户的计算机中发现了此错误:

  

System.ServiceModel.CommunicationException
  向“ https://webservices.producaorestrita.esocial.gov.br/servicos/empregador/enviarloteeventos/WsEnviarLoteEventos.svc”发出HTTP请求时发生错误。这可能是由于在HTTPS情况下未使用HTTP.SYS正确配置服务器证书。这也可能是由于客户端和服务器之间的安全绑定不匹配造成的。

     

---> System.Net.WebException 基础连接已关闭:连接意外关闭。
  ---> System.IO.IOException 无法从传输连接中读取数据:远程主机强行关闭了现有连接。
  ---> System.Net.Sockets.SocketException 远程主机在System.Net.Sockets.Socket.Receive(Byte []缓冲区,Int32偏移量)上强制关闭了现有连接,Int32大小,SocketFlags,SocketFlags)...

在大多数计算机上,我们的程序运行正常,该错误仅在某些计算机上出现。我的目标平台是.NET Framework 4.7。

在研究过程中,我找到了解决该问题的两种方法:

  1. 设置属性ServicePointManager.SecurityProtocolhttps://stackoverflow.com/a/33084791/8133067);
  2. 让Windows Update安装所需的更新(https://stackoverflow.com/a/45494549)。

在第一种解决方案的情况下:eSocial WebService使用 TLS 1.2 安全协议,但这并不总是默认的计算机配置。我已经在代码中做到了这一点,并且没有解决了大多数情况:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                        SecurityProtocolType.Tls11 |
                                        SecurityProtocolType.Tls12;

在第二种解决方案的情况下:在研究中,我确实会在Windows 7过时的计算机上发生此问题,并且理论上通过Windows Update进行的更新可以解决,但在大多数情况下没有解决,我什至抓住了Windows 10中某些错误的情况。

我什至做了一个测试程序,打开了message logtrace log ,以查看是否可以获得更多信息,但我发现了唯一的另一件事out(由于跟踪)是因为通过通道(与WebService进行通信的通道)发送消息时发生了错误。

这是我用来实例化和调用服务的代码:

 const string UrlSvcBase = @"https://webservices.{0}.esocial.gov.br/servicos/empregador/{1}";
 string urlServico = String.Format(UrlSvcBase, "producaorestrita", @"enviarloteeventos/WsEnviarLoteEventos.svc");

 var address = new EndpointAddress(urlServico);
 //var binding = new BasicHttpBinding(BasicHttpsSecurityMode.Transport);
 var binding = new BasicHttpsBinding();  //Available since .NET Framework 4.5
 binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
 // Manual de Orientação do Desenvolvedor v1.4, página 39, item '5.4.5. Validações aplicadas':
 // "O tamanho limite da mensagem SOAP é 750 kbytes."
 // O valor padrão da propriedade MaxReceivedMessageSize é 65.536,
 // que é alterado então para 750 KB * 1024 = 768.000 bytes.
 // Caso contrário, ocorre a exceção:
 //   Exception: System.ServiceModel.CommunicationException
 //   InnerException: System.Exception {System.ServiceModel.QuotaExceededException}
 //   "The maximum message size quota for incoming messages (65536) has been exceeded.
 //    To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element."  
 //   HResult: -2146233087 (0x80131501)
 binding.MaxReceivedMessageSize = 768000;

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                        SecurityProtocolType.Tls11 |
                                        SecurityProtocolType.Tls12;

 var wsClient = new WsEnviar.ServicoEnviarLoteEventosClient(binding, address);
 // 'certificado' variable's type is X509Certificate2.
 wsClient.ClientCredentials.ClientCertificate.Certificate = certificado;

 wsClient.Open();
 // 'lote' variable's type is XElement.
 var retorno = wsClient.EnviarLoteEventos(lote);
 wsClient.Close();

有人知道导致此错误的原因是什么,或者我还有其他办法可以找出问题的原因吗?

[编辑]
我的问题被标记为可能重复该问题:

  

WCF Error "This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case"

但是,我已经尝试了几乎所有这些解决方案,但都没有成功(实际上,您可以在我的原始帖子中看到该问题已经链接了),这就是为什么我发布了一个新问题,并且存在根本的区别,因为质疑服务是由用户提供的,在我的情况下,服务是第三方的。幸运的是我在其他地方有答案,所以我将其发布在这里。

1 个答案:

答案 0 :(得分:0)

我也在葡萄牙语中的堆栈溢出上发布了该问题:

  

https://pt.stackoverflow.com/q/318351/86952

在评论部分,我得到了用户 EProgrammerNotFound https://stackoverflow.com/users/2087187)的答复。

在使用Fiddler进行了一些测试之后,他发现.NET Framework中似乎存在一个错误,因为在某些情况下,它并没有回退到以前版本的安全协议。

因此,即使我已指定安全协议属性使用TLS版本1.2、1.1和1.0:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
                                        SecurityProtocolType.Tls11 |
                                        SecurityProtocolType.Tls12;

如果使用TLS 1.2的连接由于某种原因而失败(在我无法复制的特定环境中,但是在某些客户中发生),它会在没有尝试TLS 1.1的情况下停止,并且在TLS 1.0之后,然后会出现该错误。

因此,我将代码更改为仅直接使用TLS 1.0,它解决了有问题的机器上的情况:

 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

关于我在SO PT 上的问题,我还从用户 Jonas Giehl https://pt.stackoverflow.com/a/324254/86952)那里得到了答案,该用户提出了可以尝试每个代码的代码手动设置安全协议,我也曾想过但没有时间尝试。

有他的代码(稍作修改):

// Stack with protocols we are going to try.
Stack<SecurityProtocolType> protocolsToTry = new Stack<SecurityProtocolType>();
protocolsToTry.Push(SecurityProtocolType.Tls);
protocolsToTry.Push(SecurityProtocolType.Tls11);
protocolsToTry.Push(SecurityProtocolType.Tls12);
bool tryAgain = false;

do
{
   // Set the protocol we are going to try now.
   ServicePointManager.SecurityProtocol = protocolsToTry.Pop();

   try
   {
      // Tries to call the service using the current protocol.
      // If no exceptions are threw, we are ready to exit the loop.
      CallService();
      tryAgain = false;
   }
   catch (CommunicationException ex)
   {
      tryAgain = true;
   }
} while(tryAgain && protocolsToTry.Count() > 0);