2011-10-22 44 views
0

我此刻的代码如下所示:
服务器端:在某一时刻WCF双工服务的客户端(TCP绑定)是否可以同时发送和接收?

#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

#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 

客户端:

[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 

此刻我的应用程序的作品像这样: 成功登录后,每个客户端发送WcfElements(其中包含一堆lis t带有ID和版本的元素)。然后发送ReadyToReceive单向消息,登录后触发执行同步方法。该方法为客户端准备数据并使用单向接收方法首先发送数据。如果有多个wcfelement发送,则只有最后一个被标记为最后一个。每次成功从服务器接收后,客户端都会响应ReadyToReceive。所有这一切都工作得很好。问题随后开始。大多数包丢失(方法receiveWcfElement)。服务器已标记客户端正在接收并可能正在处理消息,并正在等待准备就绪的数据包,因为丢失的元素将永远不会发送。

我已经这样做了,因为据我所知客户端不能同时发送和接收。我试过这个,并得到了这个问题: 如果客户端发送wcfElement与SendWcfElement方法和服务器由于处理此元素创建了另一个元素,应该是ssend回客户端然后客户端有问题的代理,如果回调发送之前发送sendWcfElement返回表示该方法已完成。

现在我想知道客户端是否可以使用双向方法同时发送和接收数据?

+0

您能否提供一些代码?并格式化您的问题,以便人们可以阅读它。 – Felix

+0

我刚刚添加了代码,并试图更好地总结我的问题。希望现在更清楚。 – Gwynnbleid1

回答

1

我结束了与服务(两个连接)。一个用于从客户端到服务器的连接,另一个用于处理从服务器到客户端的连接的回调。

相关问题