WCF的回调操作

回调操作在实际系统中应用的非常广泛,该机制使得服务端可以回调客户端操作,而不需要在客户端实现、寄宿新的服务契约。

回调操作必须使用支持双向通信的绑定如NetTcpBinding、NetNamedPipeBinding、WSDualHttpBinding。

双程操作实例

下面给出一个双程操作的示例,考虑这要的需求,系统的客户端需要每隔一段时间向服务端发送消息,以通知服务端客户端运行正常。一旦这要的消息终端,服务端将立刻采取相应的措施,如通知系统支持人员等。这样的需求在大型系统中非常常见,一般称呼这样的通知为“心跳”,表示该消息反应了客户端的健康状态。

1、定义服务契约

定义服务契约(Service Contract)包括:

回调契约:IheartbeatCallback.cs(心跳频率回调操作)。

服务契约:IheartbeatService.cs(心跳服务契约)。

回调契约属于服务契约的一部分,在WCF中使用ServiceContract的CallbackContract属性可以定义一个服务契约。

1.1 定义回调契约

 

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

namespace WCF.Third.Contract
{
    /// <summary>
    /// 回调契约
    /// </summary>
    public interface IheartbeatCallback
    {
        /// <summary>
        /// 回调操作,调整心跳频率
        /// </summary>
        /// <param name="seconds">新的频率</param>
        [OperationContract(IsOneWay=true)]
        void UpdateInterval(int seconds);
    }
}

 

1.2 定义服务契约

 

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

namespace WCF.Third.Contract
{
    /// <summary>
    /// 心跳服务契约
    /// </summary>
    [ServiceContract(CallbackContract=typeof(IheartbeatCallback))]
    public interface IheartbeatService
    {
        [OperationContract]
        void Heartbeat();
    }
}

2、创建服务

创建HeartbeatService.cs类,实现IheartbeatService服务契约接口。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using WCF.Third.Contract;

namespace WCF.Third.Service
{
    /// <summary>
    /// 心跳服务
    /// </summary>
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public class HeartbeatService:IheartbeatService
    {
        private bool _highLoad = false;      //是否处于高负荷状态
        private bool _changed = false;       //状态是否已经改变
        //不同负荷下的心跳周期
        private int _highloadInterval = 5;   //周期是5
        private int _lowloadInterval = 2;    //周期是2

        //设置负荷状态
        public bool HighLoad
        {
            set 
            {
                lock (this)    //确定单线程访问状态
                {
                    if (_highLoad != value)
                    { 
                        //调整状态
                        _changed = true;
                        _highLoad = value;
                        Console.WriteLine("HighLoad已经被设置为:{0}", value);
                    }
                }
            }
        }

        /// <summary>
        /// 实现心跳契约
        /// </summary>
        public void Heartbeat()
        {
            //如果符合已经更改,则回调客户端,更新心跳频率
            if (_changed)
            {
                IheartbeatCallback callback = OperationContext.Current.GetCallbackChannel<IheartbeatCallback>();
                callback.UpdateInterval(_highLoad ? _highloadInterval : _lowloadInterval);
            }
            Console.WriteLine("{0}收到心跳", DateTime.Now.ToString());
        }

    }
}

3、实现服务寄宿

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using WCF.Third.Service;
using WCF.Third.Contract;

namespace WCF.Third.Host
{
    class Program
    {
        private const string serviceAddressUrl = "net.pipe://localhost/HeartbeatService";              //服务地址
        private const string serviceMetadataUrl = "net.pipe://localhost/HeartbeatService/metadata";    //元数据发布的源地址

        static void Main(string[] args)
        {
            try
            {
                //为了能访问服务对象,这里手动创建服务对象并以该对象创建寄主对象
                HeartbeatService instance = new HeartbeatService();
                using (ServiceHost host = new ServiceHost(instance))
                {
                    //添加一个终结点
                    Uri address = new Uri(serviceAddressUrl);       //地址
                    Binding binding = new NetNamedPipeBinding();    //绑定
                    Type serviceType = typeof(IheartbeatService);   //契约
                    host.AddServiceEndpoint(serviceType, binding, address);

                    host.Opened += delegate
                    {
                        Console.WriteLine("服务已经启动!");
                    };
                    host.Open();

                    while (true)
                    {
                        char command = Convert.ToChar(Console.Read());
                        //通过ServiceHost的SingletonInstance属性来获取寄宿实例
                        HeartbeatService service = (HeartbeatService)host.SingletonInstance;
                        switch (command)
                        { 
                            case 'q':   //输入q表示退出程序
                                return;
                            case 'h':   //输入h表示切换到高负荷
                                { 
                                    
                                    if (service != null)
                                    {
                                        service.HighLoad = true;
                                    }
                                    break;
                                }
                            case 'l':   //输入l表示切换到底负荷
                                {
                                    if (service != null)
                                    {
                                        service.HighLoad = false;
                                    }
                                    break;
                                }
                            default:
                                break;
                        }
                    }

                }
            }
            catch (Exception ex)    //捕捉异常
            {
                Console.WriteLine(ex.ToString());
                Console.Read();
            }
        }
    }
}

4、创建客户端调试服务

双程操作的客户端需要公开回调终结点以供服务端进行回调。为了方便双程操作的客户端编程,WCF提供了DuplexClientBase<T>类型。客户端初始化DuplexClientBase<T>代理对象时,需要提供带有回调对象的实例上下文对象。代理会围绕回调上下文创建一个终结点,回调终结点会使用和服务端相同的绑定和传输方式。

4.1 创建客户端类

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

namespace WCF.Third.Client
{
    //回调契约实现
    public class Client:IheartbeatCallback
    {
        private TimeSpan _interval = TimeSpan.FromSeconds(2);    //心跳频率
        public TimeSpan Interval{
            get{return _interval;}
        }

        public void UpdateInterval(int seconds)
        {
            lock(this)
            {
                 _interval = TimeSpan.FromSeconds(seconds);
            }
        }
    }

    /// <summary>
    /// 回调契约
    /// </summary>
    public interface IheartbeatCallback
    {
        /// <summary>
        /// 回调操作,调整心跳频率
        /// </summary>
        /// <param name="seconds">新的频率</param>
        [OperationContract(IsOneWay = true)]
        void UpdateInterval(int seconds);
    }

    /// <summary>
    /// 心跳服务契约
    /// </summary>
    [ServiceContract(CallbackContract = typeof(IheartbeatCallback))]
    public interface IheartbeatService
    {
        [OperationContract]
        void Heartbeat();
    }

    //客户端代理的实现
    public class HeartbeatProxy : DuplexClientBase<IheartbeatService>, IheartbeatService
    {
        public HeartbeatProxy(InstanceContext context, Binding binding, EndpointAddress remoteAddress) : base(context, binding, remoteAddress) { }
        public void Heartbeat()
        {
            Channel.Heartbeat();
        }
    }
}

4.2 运行客户端类

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

namespace WCF.Third.Client
{
    class Program
    {
        private const string serviceAddressUrl = "net.pipe://localhost/HeartbeatService";   
        static void Main(string[] args)
        {
            try
            {
                Client client = new Client();
                using(HeartbeatProxy proxy = new HeartbeatProxy(new InstanceContext(client),new NetNamedPipeBinding(),new EndpointAddress(serviceAddressUrl)))
                {
                    Console.WriteLine("客户端运行");
                    while (true)
                    {
                        proxy.Heartbeat();                //发送心跳
                        Thread.Sleep(client.Interval);    //等待一个周期时间
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally {
                Console.Read();
            }
        }
    }
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pan_junbiao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值