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");
        }
    }
}