第3章 消息交换模式、拓扑和编排
3.1 消息交换模式
消息交换模式是“描述消息参与者之间交换消息的模板”。
在面向服务的世界里通常有三种类型的消息交换模式:数据报,请求/应答和双工。
数据报交换模式
数据报消息交换模式表示一个单向的消息发送,或者即发即弃的消息发送。如果需要应答数据报,就需要发送者和接收者之间建立一个新的连接来应答数据报。
数据报消息交换模式使用WSDL语言描述为一个包含wsdl:input和没有wsdl:output元素的操作。
数据报和WCF契约:
契约定义方法的返回值类型是void和ContractAttribute属性的IsOneWay属性值为True。
在WCF里,唯一避免回复消息的方法就是在操作属性里设置IsOneWay属性。默认值为false,这个设置让操作默认使用请求/应答消息交换模式。
//契约
using System.ServiceModel;
using System.ServiceModel.Channels;
namespace WcfServiceApp2
{
[ServiceContract(Name="OneWayContract", Namespace="http://andersoft.com/OneWayContract")]
public interface IOneWayContract
{
[OperationContract(
Name="GetData",
Action="urn:GetData",
IsOneWay=true)]
void GetData(Message message);
}
}
//Service实例
using System;
using System.IO;
using System.ServiceModel.Channels;
using System.Xml;
namespace WcfServiceApp2
{
public class OneWayService : IOneWayContract
{
public void GetData(Message message)
{
Console.WriteLine("Received a new message...");
FileStream fs = new FileStream("Message.xml", FileMode.Create);
XmlDictionaryWriter xdw = XmlDictionaryWriter.CreateTextWriter(fs, System.Text.Encoding.UTF8, false);
xdw.Close();
}
}
}
//Host代理端 监听Service
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using WcfServiceApp2;
namespace ServiceHostApp
{
class Program
{
static void Main(string[] args)
{
BindingElement[] bindingElements = new BindingElement[3];
bindingElements[0] = new TextMessageEncodingBindingElement();
bindingElements[1] = new OneWayBindingElement();
bindingElements[2] = new HttpTransportBindingElement();
Binding binding = new CustomBinding(bindingElements);
ServiceHost svc = new ServiceHost(typeof(OneWayService));
svc.AddServiceEndpoint(typeof(IOneWayContract), binding, new Uri("http://localhost:1234/OneWayContract"));
svc.Open();
Console.WriteLine("Service is ready...");
Console.ReadKey();
}
}
}
//发送端,发送数据报
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Xml;
using WcfServiceApp2;
namespace SenderApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press Enter when the sender is ready...");
Console.ReadKey();
BindingElement[] bindingElements = new BindingElement[3];
bindingElements[0] = new TextMessageEncodingBindingElement();
bindingElements[1] = new OneWayBindingElement();
bindingElements[2] = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElements);
EndpointAddress endpointAddress = new EndpointAddress(new Uri("http://localhost:1234/OneWayContract"));
ChannelFactory<IOneWayContract> cf = new ChannelFactory<IOneWayContract>(binding, endpointAddress);
IOneWayContract oc = cf.CreateChannel();
Message ms = GenerateMessage();
ms.Headers.Action = "urn:GetData";
Console.WriteLine("Send Message...");
oc.GetData(ms);
}
//先与Writer写入流中,然后流的当前位置归0,与Reader去读流
private static Message GenerateMessage()
{
MemoryStream ms=new MemoryStream();
XmlDictionaryWriter writer=XmlDictionaryWriter.CreateTextWriter(ms,System.Text.Encoding.UTF8);
writer.WriteStartElement("WCF.OneWay");
writer.WriteElementString("Sender", "Anders");
writer.WriteElementString("Receive", "Anders");
writer.WriteElementString("IsReply", "False");
writer.WriteEndElement();
writer.Flush();
ms.Position=0;
XmlDictionaryReader reader=XmlDictionaryReader.CreateTextReader(ms, XmlDictionaryReaderQuotas.Max);
return Message.CreateMessage(MessageVersion.Soap12WSAddressing10, string.Empty, reader);
}
}
}
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">urn:GetData</a:Action>
<a:To s:mustUnderstand="1">http://localhost:1234/OneWayContract</a:To>
</s:Header>
<s:Body>
<WCF.OneWay>
<Sender>Anders</Sender>
<Receive>Anders</Receive>
<IsReply>False</IsReply>
</WCF.OneWay>
</s:Body>
</s:Envelope>
//另外的一个监听端,监听数据报,只与service的address一样,其他选项没有关系
using System;
using System.ServiceModel.Channels;
namespace ReceiverApp
{
class Program
{
static void Main(string[] args)
{
BindingElement[] bindingElements = new BindingElement[3];
bindingElements[0] = new TextMessageEncodingBindingElement();
bindingElements[1] = new OneWayBindingElement();
bindingElements[2] = new HttpTransportBindingElement();
CustomBinding binding = new CustomBinding(bindingElements);
IChannelListener<IInputChannel> listener = binding.BuildChannelListener<IInputChannel>(
new Uri("http://localhost:1234/OneWayContract"),
new BindingElementCollection());
listener.Open();
IInputChannel inputChannel = listener.AcceptChannel();
inputChannel.Open();
Message message = inputChannel.Receive();
Console.WriteLine(message.Headers.Action); //urn:GetData
message.Close();
inputChannel.Close();
listener.Close();
Console.ReadKey();
}
}
}
HTTP和数据报消息交换模式
在WCF里,通过HTTP发送数据报,再通过HTTP发送数据时,回复是一个HTTP202应答代码。HTTP202状态码的定义是:请求已经被处理,但处理还没结束。