2011-10-22 2 views
0

Mon code au moment ressemble à ceci:
côté serveur:Le client d'un service duplex WCF (liaison TCP) peut-il envoyer et recevoir en même temps?

#region IClientCallback interface 
interface IClientCallback 
{ 
    [OperationContract(IsOneWay = true)] 
    void ReceiveWcfElement(WcfElement wcfElement); 
} 
#endregion 

#region IService interface 
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))] 
interface IService 
{ 
    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)] 
    void ReadyToReceive(string userName, int source, string ostatniTypWiadomosci); 

    [OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)] 
    bool SendWcfElement(WcfElement wcfElement); 

    [OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)] 
    List<int> Login(Client name, string password, bool isAuto, bool isSuperMode); 
} 
#endregion 

#region Public enums/event args 
public delegate void WcfElementsReceivedFromClientEventHandler(object sender, WcfElementsReceivedFromClientEventArgs e); 
public class WcfElementsReceivedFromClientEventArgs : EventArgs 
{ 
    public string UserName; 
} 

public class ServiceEventArgs : EventArgs 
{ 
    public WcfElement WcfElement; 
    public Client Person; 
} 
#endregion 

#region Service 
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)] 
public class Service : IService 
{   
    #region Instance fields 
    //thread sync lock object 
    private static readonly Object SyncObj = new Object(); 
    //callback interface for clients 
    IClientCallback _callback; 
    //delegate used for BroadcastEvent 
    public delegate void ChatEventHandler(object sender, ServiceEventArgs e); 
    public static event ChatEventHandler ChatEvent; 
    private ChatEventHandler _myEventHandler; 
    //holds a list of clients, and a delegate to allow the BroadcastEvent to work 
    //out which chatter delegate to invoke 
    static readonly Dictionary<Client, ChatEventHandler> Clients = new Dictionary<Client, ChatEventHandler>(); 
    //current person 
    private Client _client; 
    #endregion 
    #region Helpers 
    private bool CheckIfPersonExists(string name) 
    { 
     return Clients.Keys.Any(p => p.UserName.Equals(name, StringComparison.OrdinalIgnoreCase)); 
    } 

    private ChatEventHandler getPersonHandler(string name) 
    { 
     foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase))) 
     { 
      ChatEventHandler chatTo; 
      Clients.TryGetValue(c, out chatTo); 
      return chatTo; 
     } 
     return null; 
    } 

    private Client GetPerson(string name) 
    { 
     return Clients.Keys.FirstOrDefault(c => c.UserName.Equals(name, StringComparison.OrdinalIgnoreCase)); 
    } 

    #endregion 

    #region IService implementation 
    public List<int> Login(Client client, string password, bool isAuto, bool isSuperMode) 
    { 
     if (client.ElementsVersions == null) 
     { 
      client.ElementsVersions = new WcfElement(WcfElement.RodzajWiadomosci.VersionControl, client.UserName); 
     } 
     //create a new ChatEventHandler delegate, pointing to the MyEventHandler() method 
     _myEventHandler = MyEventHandler; 

     lock (SyncObj) 
     { 
      if (!CheckIfPersonExists(client.UserName)) 
      { 
       _client = client; 
       Clients.Add(client, _myEventHandler); 
      } 
      else 
      { 
       _client = client; 
       foreach (var c in Clients.Keys.Where(c => c.UserName.Equals(client.UserName))) 
       { 
        ChatEvent -= Clients[c]; 
        Clients.Remove(c); 
        break; 
       } 
       Clients[client] = _myEventHandler; 
      } 
      _client.LockObj = new object(); 
     } 

     _callback = OperationContext.Current.GetCallbackChannel<IClientCallback>(); 
     ChatEvent += _myEventHandler; 
     var rValue = isAuto ? bazaDanych.Login(client.UserName, isSuperMode) : bazaDanych.Login(client.UserName, password); 
     return rValue; 
    } 

    public void PerformDataSync(Client c) 
    { 
     WcfElement wcfDelete = null; 
     WcfElement wcfUpdate = null; 
     //... 
     //this method prepares elements for client 
     //when done it adds them to clients queue (List<WcfElement) 

     try 
     { 
      var counter = 0; 
      if (wcfDelete != null) 
      { 
       foreach (var wcf in WcfElement.SplitWcfElement(wcfDelete, false))//split message into small ones 
       { 
        c.AddElementToQueue(wcf, counter++); 
       } 
      } 
      if (wcfUpdate != null) 
      { 
       foreach (var wcf in WcfElement.SplitWcfElement(wcfUpdate, true)) 
       { 
        c.AddElementToQueue(wcf, counter++); 
       } 
      } 
      SendMessageToGui(string.Format("Wstępna synchronizacja użytkownika {0} zakończona.", c.UserName)); 
      c.IsSynchronized = true; 
     } 
     catch (Exception e) 
     { 
     } 
    } 

    private void SendMessageToClient(object sender, EventArgs e) 
    { 
     var c = (Client) sender; 
     if (c.IsReceiving || c.IsSending) 
     { 
      return; 
     } 
     c.IsReceiving = true; 
     var wcfElement = c.GetFirstElementFromQueue(); 
     if (wcfElement == null) 
     { 
      c.IsReceiving = false; 
      return; 
     } 
     Clients[c].Invoke(this, new ServiceEventArgs { Person = c, WcfElement = wcfElement }); 
    } 

    public void ReadyToReceive(string userName) 
    { 
     var c = GetPerson(userName); 
     c.IsSending = false; 
     c.IsReceiving = false; 
     if (c.IsSynchronized) 
     { 
      SendMessageToClient(c, null); 
     } 
     else 
     { 
      PerformDataSync(c); 
     } 
    } 

    public bool SendWcfElement(WcfElement wcfElement) 
    { 
     var cl = GetPerson(wcfElement.UserName); 
     cl.IsSending = true; 
     if (wcfElement.WcfElementVersion != bazaDanych.WcfElementVersion) return false; 

     //method processes messages and if needed creates creates WcfElements which are added to every clients queue 
     return ifSuccess; 
    } 
    #endregion 
    #region private methods 
    private void MyEventHandler(object sender, ServiceEventArgs e) 
    { 
     try 
     { 
      _callback.ReceiveWcfElement(e.WcfElement); 
     } 
     catch (Exception ex) 
     { 
     } 
    } 
    #endregion 
} 
#endregion 

côté client dans un moment

#region Client class 
[DataContract] 
public class Client 
{ 
    #region Instance Fields 

    /// <summary> 
    /// The UserName 
    /// </summary> 
    [DataMember] 
    public string UserName { get; set; } 

    [DataMember] 
    public WcfElement ElementsVersions { get; set; } 

    private bool _isSynchronized; 
    public bool IsSynchronized 
    { 
     get { return _isSynchronized; } 
     set 
     { 
      _isSynchronized = value; 
     } 
    } 

    public bool IsSending { get; set; } 
    public bool IsReceiving { get; set; } 

    private List<WcfElement> ElementsQueue { get; set; } 
    public object LockObj { get; set; } 

    public void AddElementToQueue(WcfElement wcfElement, int position = -1) 
    { 
     try 
     { 
      lock (LockObj) 
      { 
       if (ElementsQueue == null) ElementsQueue = new List<WcfElement>(); 
       if (position != -1 && position <= ElementsQueue.Count) 
       { 
        try 
        { 
         ElementsQueue.Insert(position, wcfElement); 
        } 
        catch (Exception e) 
        { 
        } 
       } 
       else 
       { 
        try 
        { 
         //dodaje na koncu 
         ElementsQueue.Add(wcfElement); 
        } 
        catch (Exception e) 
        { 
        } 
       } 
      } 
     } 
     catch (Exception e) 
     { 
     } 
    } 

    public WcfElement GetFirstElementFromQueue() 
    { 
     if (ElementsQueue == null) return null; 
     if (ElementsQueue.Count > 0) 
     { 
      var tmp = ElementsQueue[0]; 
      ElementsQueue.RemoveAt(0); 
      return tmp; 
     } 
     return null; 
    } 

    #endregion 
    #region Ctors 
    /// <summary> 
    /// Assign constructor 
    /// </summary> 
    /// <param name="userName">The userName to use for this client</param> 
    public Client(string userName) 
    { 
     UserName = userName; 
    } 
    #endregion 
} 
#endregion 

ProxySingletion:

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)] 
public sealed class ProxySingleton : IClientCallback 
{ 
    #region Instance Fields 

    private static ProxySingleton _singleton; 
    public static bool IsConnected; 
    private static readonly object SingletonLock = new object(); 
    private ServiceProxy _proxy; 
    private Client _myPerson; 

    private delegate void HandleDelegate(Client[] list); 

    private delegate void HandleErrorDelegate(); 

    //main proxy event 
    public delegate void ProxyEventHandler(object sender, ProxyEventArgs e); 

    public static event ProxyEventHandler ProxyEvent; 
    //callback proxy event 
    public delegate void ProxyCallBackEventHandler(object sender, ProxyCallBackEventArgs e); 

    public static event ProxyCallBackEventHandler ProxyCallBackEvent; 

    #endregion 

    #region Ctor 

    /// <summary> 
    /// Blank constructor 
    /// </summary> 
    private ProxySingleton() 
    { 
    } 

    #endregion 

    #region Public Methods 

    #region IClientCallback implementation 
    public void ReceiveWcfElement(WcfElement wcfElement) 
    { 
     //process received data 
     //... 
     ReadyToReceive(); 
    } 
    #endregion 

    public void ReadyToReceive() 
    { 
     try 
     { 
      if (bazaDanych.Dane.Client.IsSending) return; 
      var w = bazaDanych.Dane.Client.GetFirstElementFromQueue(); 
      if (w != null) 
      { 
       SendWcfElement(w); 
       return; 
      } 
      _proxy.ReadyToReceive(bazaDanych.Dane.Client.UserName, source, ostatniTypWiadomosci); 
     } 
     catch (Exception) 
     { 
      IsConnected = false; 
     } 
    } 

    public static WcfElement CurrentWcfElement; 
    public bool SendWcfElement(WcfElement wcfElement) 
    { 
     if (bazaDanych.Dane.Client.IsReceiving) 
     { 
      bazaDanych.Dane.Client.AddElementToQueue(wcfElement); 
      return true; 
     } 
     bazaDanych.Dane.Client.IsSending = true; 
     foreach (var wcfElementSplited in WcfElement.SplitWcfElement(wcfElement, true)) 
     { 
      CurrentWcfElement = wcfElementSplited; 
      try 
      { 
       var r = _proxy.SendWcfElement(wcfElementSplited); 
       CurrentWcfElement = null; 
      } 
      catch (Exception e) 
      { 
       IsConnected = false; 
       return false; 
      } 
     } 
     bazaDanych.Dane.Client.IsSending = false; 
     ReadyToReceive(); 
     return true; 
    } 

    public void ListenForConnectOrReconnect(EventArgs e) 
    { 
     SendWcfElement(WcfElement.GetVersionElement());//send wcfelement for perform PerformDataSync 
     ReadyToReceive(); 
    } 

    public static bool IsReconnecting; 
    public bool ConnectOrReconnect(bool shouldRaiseEvent = true) 
    { 
     if (IsReconnecting) 
     { 
      return IsConnected; 
     } 
     if (IsConnected) return true; 
     IsReconnecting = true; 
     bazaDanych.Dane.Client.IsReceiving = false; 
     bazaDanych.Dane.Client.IsSending = false; 
     bazaDanych.Dane.Client.IsSynchronized = false; 
     try 
     { 
      var site = new InstanceContext(this); 
      _proxy = new ServiceProxy(site); 
      var list = _proxy.Login(bazaDanych.Dane.Client, bazaDanych.Dane.UserPassword, bazaDanych.Dane.UserIsAuto, bazaDanych.Dane.UserIsSuperMode); 
      bazaDanych.Dane.UserRights.Clear(); 
      bazaDanych.Dane.UserRights.AddRange(list); 
      IsConnected = true; 
      if (shouldRaiseEvent) ConnectOrReconnectEvent(null); 
     } 
     catch (Exception e) 
     { 
      IsConnected = false; 
     } 
     IsReconnecting = false; 

     return IsConnected; 
    } 
} 
#endregion 

Au moment de mes œuvres app comme ceci: Après connexion réussie chaque client envoie WcfElements (qui contient tas de lis t avec des identifiants et des versions d'éléments). Ensuite, il envoie un message à sens unique ReadyToReceive qui après la connexion déclenche la méthode performingync. Cette méthode prépare les données pour le client et envoie le premier en utilisant la méthode de réception unidirectionnelle. S'il y a plus d'un wcfelement à envoyer alors seulement le dernier est marqué comme dernier. Le client répond avec ReadyToReceive après chaque réception réussie du serveur. Tout jusqu'à ce point fonctionne très bien. Le problème commence plus tard. La plupart du temps, les paquets sont perdus (méthode receiveWcfElement). Le serveur a marqué que le client reçoit et peut-être traite le message et attend le paquet readytoreceive, qui ne sera jamais envoyé à cause d'un élément perdu.

Je l'ai fait comme ça parce que, autant que je sache, le client ne peut pas envoyer et recevoir en même temps. J'ai essayé ceci et j'ai eu ce problème: Si le client envoie wcfElement avec la méthode SendWcfElement et le serveur en raison du traitement de cet élément créé un autre élément qui était supposé être retourné au client alors le client aurait un proxy défectueux si le callback était envoyé avant sendWcfElement retourné vrai indiquant que la méthode a été complétée.

Maintenant, je me demande s'il est possible pour le client d'envoyer et de recevoir en même temps en utilisant des méthodes bidirectionnelles? Je me suis retrouvé avec des services (deux connexions)

+0

Pourriez-vous s'il vous plaît fournir un code? Et formatez votre question afin qu'un humain puisse la lire s'il vous plait. – Felix

+0

Je viens d'ajouter du code et j'ai essayé de résumer mon problème mieux. J'espère que c'est plus clair maintenant. – Gwynnbleid1

Répondre

1

Un pour la connexion du client au serveur et un autre avec le callback qui gère la connexion du serveur au client.

Questions connexes