【发布时间】:2010-10-28 08:18:47
【问题描述】:
我发现了一种情况,如果 WCF ServiceHost 具有需要一段时间来处理的 UserNamePasswordValidator,WCF 客户端可能会导致 WCF ServiceHost 崩溃。在我向微软提出这个问题之前,我想听听有关解决方案的任何建议。
重现的步骤是:
- 打开一个频道
- 调用需要 10 秒返回的 API 方法。
- 在 API 方法返回之前关闭通道。
- API 方法返回。 ServiceHost 崩溃。
仅当 UserNamePasswordValidator 需要一段时间来处理(我的示例,2 秒睡眠)时才会发生上述情况。如果 UserNamePasswordValidator 立即返回,则上述情况发生变化:
- 打开一个频道
- 调用需要 10 秒返回的 API 方法。
- 在 API 方法返回之前关闭通道。
- 关闭通道等待挂起的 API 方法调用。
- API 方法返回。
- 频道已关闭。一切都很好。
客户端代码如下:
public static void Main(string[] args)
{
var binding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
var identity = new DnsEndpointIdentity("Dummy");
var endpointAddress = new EndpointAddress(new Uri("net.tcp://localhost:5000/"), identity);
var channelFactory = new ChannelFactory<IService>(binding, endpointAddress);
channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
channelFactory.Credentials.UserName.UserName = "foo";
channelFactory.Credentials.UserName.Password = "bar";
channelFactory.Open();
IService service = channelFactory.CreateChannel();
ThreadPool.QueueUserWorkItem(CallPingOnChannel, service);
Thread.Sleep(TimeSpan.FromSeconds(1));
channelFactory.Close();
}
private static void CallPingOnChannel(object state)
{
var result = ((state) as IService).Ping();
}
ServiceHost设置如下:
public static void Main(string[] args)
{
var serviceHost = new ServiceHost(typeof(Service), TcpBaseAddress);
serviceHost.Description.Behaviors.Add(new ErrorHandler());
var netTcpBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
netTcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserValidator();
serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
serviceHost.Credentials.ServiceCertificate.Certificate = GetCertificate();
serviceHost.AddServiceEndpoint(typeof(IService), netTcpBinding, "");
serviceHost.Open();
Console.ReadLine();
}
而UserValidator的实现如下:
public class UserValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
导致主线程崩溃的未处理异常是:
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
【问题讨论】:
-
建议...我不想说显而易见的,但是:验证密码不需要很长时间?
-
Marc:如你所知,密码验证可能依赖于外部资源,例如文件系统、数据库、缓存等。如果这些进程存在性能问题,它绝不会导致我的 API 崩溃 尤其不是手工制作的客户。