【发布时间】:2015-11-18 15:30:02
【问题描述】:
我有两个 WCF 服务在我的计算机 (Windows 8) 的 Windows 窗体应用程序 (C# / .NET 4) 中运行:
第一次服务:https://localhost:9002
二次服务:https://local_192-168-1-104.desktop.Company.com:9003
第一个服务是本地访问的,第二个服务是本地网络中的其他计算机访问的。主机:local_192-168-1-104.desktop.Company.com被DNS解析为ip:192.168.1.104,这是我的本地网络ip地址。
两个服务都使用 https,第一个使用 SelfSigned 证书,第二个使用证书:desktop.Company.com
问题:在一段时间不活动后,第二个服务停止工作。
没有异常被记录并且WebServiceHost的事件Faulted()、UnknownMessageReceived()、Closing()或者Closed()没有被执行。
如果我关闭应用程序并重新启动它,问题仍然存在。在这种错误情况下,WebServiceHost 的 State 属性为“Opened”,但服务没有响应。
如果我停止服务,重新配置服务(主题:配置证书服务 2),然后再次启动,问题会停止一段时间。
我使用这个测试 wcf 服务:
using (WebClient webClient = new WebClient())
{
webClient.DownloadDataCompleted += webClient_DownloadDataCompleted;
webClient.DownloadDataAsync(new Uri("https://local_192-168-1-104.desktop.Company.com:9003/Message/Test"));
}
我收到了这个例外:
底层连接已关闭:发送时发生意外错误。
>> INNER EXCEPTION:
Message: An existing connection was forcibly closed by the remote host
Type: System.Net.Sockets.SocketException (System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)
Source: System
TargetSite: Int32 EndReceive(System.IAsyncResult)
ErrorCode: 10054
StackTrace:
---------------------------
at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)
at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
---------------------------
Windows 错误代码 10054 是:
对等方重置连接。
现有连接被远程主机强行关闭。这个 通常如果远程主机上的对等应用程序是 突然停止,主机重启,主机或远程网络 接口被禁用,或远程主机使用硬关闭(请参阅 setsockopt 以获取有关远程 SO_LINGER 选项的更多信息 插座)。如果连接因以下原因而中断,也可能导致此错误 在一个或多个操作时检测故障的保持活动 正在进行中。正在进行的操作失败 WSAENETRESET。后续操作因 WSAECONNRESET 而失败。
我尝试禁用 Keep Alive,但问题仍然存在。
更多详情如下:
配置证书服务1
netsh http delete urlacl url=https://+:9002/
netsh http delete sslcert ipport=0.0.0.0:9002
netsh http add urlacl url=https://+:9002/ user=Everyone
makecert -sk RootCA -sky signature -pe -n CN=localhost -r -sr LocalMachine -ss Root certificate\CustomCertificate_MyCA.cer
makecert -sk server -sky exchange -pe -n CN=localhost -ir LocalMachine -is Root -ic certificate\CustomCertificate_MyCA.cer -sr LocalMachine -ss My certificate\CustomCertificate.cer
(install certificate in CertificateStore)
netsh http add sslcert ipport=0.0.0.0:9002 certhash=F3F40BF81AF0.... appid={00a1d32c-68bd-4693-a872-...}
启动服务1
var mainServiceHost = new Company.Library.WCF.JSONServiceHost("localhost", 9002, true);
mainServiceHost.Start(typeof(Service.Message), typeof(Service.IMessage), false);
配置证书服务2
netsh http delete urlacl url=https://+:9002/
netsh http delete sslcert ipport=192.168.1.146:9003
netsh http add urlacl url=https://+:9003/ user=Everyone
(install certificate in CertificateStore)
netsh http add sslcert ipport=192.168.1.146:9003 certhash=1C36C9C... appid={00a1d32c-68bd-4693-a872-1473...}
启动服务2
var localServerServiceHost = new Company.Library.WCF.JSONServiceHost("local_192-168-1-104.desktop.Company.com", 9003, true);
localServerServiceHost.Start(typeof(Service.Message), typeof(Service.IMessage), false);
JSONServiceHost 类
public void Start(Type serviceType, Type implementedContract, bool keepAliveEnabled)
{
EndpointAddress endpoint = new EndpointAddress(Url);
CustomServiceBehavior serviceBehavior = new CustomServiceBehavior();
svcWebHost.Description.Behaviors.Add(serviceBehavior);
BindingElementCollection bindingElements;
WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
bindingElements = binding.CreateBindingElements();
if (bindingElements != null)
{
var transport = bindingElements.Find<HttpsTransportBindingElement>();
if (transport != null)
transport.KeepAliveEnabled = keepAliveEnabled;
}
binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
binding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
binding.ReaderQuotas.MaxDepth = int.MaxValue;
binding.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
binding.MaxBufferSize = int.MaxValue;
binding.MaxBufferPoolSize = int.MaxValue;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.SendTimeout = TimeSpan.FromMinutes(5);
binding.ReceiveTimeout = TimeSpan.FromMinutes(5);
binding.OpenTimeout = TimeSpan.FromMinutes(5);
binding.CloseTimeout = TimeSpan.FromMinutes(5);
ServiceEndpoint serviceEndpoint = svcWebHost.AddServiceEndpoint(implementedContract, binding, endpoint.Uri);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
svcWebHost.Description.Behaviors.Add(smb);
ServiceDebugBehavior sdb = svcWebHost.Description.Behaviors.Find<ServiceDebugBehavior>();
if (sdb != null)
sdb.IncludeExceptionDetailInFaults = true;
foreach (var operation in serviceEndpoint.Contract.Operations)
operation.Behaviors.Add(new CustomOperationBehavior());
svcWebHost.Open();
}
应用程序启动时执行的附加配置
private static void ConfigureGlobalServiceParameters()
{
System.Net.ServicePointManager.CheckCertificateRevocationList = false;
System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue;
System.Net.ServicePointManager.DnsRefreshTimeout = -1;
System.Net.ServicePointManager.Expect100Continue = false;
System.Net.ServicePointManager.MaxServicePointIdleTime = 20 * 1000;
System.Net.ServicePointManager.MaxServicePoints = 10;
System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(delegate { return true; });
System.Net.ServicePointManager.UseNagleAlgorithm = false;
System.Net.ServicePointManager.SetTcpKeepAlive(false, 0, 0);
}
CustomEndpointBehavior
public class CustomEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
var customInspector = new CustomDispatchMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(customInspector);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
自定义操作行为
public class CustomOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)
{
}
public void Validate(OperationDescription operationDescription)
{
}
}
CustomServiceBehavior
public class CustomServiceBehavior : IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
CustomEndpointBehavior endpointBehavior = new CustomEndpointBehavior();
foreach (var endpoint in serviceDescription.Endpoints)
endpoint.Behaviors.Add(endpointBehavior);
}
public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
}
谢谢!
【问题讨论】:
-
附加信息:我创建了一个线程每 5 分钟测试一次服务,使用这个问题永远不会发生。