前面的内容已经介绍了关于构建消息发送等有关知识了,接下来,我们就要开始深入到接收消息的这部了。引子 关于接收问题,涉及到两个问题: 问题1、由于MSMQ并没有规定消息的格式和主体的结构,所以作为接收者应该而且必须知道如何解析消息的主体。只要发送者和接收者都能明白他就行。 问题2、怎样可以监控队列并在消息到来时读取他,这就要求接收者必须采取一个非常有效的队列监控机制。 所以我们在进行构建接受者的时候,就要充分地考虑到这两个问题。接下来,我们就逐一地解决他们。
消息的自定义类型 这就是我们之前提到的怎样解析消息,前面的例子使用的是一些简单的类型来说明消息队列的结构,然而在发送的数据包含着具体应用程序的数据,其中可以是一个订单,一个收据等。这时你才会发现,消息队列的强大功能。 内置的消息格式化程序可以很容易地把包含有应用程序数据的托管对象解析为消息
.NET消息队列提供了如下的格式程序:XmlMessageFormatter ,BinaryMessageFormatter ,ActiveXMessageFormatter
类型特性XmlMessageFormatter 这是默认的格式化程序,前面的例子都是使用它的,从这个名称我们就可以联想到,它是会将自定义的类型串行化为一个XML表示,这个格式化程序很慢,并且会创建相对较多的消息。然而,这些消息可以被运行在不同平台下的应用程序共享和理解。BinaryMessageFormatter 这个格式化应用程序会把自定义类型串行化为一个专有的二进制的格式。他比上面一种的速度要快得多,而且生成的消息很紧凑。然而只有运行在.NET中的接收者才可以容易地解析这个消息的内容。ActiveXMessageFormatter ActiveXMessageFormatter 和BinaryMessageFormatter一样,他会把自定义的类型串行化为专用的二进制格式。这个格式也就是MSMQ的COM组件使用的格式。这些传统的COM组件为COM语言(比如Visual Basic 6)提供了基于MSMQ的功能。因此,您可以在用Visual Basic 6编写的MSMQ应用程序中使用这个格式化程序来发送消息或接收消息。 示例的代码: 1、使用XmlMessageFormatter 下面是发送代码 1 private void btnsend_Click( object sender, System.EventArgs e) 2 { 3 4 try 5 { 6 MessageQueue mq=MessageQueue.Create(@".private$myPrivate"); 7 8 SudentInfo form=new SudentInfo(this.txtname.Text,this.txtemail.Text,this.txtaddress.Text); 9 System.Messaging.Message msg=new System.Messaging.Message(); 10 msg.Label="A student info"; 11 msg.Body=form; 12 mq.Send(msg); 13 } 14 catch(Exception ex) 15 { 16 MessageBox.Show(ex.Message,"Warning"); 17 } 18 19 20 }
接收代码:
private void btnrec_Click( object sender, System.EventArgs e) { MessageQueue mq=new MessageQueue(@".private$myPrivate"); Type[] types=new Type[]{typeof(ch2.SudentInfo)}; mq.Formatter=new XmlMessageFormatter(types); System.Messaging.Message msg=mq.Receive(); ch2.SudentInfo form=(ch2.SudentInfo)msg.Body; this.txtsemail.Text=form.Email; this.txtsname.Text=form.Name; this.txtsadd.Text="私有字段,不能串行化"; }前面我们用的代码,引用了一个自定义的类库,因此只有测试的程序引用了他才能够编译成功。所以我们必须将这个程序集部署到发送者和接收者中。这里似乎就失去了分布式的意义。那么我们应该怎样解决呢?下面介绍两个方法,1、我们可以把消息类型指定为字符串数组,在这个数组中的每一个字符串都包含有类型的全名。2、还可以让用编程的方式判断传入的类型以及构造出正确的XmlMessageFormatter。例如我们使用Message.Label属性来表示包含在消息主体中的数据类型。
方案一、string[] types=new string[]{"",""};mq.Formatter=new XmlMessageFormatter(types);
方案二、//发送者Message msg=new Message();msg.Label=typeof(Customer).AssemblyQualifiedName;//接收者string[] types=new string[]{msg.Label};msg.Formatter=new XmlMessageFormatter(types);object o=msg.Body;
尽管这个形式相对来说很慢,但是他还是有一些优点的。因为由于消息是简单的XML,所以它可以被任何XML解析器读取和解释。换句话说,应用程序不用XmlMessageFormatter也能反串行化整个消息。//接收代码Message msg=mq.Receive();XmlTextReader xtr=new XmlTextReader (msg.BodyStream);2、使用BinaryMessageFormatter示例代码
private void sendByBinary() { try { MessageQueue mq=MessageQueue.Create(@".private$myPrivate"); ch2.DeptmentInfo form=new DeptmentInfo(this.txtname.Text,this.txtemail.Text,this.txtaddress.Text); System.Messaging.Message msg=new System.Messaging.Message(); //使用BinaryMessageFormatter这个类型进行串行化,默认情况下,这个属性不进行赋值的话就是XmlMessageFormatter msg.Formatter=new BinaryMessageFormatter(); msg.Label="A student info"; msg.Body=form; mq.Send(msg); } catch(Exception ex) { MessageBox.Show(ex.Message,"Warning"); } } private void recByBinary() { try { MessageQueue mq=MessageQueue.Create(@".private$myPrivate"); //使用BinaryMessageFormatter这个类型进行串行化,默认情况下,这个属性不进行赋值的话就是XmlMessageFormatter msg.Formatter=new BinaryMessageFormatter(); //接收 System.Messaging.Message msg=mq.Receive(); //获取接收的对象 ch2.DeptmentInfo dept=(ch2.DeptmentInfo)msg.Body; //输出 Console.WriteLine(dept.Address); Console.WriteLine(dept.Name); } catch(Exception ex) { MessageBox.Show(ex.Message,"Warning"); } }但要注意的是,和XmlMessageFormatter不同的是,BinaryMessageFormatter使用的是二进制格式来把对象串行化到消息主体中。实际上,他使用了和.Net Remoting一样的运行库串行化机制,这就意味着要使用Serializable特性来修饰类型。同时对比一下,他没有默认的形式那么灵活,尽管速度快,紧凑的多。然而发送者和接收者都必须有一个程序集的副本。3、使用ActiveXMessageFormatter他的代码和前面的两个模式一样,在这里就不赘言了。