zoukankan      html  css  js  c++  java
  • WCF: Server Calls Back to Clients

    Server 对Client的回调是很有用的, 比如Client向服务器提出一个请求, 而那个操作服务器需要处理很长时间, 如果不使用Callback, Client必须阻塞线程并等待服务器的响应, 但如果使用异步Callback, Client可以在提出请求后立刻返回当前线程, 而服务器则再操作完成之后执行Client自定义的回调方法.

    WCF对于这种服务器回调客户端提供了非常好的支持, 但这只支持NetTcpBinding和NamedPipeBinding, 对于Http是不支持的...

    下面使用一个简单的NetTcpBinding来示例如何建立和使用服务器异步回调的服务.

    首先定义服务器的Contract:

        [ServiceContract(CallbackContract = typeof(IBankServiceCallback))]

        public interface IBankService

        {

            [OperationContract(IsOneWay = true)]

            void LongTimeOperation();

        }

    这个服务叫BankService, 但它只有一个LongTimeOperation方法. CallbackContract 定义了回调的接口, 注意一个ServiceContract只能有个CallbackContract. 而把方法定义成IsOneWay是异步调用所必须的.

    回调接口:

        public interface IBankServiceCallback

        {

            [OperationContract]

            void OperationComplete(DateTime time);

        }

    这个回调接口需要Client实现, 但服务器端必须知道如何调用Callback, 所以定义一个接口是必须的.

    BankService的实现:

        [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]

        public class BankService : IBankService

        {

            #region IBankService Members

     

            public void LongTimeOperation()

            {

                Thread.Sleep(10000); //模拟长时间操作...

                IBankServiceCallback callback = OperationContext.Current.GetCallbackChannel<IBankServiceCallback>();

                callback.OperationComplete(DateTime.Now);

            }

     

            #endregion

        }

    ConcurrencyMode = ConcurrencyMode.Reentrant 这句指定了服务的方法可以被重复调用(但是是单线程的), 如果不指定, 则默认是 ConcurrencyMode.Single , 导致在回调过程产生死锁..

    IBankServiceCallback callback = OperationContext.Current.GetCallbackChannel<IBankServiceCallback>() 这句获得可以调用Client实现的Callback方法的代理.

    下面是Client对Callback接口的实现. 当服务器回调时, 客户端将会调用这个方法:

            public class MyCallback : IBankServiceCallback

            {

                #region IBankServiceCallback Members

     

                public void OperationComplete(DateTime time)

                {

                    Console.WriteLine("OperationComplete @ " + DateTime.Now.ToLongTimeString());

                }

     

                #endregion

            }

    OK..该定义的都定义了, 下面用一个Main函数模拟服务器和客户端的交互:

            [STAThread]

            static void Main()

            {

                string uriStr = "net.tcp://localhost:10101/BankService/";

                Uri uri = new Uri(uriStr);

                NetTcpBinding tcp = new NetTcpBinding();

                EndpointAddress endpoint = new EndpointAddress(uriStr);

     

                ServiceHost service = new ServiceHost(typeof(BankService), uri);

                service.AddServiceEndpoint(typeof(IBankService), tcp, uri);

                service.Open();

                Console.WriteLine("service ok, 'enter' to continue...");

                Console.ReadLine();

     

                InstanceContext context=new InstanceContext(new MyCallback());

                DuplexChannelFactory<IBankService> duplexCF = new DuplexChannelFactory<IBankService>(context, tcp, endpoint);

                IBankService serverSvc = duplexCF.CreateChannel();

                Console.WriteLine("client ok, 'enter' to continue...");

                Console.ReadLine();

                Console.WriteLine("start @ " + DateTime.Now.ToLongTimeString());

                serverSvc.LongTimeOperation();

                Console.WriteLine("server processing...");

                Console.ReadLine();

            }

    注意斜体部分, 首先建立一个InstanceContext, 包含了回调方法的实现对象, 然后使用DuplexChannelFactory建立服务对象.

    运行结果如下:

    service ok, 'enter' to continue...

    client ok, 'enter' to continue...

    start @ 13:43:56
    server processing...
    OperationComplete @ 13:44:06

    OVER~

  • 相关阅读:
    SharePoint 创建 Lookup 类型的Site Column解决跨站问题
    Thinking In Design Pattern——MVP模式演绎
    SharePoint自动化部署,利用SPSD工具包
    SharePoint自动化部署,利用PowerShell 导出/导入AD中的用户
    64位内核第十四讲,获取系统滴答数与日期时间
    内核中通过进程PID获取进程的全部路径
    内核中根据进程Pid获取卷的全目录
    内核中PID_HANDLE_OBJECT等互相转换
    获取指定句柄的类型号.
    获取句柄的类型以及对应的ID序号
  • 原文地址:https://www.cnblogs.com/Dah/p/517173.html
Copyright © 2011-2022 走看看