WCF技术剖析(卷1)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.1.3 案例演示:如何直接通过绑定进行消息通信

现在通过一个简单的例子,演示如何直接通过绑定(在这里使用最简单的BasicHttpBinding)进行消息通信。整个解决方案由两个Console应用组成,它们分别模拟消息的监听方与发送方,案例应用的结构如图3-3所示。

图3-3 基于绑定通信案例的应用结构

步骤一 创建请求监听端应用程序

        namespace Artech.MessagingViaBinding.Listener
        {
            class Program
            {
                static void Main(string[] args)
                {
                      Uri listenUri      = new Uri("http://127.0.0.1:9999/listener");
                      Binding binding    = new BasicHttpBinding();
                      IChannelListener<IReplyChannel> channelListener = binding.
                        BuildChannelListener<IReplyChannel>(listenUri);
                      channelListener.Open();
                      IReplyChannel channel = channelListener.AcceptChannel
                        (TimeSpan.MaxValue);
                      channel.Open();
                      Console.WriteLine("开始监听...");
                      while (true)
                      {
                            (TimeSpan.MaxValue);
                            requestContext.RequestMessage);
                        requestContext.Reply(CreateReplyMessage(binding));
                      }
                }
                static Message CreateReplyMessage(Binding binding)
                {
                      string action    = "urn:artech.com/reply";
                      string body      = "这是一个简单的回复消息!";
                      return Message.CreateMessage(binding.MessageVersion,action,body);
                }
            }
        }

我们通过代码分解,对代码执行的流程进行简单的介绍:首先,BasicHttpBinding对象被创建出来,调用绑定对象的BuildChannelListener<IReplyChannel>方法,创建IChannelListener<IReplyChannel>对象。该方法接收一个URI类型的参数,表示监听地址。调用Open方法开启创建出来的信道监听器(IChannelListener<IReplyChannel>)对象。

        Uri listenUri    = new Uri("http://127.0.0.1:9999/listener");
        Binding binding  = new BasicHttpBinding();
        IChannelListener<IReplyChannel> channelListener =
          binding.BuildChannelListener<IReplyChannel>(listenUri);
        channelListener.Open();

在信道监听器通过绑定对象被成功创建并开启后,通过调用AcceptChannel方法创建信道栈进行请求的监听,信道栈通过若干信道有序连接而成,方法最终返回的是位于栈顶的信道对象。

        IReplyChannel channel = channelListener.AcceptChannel(TimeSpan.MaxValue);
        channel.Open();

一旦信道栈被成功创建,那么就可以利用它对请求消息进行接收、处理了。在本例中,通过一个无限循环来处理来自不同客户端的消息请求。请求的接收通过IReplyChannel的ReceiveRequest方法实现。该方法接受一个TimeSpan类型参数,代表该方法从执行开始成功接受请求的时间,由于客户端请求的频率不确定,在这里给它指定了一个最大值。ReceiveRequest并不像我们想象的一样返回一个代表请求消息的Message对象,而是返回一个RequestContext对象,并通过该对象将创建的回复消息回复给请求方。

注: 在请求/回复消息交换模式中,RequestContext是连接请求和回复的纽带。RequestContext不仅仅是对请求消息的封装,还可以用于回复消息的发送。在本例中,我们通过它的RequestMessage属性得到请求消息,然后通过CreateReplyMessage方法创建一个回复消息,通过Reply方法回复给发送方。

        RequestContext requestContext = channel.ReceiveRequest(TimeSpan.MaxValue);
        Console.WriteLine("接收到请求消息:\n{0}", requestContext.RequestMessage);
        requestContext.Reply(CreateReplyMessage(binding));

在创建回复消息的时候,须要考虑消息的版本问题。在WCF中,消息版本通过System.ServiceModel.Channels.MessageVersion类表示,一般消息的版本包含两个部分的内容:SOAP的版本和WS-Addressing的版本,它们分别通过System.ServiceModel.Channels. AddressingVersion和System.ServiceModel.Channels.AddressingVersion表示。对一个绑定对象来说,并不是所有的MessageVersion都支持,为此,在创建回复消息的时候,通过具体的绑定对象确定回复消息的MessageVersion。

        static Message CreateReplyMessage(Binding binding)
        {
            string action = "urn:artech.com/reply";
            string body = "这是一个简单的回复消息!";
            return Message.CreateMessage(binding.MessageVersion, action, body);
        }

步骤二 创建消息发送端应用程序

        namespace Artech.MessagingViaBinding.Sender
        {
            class Program
            {
                static void Main(string[] args)
                {
                      Uri listenUri = new Uri("http://127.0.0.1:9999/listener");
                      Binding binding = new BasicHttpBinding();
                      IChannelFactory<IRequestChannel> channelFactory =
                        binding.BuildChannelFactory<IRequestChannel>();
                      channelFactory.Open();
                      IRequestChannel channel = channelFactory.CreateChannel(new
                        EndpointAddress(listenUri));
                      channel.Open();
                      Message replyMessage = channel.Request(CreateRequestMessage
                        (binding));
                      Console.WriteLine("接收到回复消息\n{0}", replyMessage);
                      Console.Read();
                }
                static Message CreateRequestMessage(Binding binding)
                {
                        string action = "urn:artech.com/request";
                        string body = "这是一个简单的请求消息!";
                        return Message.CreateMessage(binding.MessageVersion,action,body);
                }
            }
        }

发送端的程序和监听端类似,在监听程序中,通过绑定创建了IChannelListener<IReplyChannel>对象,并调用AcceptChannel方法创建监听信道栈并进行消息的接收与处理。与之相对,在客户端我们通过绑定创建IChannelFactory<IRequestChannel>对象,并通过该对象创建信道栈进行请求消息的发送。

        Binding binding = new BasicHttpBinding();
        IChannelFactory<IRequestChannel> channelFactory =
        binding.BuildChannelFactory<IRequestChannel>();
        channelFactory.Open();

输出结果

成功创建了请求监听端和消息发送端的应用程序之后,先后运行这两个控制台的应用程序,对于监听端,将会出现如图3-4的输出结果。<s:Envelope>包含的内容正是在消息发送端生成并发送的SOAP消息的内容。

图3-4 基于绑定通信案例监听端运行结果

由于监听端在接收到请求消息后,向请求端返回了一个回复消息,这个回复消息将会成功输出到消息请求端。图3-5是消息发送端运行时的真实输出结果。

图3-5 基于绑定通信案例请求端运行结果