WCF:服务不适用于多个客户端
WCF : Service does not work for multiple clients
我有一个带有点对点绑定的自托管 WCF 服务。我正在使用 DuplexChannel 来获取响应。回调本身正在工作。我将回调保存在静态字典中。当客户端登录时,回调被添加到字典中。但是当我创建多个客户端时,静态变量是空的。
这是我的服务界面:
[ServiceContract(SessionMode = SessionMode.Allowed, CallbackContract = typeof(ICallbackService))]
public interface IService
{
[OperationContract(IsOneWay = true)]
void Login(string email, string password);
[OperationContract(IsOneWay = true)]
void Logout(int userId);
}
我的 IService 实现:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
private ServiceHost host = null;
private DuplexChannelFactory<IService> channelFactory = null;
private IService service = null;
private static Dictionary<ICallbackService, ICallbackService> pair = new Dictionary<ICallbackService, ICallbackService>();
private static Dictionary<string, ICallbackService> clients = new Dictionary<string, ICallbackService>();
private ICallbackService callback;
public Service(InstanceContext context)
{
startService(context);
}
private void startService(InstanceContext context)
{
host = new ServiceHost(this);
host.Open();
context.Open();
channelFactory = new DuplexChannelFactory<IBattleshipService>(context, "Endpoint");
channelFactory.Open();
service = channelFactory.CreateChannel();
callback = context.GetServiceInstance() as ICallbackService;
}
private void stopService()
{
if (host != null)
{
if (host.State == CommunicationState.Closed)
{
channelFactory.Close();
host.Close();
}
}
}
public void Login(string email, string password)
{
User user = getAllUsers().Find(u => u.Email.ToLower() == email.ToLower() && u.Password == password);
if (user != null)
{
Console.WriteLine("user : " + email + " has logged in");
clients.Add(email, callback);
callback.Authenticate(true);
}
else callback.Authenticate(false);
}
public void Logout(int userId)
{
string email = getUser(userId).Email;
clients.Remove(email);
stopService();
}
}
我的回拨服务:
public interface ICallbackService
{
[OperationContract(IsOneWay = true)]
void Authenticate(bool authenticated);
}
在我的客户端应用程序中实现回调服务 class:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Single, UseSynchronizationContext = false)]
public class Client : Service.ICallbackService,
{
public bool Authentication { get; internal set; }
public Client()
{
}
public void Authenticate(bool authenticated)
{
Console.WriteLine("authentication : " + authenticated);
Authentication = authenticated;
}
}
这是有效代码的完整示例。我希望你能理解代码。这很容易。这是 link to the project. Run example :
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace WCFTest
{
class Program
{
public interface ICallbackService
{
[OperationContract(IsOneWay = true)]
void Authenticate(bool authenticated);
}
[ServiceContract(SessionMode = SessionMode.Allowed, CallbackContract = typeof(ICallbackService))]
public interface IService
{
[OperationContract(IsOneWay = true)]
void Login(string email, string password);
[OperationContract(IsOneWay = true)]
void Logout(string email);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
private Dictionary<string, ICallbackService> _clientDict = new Dictionary<string, ICallbackService>();
public void Login(string email, string password)
{
WriteInfoAboutInvoker("Login");
// Get client callback
var callback = OperationContext.Current.GetCallbackChannel<ICallbackService>();
lock (_clientDict)
{
_clientDict.Add(email, callback);
Console.WriteLine("Added '{0}'", email);
}
}
public void Logout(string email)
{
WriteInfoAboutInvoker("Logout");
lock (_clientDict)
{
if (_clientDict.ContainsKey(email))
{
_clientDict.Remove(email);
Console.WriteLine("Removed '{0}'", email);
}
}
}
private void WriteInfoAboutInvoker(string method)
{
var ctx = OperationContext.Current;
var msgProp = ctx.IncomingMessageProperties;
var endpoint = msgProp[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
Console.WriteLine("Invoked {0} from {1}:{2}. SessionId: {3}", method, endpoint.Address, endpoint.Port, ctx.SessionId);
}
}
public class ServiceClient : IDisposable, ICallbackService, IService
{
private readonly string _cs;
private IService _service;
private DuplexChannelFactory<IService> _channel;
/// <summary>
///
/// </summary>
/// <param name="cs">Format is 'hostname:port'</param>
public ServiceClient(string cs)
{
if (string.IsNullOrEmpty(cs))
throw new ArgumentException("cs");
_cs = cs;
}
public void Connect()
{
try { InternalConnect(); }
catch (Exception ex)
{
throw new Exception(string.Format("Can't connect to server '{0}'. Error: {1}", _cs, ex.Message));
}
}
public void Dispose()
{
try
{
_channel.Closed -= Channel_Closed;
_channel.Faulted -= Channel_Faulted;
_channel.Close();
}
catch { }
}
private void InternalConnect()
{
InstanceContext ctx = new InstanceContext(this);
NetTcpBinding tcpBinding = CreateBindings();
_channel = new DuplexChannelFactory<IService>(ctx, tcpBinding, new EndpointAddress("net.tcp://" + _cs));
_channel.Closed += Channel_Closed;
_channel.Faulted += Channel_Faulted;
_service = _channel.CreateChannel();
}
void Channel_Closed(object sender, EventArgs e)
{
Console.WriteLine("Channel closed");
}
private NetTcpBinding CreateBindings()
{
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.Security.Mode = SecurityMode.None;
tcpBinding.Security.Mode = SecurityMode.Transport;
tcpBinding.CloseTimeout = TimeSpan.FromSeconds(1);
tcpBinding.OpenTimeout = TimeSpan.FromSeconds(2);
tcpBinding.ReceiveTimeout = TimeSpan.FromSeconds(15);
tcpBinding.SendTimeout = TimeSpan.FromSeconds(15);
return tcpBinding;
}
void Channel_Faulted(object sender, EventArgs e)
{
Console.WriteLine("Channel faulted!!!");
}
[Obsolete("This method used only for callback", true)]
public void Authenticate(bool authenticated)
{
Console.WriteLine("authenticated: {0}", authenticated);
}
public void Login(string email, string password)
{
_service.Login(email, password);
}
public void Logout(string email)
{
_service.Logout(email);
}
}
static void Main(string[] args)
{
// args[0] contains mode: server or client
if (args[0] == "server"){
RunServer("localhost:42424");
}
else if (args[0] == "client"){
RunClient("localhost:42424");
}
else{
Console.WriteLine("Unknown mode. Only server or client");
}
}
private static void RunClient(string cs)
{
// Create client for our servuce
using (var client = new ServiceClient(cs))
{
// Connect to it
client.Connect();
var fakeEmail = Guid.NewGuid().ToString();
// Call service methods (operation contracts)
client.Login(fakeEmail, fakeEmail);
Console.WriteLine("Invoked Login");
Console.WriteLine("Press 'Enter' to call Logout");
Console.ReadLine();
client.Logout(fakeEmail);
Console.WriteLine("Invoked Logout");
}
}
static void RunServer(string cs)
{
var service = new Service();
var sh = new ServiceHost(service, new Uri("net.tcp://" + cs));
Console.WriteLine("ServiceHost created");
try
{
sh.Open();
}
catch (Exception ex)
{
Console.WriteLine("Couldn't open ServiceHost: {0}", ex);
return;
}
Console.WriteLine("ServiceHost opened");
sh.Closed += ServiceHost_Closed;
sh.Faulted += ServiceHost_Faulted;
Console.WriteLine("Press 'Enter' to quit");
Console.ReadLine();
}
private static void ServiceHost_Faulted(object sender, EventArgs e)
{
Console.WriteLine("ServiceHost faulted!!!");
}
private static void ServiceHost_Closed(object sender, EventArgs e)
{
Console.WriteLine("ServiceHost closed");
}
}
}
我有一个带有点对点绑定的自托管 WCF 服务。我正在使用 DuplexChannel 来获取响应。回调本身正在工作。我将回调保存在静态字典中。当客户端登录时,回调被添加到字典中。但是当我创建多个客户端时,静态变量是空的。
这是我的服务界面:
[ServiceContract(SessionMode = SessionMode.Allowed, CallbackContract = typeof(ICallbackService))]
public interface IService
{
[OperationContract(IsOneWay = true)]
void Login(string email, string password);
[OperationContract(IsOneWay = true)]
void Logout(int userId);
}
我的 IService 实现:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
private ServiceHost host = null;
private DuplexChannelFactory<IService> channelFactory = null;
private IService service = null;
private static Dictionary<ICallbackService, ICallbackService> pair = new Dictionary<ICallbackService, ICallbackService>();
private static Dictionary<string, ICallbackService> clients = new Dictionary<string, ICallbackService>();
private ICallbackService callback;
public Service(InstanceContext context)
{
startService(context);
}
private void startService(InstanceContext context)
{
host = new ServiceHost(this);
host.Open();
context.Open();
channelFactory = new DuplexChannelFactory<IBattleshipService>(context, "Endpoint");
channelFactory.Open();
service = channelFactory.CreateChannel();
callback = context.GetServiceInstance() as ICallbackService;
}
private void stopService()
{
if (host != null)
{
if (host.State == CommunicationState.Closed)
{
channelFactory.Close();
host.Close();
}
}
}
public void Login(string email, string password)
{
User user = getAllUsers().Find(u => u.Email.ToLower() == email.ToLower() && u.Password == password);
if (user != null)
{
Console.WriteLine("user : " + email + " has logged in");
clients.Add(email, callback);
callback.Authenticate(true);
}
else callback.Authenticate(false);
}
public void Logout(int userId)
{
string email = getUser(userId).Email;
clients.Remove(email);
stopService();
}
}
我的回拨服务:
public interface ICallbackService
{
[OperationContract(IsOneWay = true)]
void Authenticate(bool authenticated);
}
在我的客户端应用程序中实现回调服务 class:
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Single, UseSynchronizationContext = false)]
public class Client : Service.ICallbackService,
{
public bool Authentication { get; internal set; }
public Client()
{
}
public void Authenticate(bool authenticated)
{
Console.WriteLine("authentication : " + authenticated);
Authentication = authenticated;
}
}
这是有效代码的完整示例。我希望你能理解代码。这很容易。这是 link to the project. Run example
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace WCFTest
{
class Program
{
public interface ICallbackService
{
[OperationContract(IsOneWay = true)]
void Authenticate(bool authenticated);
}
[ServiceContract(SessionMode = SessionMode.Allowed, CallbackContract = typeof(ICallbackService))]
public interface IService
{
[OperationContract(IsOneWay = true)]
void Login(string email, string password);
[OperationContract(IsOneWay = true)]
void Logout(string email);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service : IService
{
private Dictionary<string, ICallbackService> _clientDict = new Dictionary<string, ICallbackService>();
public void Login(string email, string password)
{
WriteInfoAboutInvoker("Login");
// Get client callback
var callback = OperationContext.Current.GetCallbackChannel<ICallbackService>();
lock (_clientDict)
{
_clientDict.Add(email, callback);
Console.WriteLine("Added '{0}'", email);
}
}
public void Logout(string email)
{
WriteInfoAboutInvoker("Logout");
lock (_clientDict)
{
if (_clientDict.ContainsKey(email))
{
_clientDict.Remove(email);
Console.WriteLine("Removed '{0}'", email);
}
}
}
private void WriteInfoAboutInvoker(string method)
{
var ctx = OperationContext.Current;
var msgProp = ctx.IncomingMessageProperties;
var endpoint = msgProp[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
Console.WriteLine("Invoked {0} from {1}:{2}. SessionId: {3}", method, endpoint.Address, endpoint.Port, ctx.SessionId);
}
}
public class ServiceClient : IDisposable, ICallbackService, IService
{
private readonly string _cs;
private IService _service;
private DuplexChannelFactory<IService> _channel;
/// <summary>
///
/// </summary>
/// <param name="cs">Format is 'hostname:port'</param>
public ServiceClient(string cs)
{
if (string.IsNullOrEmpty(cs))
throw new ArgumentException("cs");
_cs = cs;
}
public void Connect()
{
try { InternalConnect(); }
catch (Exception ex)
{
throw new Exception(string.Format("Can't connect to server '{0}'. Error: {1}", _cs, ex.Message));
}
}
public void Dispose()
{
try
{
_channel.Closed -= Channel_Closed;
_channel.Faulted -= Channel_Faulted;
_channel.Close();
}
catch { }
}
private void InternalConnect()
{
InstanceContext ctx = new InstanceContext(this);
NetTcpBinding tcpBinding = CreateBindings();
_channel = new DuplexChannelFactory<IService>(ctx, tcpBinding, new EndpointAddress("net.tcp://" + _cs));
_channel.Closed += Channel_Closed;
_channel.Faulted += Channel_Faulted;
_service = _channel.CreateChannel();
}
void Channel_Closed(object sender, EventArgs e)
{
Console.WriteLine("Channel closed");
}
private NetTcpBinding CreateBindings()
{
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.Security.Mode = SecurityMode.None;
tcpBinding.Security.Mode = SecurityMode.Transport;
tcpBinding.CloseTimeout = TimeSpan.FromSeconds(1);
tcpBinding.OpenTimeout = TimeSpan.FromSeconds(2);
tcpBinding.ReceiveTimeout = TimeSpan.FromSeconds(15);
tcpBinding.SendTimeout = TimeSpan.FromSeconds(15);
return tcpBinding;
}
void Channel_Faulted(object sender, EventArgs e)
{
Console.WriteLine("Channel faulted!!!");
}
[Obsolete("This method used only for callback", true)]
public void Authenticate(bool authenticated)
{
Console.WriteLine("authenticated: {0}", authenticated);
}
public void Login(string email, string password)
{
_service.Login(email, password);
}
public void Logout(string email)
{
_service.Logout(email);
}
}
static void Main(string[] args)
{
// args[0] contains mode: server or client
if (args[0] == "server"){
RunServer("localhost:42424");
}
else if (args[0] == "client"){
RunClient("localhost:42424");
}
else{
Console.WriteLine("Unknown mode. Only server or client");
}
}
private static void RunClient(string cs)
{
// Create client for our servuce
using (var client = new ServiceClient(cs))
{
// Connect to it
client.Connect();
var fakeEmail = Guid.NewGuid().ToString();
// Call service methods (operation contracts)
client.Login(fakeEmail, fakeEmail);
Console.WriteLine("Invoked Login");
Console.WriteLine("Press 'Enter' to call Logout");
Console.ReadLine();
client.Logout(fakeEmail);
Console.WriteLine("Invoked Logout");
}
}
static void RunServer(string cs)
{
var service = new Service();
var sh = new ServiceHost(service, new Uri("net.tcp://" + cs));
Console.WriteLine("ServiceHost created");
try
{
sh.Open();
}
catch (Exception ex)
{
Console.WriteLine("Couldn't open ServiceHost: {0}", ex);
return;
}
Console.WriteLine("ServiceHost opened");
sh.Closed += ServiceHost_Closed;
sh.Faulted += ServiceHost_Faulted;
Console.WriteLine("Press 'Enter' to quit");
Console.ReadLine();
}
private static void ServiceHost_Faulted(object sender, EventArgs e)
{
Console.WriteLine("ServiceHost faulted!!!");
}
private static void ServiceHost_Closed(object sender, EventArgs e)
{
Console.WriteLine("ServiceHost closed");
}
}
}