2016-05-27 3 views
0

У меня есть собственная WCF-служба с одноранговой привязкой. Я использую DuplexChannel для получения ответов. Сам обратный вызов работает. Я сохраняю обратные вызовы в статическом словаре. Когда клиент входит в систему, обратный вызов добавляется в словарь. Но когда я создаю несколько клиентов, статические переменные пусты.WCF: служба не работает для нескольких клиентов

Это моя служба интерфейса:

реализация
[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); 
} 

Исполнительное обратного вызова класса обслуживания в моем клиентском приложении:

[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; 
    } 

}

+0

1. Почему пара и клиенты являются статическими? –

+0

Я хочу хранить всех клиентов и отправлять уведомления определенным пользователям –

+0

Также вы должны использовать один из примитивов синхронизации для пары и клиентов, например lock. Или используйте поточный безопасный контейнер - ConcurrentDictionary –

ответ

1

Вот полный пример кода работал. Надеюсь, вы поймете код. Это довольно легко. Вот link to the project. Пример запуска enter image description here:

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

Я пробовал это, но служба, созданная каналом, не вызывает методы в моем классе 'Сервис' –

+0

. Вы использовали тот же код или внесли некоторые изменения? –

+0

Я решил проблему, я использовал разные конечные точки, я изменил их, и теперь он вызывает методы, но я получаю «ArgumentException: TransactionFlowProperty уже существует». Это происходит при вызове обратного вызова –