WCF双工服务(TCP绑定)的客户端可以同时发送和接收吗?

发布于 2024-12-11 11:28:29 字数 13383 浏览 0 评论 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 和元素版本的列表)。然后它发送 ReadyToReceive 单向消息,该消息在登录后触发 PerformSync 方法。该方法为客户端准备数据,并使用单向接收方法首先发送数据。如果有多个 wcf 元素要发送,则只有最后一个元素被标记为最后一个。每次从服务器成功接收后,客户端都会以 ReadyToReceive 进行响应。到目前为止,一切都运行良好。问题稍后开始。大多数包都会丢失(方法 receiveWcfElement)。服务器已标记客户端正在接收并且可能正在处理消息,并且正在等待准备接收数据包,由于丢失元素,该数据包永远不会发送。

我这样做是因为据我所知客户端不能同时发送和接收。我已经尝试过并遇到这个问题: 如果客户端使用 SendWcfElement 方法发送 wcfElement,并且服务器由于处理此元素而创建了另一个应该发送回客户端的元素,那么如果在 sendWcfElement 返回 true 之前发送了回调(表明该方法已完成),则客户端会出现代理故障。

现在我想知道客户端是否可以使用两种方式同时发送和接收?

My code at the moment looks like this:
Server side:

#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

Client side in a 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

At the moment my app works like this:
After successful login every client sends WcfElements(which contains bunch of list with ids and versions of elements). Then it sends ReadyToReceive one way message which after login fires performsync method. That method prepares data for client and sends first of them using one way receive method. IF there is more than one wcfelement to send then only last one is marked as last. Client responds with ReadyToReceive after every successful receive from Server. All up to this point works quite well. Problem starts later. Mostly packages are lost (method receiveWcfElement). Server has marked that client is receiving and maybe processing message and is waitng for readytoreceive packet, which will never be send because of lost element.

I've made it like this because as far as I know client can't send and receive at the same time. I've tried this and got this problem:
If client send wcfElement with SendWcfElement method and server due to processing this element created another element which was supposed to be ssend back to client then client whoud have faulted proxy if callback was send before sendWcfElement returned true indicating that method was completed.

Now I wonder if it is possible for client to send and receive at the same time using two way methods ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

乙白 2024-12-18 11:28:29

我最终得到了服务(两个连接)。一种用于从客户端到服务器的连接,另一种用于处理从服务器到客户端的连接的回调。

I ended up with to services(two connections). One for connection from client to server and another with callback which handles connection from server to client.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文