0

現時点での私のコードは次のようになります:
サーバー側:

#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

すぐにクライアント側

#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

現時点で、私のアプリは次のように動作します: ログインが成功すると、すべてのクライアントが WcfElements (要素の ID とバージョンを含む一連のリストを含む) を送信します。次に、ログイン後に performsync メソッドを起動する ReadyToReceive 一方向メッセージを送信します。このメソッドは、クライアント用のデータを準備し、一方向の受信メソッドを使用して最初に送信します。送信する wcfelement が複数ある場合は、最後の 1 つだけが最後としてマークされます。サーバーからの受信が成功するたびに、クライアントは ReadyToReceive で応答します。この時点まではすべて非常にうまく機能します。問題は後で始まります。ほとんどのパッケージは失われます (メソッド receiveWcfElement)。サーバーは、クライアントがメッセージを受信して​​処理している可能性があることをマークし、readytoreceive パケットを待機していますが、要素が失われたために送信されることはありません。

私が知る限り、クライアントは同時に送受信できないため、このようにしました。私はこれを試してみましたが、この問題が発生しました: クライアントが SendWcfElement メソッドを使用して wcfElement を送信し、サーバーがこの要素を処理するために、クライアントに ssend を返すはずの別の要素を作成した場合、sendWcfElement が true を返す前にコールバックが送信された場合、プロキシに障害が発生したクライアントメソッドが完了したことを示します。

ここで、クライアントが双方向の方法を使用して同時に送受信できるかどうか疑問に思いますか?

4

1 に答える 1

1

最終的にサービス(2つの接続)になりました。1 つはクライアントからサーバーへの接続用で、もう 1 つはサーバーからクライアントへの接続を処理するコールバック付きです。

于 2012-04-13T18:15:05.880 に答える