我来自巴西,我们的政府发布了一个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。
在研究过程中,我找到了解决该问题的两种方法:
ServicePointManager.SecurityProtocol
(https://stackoverflow.com/a/33084791/8133067); 在第一种解决方案的情况下:eSocial WebService使用 TLS 1.2 安全协议,但这并不总是默认的计算机配置。我已经在代码中做到了这一点,并且没有解决了大多数情况:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls |
SecurityProtocolType.Tls11 |
SecurityProtocolType.Tls12;
在第二种解决方案的情况下:在研究中,我确实会在Windows 7过时的计算机上发生此问题,并且理论上通过Windows Update进行的更新可以解决,但在大多数情况下没有解决,我什至抓住了Windows 10中某些错误的情况。
我什至做了一个测试程序,打开了message log和trace 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();
有人知道导致此错误的原因是什么,或者我还有其他办法可以找出问题的原因吗?
[编辑]
我的问题被标记为可能重复该问题:
但是,我已经尝试了几乎所有这些解决方案,但都没有成功(实际上,您可以在我的原始帖子中看到该问题已经链接了),这就是为什么我发布了一个新问题,并且存在根本的区别,因为质疑服务是由用户提供的,在我的情况下,服务是第三方的。幸运的是我在其他地方有答案,所以我将其发布在这里。
答案 0 :(得分:0)
我也在葡萄牙语中的堆栈溢出上发布了该问题:
在评论部分,我得到了用户 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);