WCF使用入门(一)【基于双工通信的WCF应用】

没有见过地狱的眼睛,永远也看不到光明


IChatCallback.cs的代码:

 //由于回调契约本质也是一个服务契约,所以定义方式和一般意义上的服务契约基本一样。有一点不同的是,由于定义IChatService的时候已经通过[ServiceContract(CallbackContract=typeof(IChatCallback))]指明ICallback是一个服务契约了,所以ICallback不再需要添加ServiceContractAttribute特性。IChatCallback定义了一个服务操作MessageBroadcast用于消息发送,由于服务端不需要回调的返回值,索性将回调操作也设为单向方法。
    public interface IChatCallback
    {
        [OperationContract(IsOneWay=true)]
        void MessageBroadcast(string msg);
    }

IChatService.cs代码:

  //定义服务契约和回调契约
    //首先进行服务契约的定义,我们照例通过接口(IChatService)的方式定义服务契约,作用于指定Send操作,我们通过OperationContractAttribute特性的IsOneway属性将操作定义成单向的操作,这意味着客户端仅仅是向服务端发送一个请求,并不会通过回复消息得到任何结果。
    [ServiceContract(SessionMode=SessionMode.Required, CallbackContract=typeof(IChatCallback))]
    public interface IChatService
    {
        [OperationContract(IsOneWay=true,IsInitiating=true)]
        void Register();
        [OperationContract(IsOneWay=true,IsInitiating=false)]
        void Send(string message);
    }

ChatService.cs代码:

 //实现服务

    //在实现了上面定义的服务契约IChatService的服务ChatService中,实现了Send操作,完成工作。
     //结果显示是通过回调的方式实现的,所以需要借助于客户端提供的回调对象(该对象在客户端调用ChatService的时候指定,在介绍客户端代码的实现的时候会讲到)。
    //在WCF中,回调对象通过当前OperationContext的GetCallback<T>方法获得(T代表回调契约的类型)。
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single //, ConcurrencyMode=ConcurrencyMode.Reentrant 
        )] 
    public class ChatService : IChatService
    {
        List<string> messages = new List<string>();
        List<IChatCallback> clients = new List<IChatCallback>();

        public void Send(string message)
        {
            messages.Add(message);
            foreach(var client in clients)
            {
                if (client != Callback)
                {
                    client.MessageBroadcast(message);
                }
                //如果服务正在调用调用者,那么服务必须具有ConcurrencyMode.Reentrant 或者 Multiple,否则将发生死锁。
                //else
                //{
                //    client.MessageBroadcast(message);
                //}
            }            
        }

        IChatCallback Callback
        {
            get
            {
                //注: OperationContext在WCF中是一个非常重要、也是一个十分有用的对象,它代表服务操作执行的上下文。
                //我们可以通过静态属性Current(OperationContext.Current)得到当前的OperationContext。
                //借助OperationContext,我们可以在服务端或者客户端获取或设置一些上下文,比如在客户端可以通过它为出栈消息(outgoing message)添加SOAP报头,以及HTTP报头(比如Cookie)等。
                //在服务端,则可以通过OperationContex获取在客户端设置的SOAP报头和HTTP报头。关于OperationContext的详细信息,可以参阅MSDN在线文档。
                return OperationContext.Current.GetCallbackChannel<IChatCallback>();
            }
        }

        public void Register()
        {
            clients.Add(Callback);            
        }
    }
 class Program
    {
        static void Main(string[] args)
        {
            //启动新的聊天服务器。
            using(var host = new ServiceHost(typeof(ChatService)))
            {
                host.Open();
                Console.WriteLine("聊天服务器运行 " + string.Join(", ", host.Description.Endpoints.Select(s => s.Address)));
                Console.WriteLine("按任何一个键退出。");
                Console.ReadKey();
            }

        }
    }
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WcfDuplexChat.ChatService">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:12000/"/>
          </baseAddresses>
        </host>
        <endpoint address="ChatServer" binding="wsDualHttpBinding" contract="Contract.IChatService">

        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange">

        </endpoint> 
      </service>      
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

ChatCallbackHandler.cs代码:

  //服务回调的实现
    public class ChatCallbackHandler : Contract.IChatCallback, ChatService.IChatServiceCallback
    {

        public void MessageBroadcast(string msg)
        {
            Console.WriteLine(msg);
        }
    }

app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
      <!--Not used configured in code-->
        <!--<bindings>
            <wsDualHttpBinding>
              <binding name="WSDualHttpBinding_IChatService" ></binding>
            </wsDualHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:12000/ChatServer" binding="wsDualHttpBinding"
                bindingConfiguration="WSDualHttpBinding_IChatService" contract="ChatService.IChatService"
                name="WSDualHttpBinding_IChatService">
                <identity>
                    <userPrincipalName value="simtex\Simon Pedersen" />
                </identity>
            </endpoint>
        </client>-->

      <!--服务寄宿-->
      <bindings>
            <wsDualHttpBinding>
                <binding name="WSDualHttpBinding_IChatService" />
            </wsDualHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:12000/ChatServer" binding="wsDualHttpBinding"
                bindingConfiguration="WSDualHttpBinding_IChatService" contract="ChatService.IChatService"
                name="WSDualHttpBinding_IChatService">
                <identity>
                    <userPrincipalName value="simtex\Simon Pedersen" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Program.cs代码:

 internal class Program
    {
        //基于双工通信的WCF应用
        private static void Main(string[] args)
        {
            //让用户输入端口,使多个聊天客户端可以在同一台机器上运行。
            int port = 12001;
            if (args.Length < 1)
            {
                Console.WriteLine("输入端口号:");
                port = int.Parse(Console.ReadLine());
            }
            else
            {
                port = int.Parse(args[0]);
            }
            var instanceContext = new InstanceContext(new ChatCallbackHandler());

            //注:在WCF预定义绑定类型中,WSDualHttpBinding和NetTcpBinding均提供了对双工通信的支持,但是两者在对双工通信的实现机制上却有本质的区别。WSDualHttpBinding是基于HTTP传输协议的;而HTTP协议本身是基于请求-回复的传输协议,基于HTTP的通道本质上都是单向的。WSDualHttpBinding实际上创建了两个通道,一个用于客户端向服务端的通信,而另一个则用于服务端到客户端的通信,从而间接地提供了双工通信的实现。而NetTcpBinding完全基于支持双工通信的TCP协议。
            var binding = new WSDualHttpBinding();
            binding.ClientBaseAddress = new Uri(string.Format("http://localhost:{0}/ChatClient", port));
            var endpoint = new EndpointAddress("http://localhost:12000/ChatServer");
            //using(var client = new ChatService.ChatServiceClient(instanceContext,binding, endpoint)) //Visual Studio生成的客户端代理。

            //双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息。基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和单项模式)消息交换的组合。双工MEP又具有一些变体,比如典型的订阅-发布模式就可以看成是双工模式的一种表现形式。双工消息交换模式使服务端回调(Callback)客户端操作成为可能。
            using (var factory = new DuplexChannelFactory<Contract.IChatService>(instanceContext, binding, endpoint))
                //手工创造Channelfactory
            {
                var client = factory.CreateChannel();
                try
                {
                    //client.Open();
                    client.Register();
                    Console.WriteLine("聊天客户端准备运行: " + binding.ClientBaseAddress);
                    do
                    {
                        var message = Console.ReadLine();
                        client.Send(message);
                    } while (true);
                }
                catch (Exception ex)
                {
                    Console.Write(ex.ToString());
                }
            }
            Console.ReadKey();
        }
    }

项目运行结果如图:

这里写图片描述


这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值