WCF 服务器/客户端回调,从客户端到服务器的回复

本文关键字:客户端 服务器 回复 回调 WCF | 更新日期: 2023-09-27 18:31:46

在我的客户端/服务器应用程序中,我想计算每个人客户端中的其他值。我使用回调进行了应用程序,但出了点问题。当我想要调用方法pipeproxy.polacz(S);时,我得到异常,该方法现在获取服务器值并写入服务器控制台。

例外情况是:

This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant or Multiple on CallbackBehaviorAttribute.

另一个问题是,所有客户端的这种功能的总和如何。例;

client 1: S = 1;  
client 2: S = 2;  
client 3: S = 3;

这个函数从所有客户端获取结果并求和。所以服务器将在服务器控制台中写入 6。

我的应用程序代码:

服务器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Interface;
namespace WCFapp
{
    class Program
    {
        static void Main(string[] args)
        {
            Klienci cust = new Klienci();
            cust.Connect();
        }
    }
}

.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Interface;
namespace WCFapp
{
     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class Klienci : IMessage
{
    private static List<ImessageCallback> subscribers =
        new List<ImessageCallback>();
    public void lista()
    {
        string nm = Console.ReadLine();
        if (nm == "1")
        {
            Console.WriteLine("Number of conected clients: " + subscribers.Count());
            funkcja();
        }
    }
    public void Connect()
    {
        using (ServiceHost host = new ServiceHost(
            typeof(Klienci), new Uri("net.tcp://localhost:8000")))
        {
            host.AddServiceEndpoint(typeof(IMessage),
                new NetTcpBinding(), "ISubscribe");
            try
            {
                host.Open();
                lista();
                Console.ReadLine();
                host.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
    public bool Subscribe()
    {
        try
        {
            ImessageCallback callback = OperationContext.Current.GetCallbackChannel<ImessageCallback>();
            if (!subscribers.Contains(callback))
                subscribers.Add(callback);
            Console.WriteLine("Client is conected ({0}).", callback.GetHashCode());
            return true;
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            return false;
        }
    }
    public bool Unsubscribe()
    {
        try
        {
            ImessageCallback callback = OperationContext.Current.GetCallbackChannel<ImessageCallback>();
            if (subscribers.Contains(callback))
                subscribers.Remove(callback);
            Console.WriteLine("Client is unconected ({0}).", callback.GetHashCode());
            return true;
        }
        catch
        {
            return false;
        }
    }
    public void funkcja()
    {
        int a = 1; int b = 3;
        subscribers.ForEach(delegate(ImessageCallback callback)
        {
            if (((ICommunicationObject)callback).State == CommunicationState.Opened)
            {
            Console.WriteLine("a= {0} , b= {1}", a, b);
            callback.klient_licz(a, b);
            a++;
            b++;
            }
        });
    }
    public void polacz(int S)
    {
        Console.WriteLine("Sum: {0}", S);
    }
  }
}

接口:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Interface
{
     [ServiceContract(CallbackContract = typeof(ImessageCallback), SessionMode = SessionMode.Required)]
public interface IMessage
{
    [OperationContract]
    void funkcja();
    [OperationContract]
    void polacz(int S);
    [OperationContract]
    bool Subscribe();
    [OperationContract]
    bool Unsubscribe();
}
[ServiceContract]
public interface ImessageCallback
{
    [OperationContract]
    void klient_licz(int a, int b);
}

}

客户:

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Interface;
namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            clients cl = new clients();
            if (cl.Conect() == true)
            {
                string tmp = Console.ReadLine();
                while (tmp != "EXIT")
                {
                    cl.SendMessage(tmp);
                    tmp = Console.ReadLine();
                }
            }
             cl.Close();
             Environment.Exit(0);
       }
   }
}

.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Interface;
namespace Client
{
    class clients : ImessageCallback, IDisposable
    {
        IMessage pipeProxy = null;
        public bool Conect()
        {
            DuplexChannelFactory<IMessage> pipeFactory =
                new DuplexChannelFactory<IMessage>(
                    new InstanceContext(this),
                    new NetTcpBinding(),
                    new EndpointAddress("net.tcp://localhost:8000/ISubscribe"));
            try
            {
                pipeProxy = pipeFactory.CreateChannel();
                pipeProxy.Subscribe();
               return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
        }
        public void Close()
        {
            pipeProxy.Unsubscribe();
        }

        public void klient_licz(int a, int b)
        {
            int S = a + b;
            Console.WriteLine("Sum= {0}", S);
            pipeProxy.polacz(S); //ERROR
        }
    }
}

WCF 服务器/客户端回调,从客户端到服务器的回复

这里的问题是,在回调方法klient_licz(由服务器调用)中,您正在进行另一个服务器调用。 这是不允许的,您的合同当前设置方式。

  1. 检查你是否真的需要这种行为。 你真的需要在回调接口 (klient_licz) 上对方法进行服务器调用 INSIDE 吗?

  2. 如果您确实需要此行为,那么您可以通过在回调接口上标记klient_licz调用 OneWay 来解决问题。 这意味着服务器对回调的调用不会阻塞,直到客户端返回(这是当前导致问题的原因,因为服务器正在等待客户端调用返回,但客户端调用正在等待对服务器的调用):

    [ServiceContract]  
    public interface ImessageCallback {  
            [OperationContract(IsOneWay = true)]  
            void klient_licz(int a, int b);  
    }
    
  3. 或者,您可以使用默认模式 Single 以外的并发模式标记回调暗示。 例如,重入如下所示 - 但请记住,这意味着对回调的调用不会很快编接到 UI thead 即将在线程池线程上,因此您必须调度以更新回调接口上的 UI from 方法:

    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]  
    class clients : ImessageCallback, IDisposable {  
     ...
    }
    

如果你想了解并发模式以及它如何影响执行,那么你真的需要做一些背景阅读,因为它确实有点复杂 - 但如果你没有这个背景,很难真正理解当你改变并发模式时会发生什么。 这篇 dasBlonde 博客文章很好地总结了不同的模式和行为 - 但您可能希望从一些更面向初学者的教程开始。