关于消息队列的研究
最近研究了下MSMQ,有些心得,记录下来
首先介绍一下MSMQ的相关概念:
大家可以在MSDN上找到相应的内容,我在这里就不做转述了
http://msdn.microsoft.com/zh-cn/library/19ww660c.aspx
再说说消息队列的几个优点:
稳定性 — 组件失败对消息的影响程度远远小于组件间的直接调用,因为消息存储在队列中并一直留在
那里,直到被适当地处理。消息处理同事务处理相似,因为消息处理是有保证的。
消息优先级 — 更紧急或更重要的消息可在相对不重要的消息之前接收,因此可以为关键的应用程序保
证足够的响应时间。
脱机能力 — 发送消息时,它们可被发送到临时队列中并一直留在那里,直到被成功地传递。当因任何
原因对所需队列的访问不可用时,用户可以继续执行操作。同时,其他操作可以继续进行,如同消息已
经得到了处理一样,这是因为网络连接恢复时消息传递是有保证的。
事务性消息处理 — 将多个相关消息耦合为单个事务,确保消息按顺序传递、只传递一次并且可以从它
们的目标队列中被成功地检索。如果出现任何错误,将取消整个事务。
安全性 - MessageQueue 组件基于的消息队列技术使用 Windows 安全来保护访问控制、提供审核并对组
件发送和接收的消息进行加密和验证。
下面说说在.net 2.0下,如何对MSMQ进行编程
首先机器要安装MSMQ,
其次,对MSMQ编程的类,都在System.Messaging命名空间下
主要有如下三个类
System.Messaging.Message; 提供对定义消息队列消息所需的属性的访问,使用 Message 类从队列中查
看或接收消息,或者在将消息发送到队列时对消息属性进行精确的控制。
System.Messaging.MessageQueue;提供对“消息队列”服务器上的队列的访问,MessageQueue 类提供对
“消息队列”队列的引用。可以在 MessageQueue 构造函数中指定一个连接到现有资源的路径,或者可
在服务器上创建新队列。
System.Messaging.MessageQueueTransaction;提供消息队列内部事务,若要作为事务的一部分发送或接
收消息,可以使用 MessageQueueTransaction 类创建事务并将其传递到 MessageQueue.Send 方法或
MessageQueue.Receive 方法的重载(采用 transaction 参数)。作为事务的一部分发送的消息必须被
发送到事务性队列。必须使用指定的事务接收从事务性队列接收的消息。
下面分步说说.net 对MSMQ的编码:
1、消息队列实例的建立和引用
有三种方法可以对消息队列加以引用:
通过路径 — 唯一标识计算机和感兴趣的队列的名称的路径。
路径语法
公共队列
MachineName\QueueName
专用队列
MachineName\Private$\QueueName
日记队列
MachineName\QueueName\Journal$
计算机日记队列
MachineName\Journal$
计算机死信队列
MachineName\Deadletter$
计算机事务性死信队列
MachineName\XactDeadletter$
通过格式名 — 队列的唯一标识符,创建队列时由 MSMQ 生成,或者后来由应用程序生成。
公共队列
FORMATNAME:PUBLIC=QueueGUID
专用队列
FORMATNAME:PRIVATE=MachineGUID\QueueNumber
日记队列
FORMATNAME:PUBLIC=QueueGUID;JOURNAL
- 或 -
FORMATNAME:PRIVATE=MachineGUID\QueueNumber;JOURNAL
通过标签 — 可能不唯一的描述性队列名称,创建队列时由队列管理员指派。
LABEL:MyQueue"
对于队列在计算机间进行移动的情况,标签非常有用。如果仅通过标签引用队列,当队列已被移动到新
的位置后,只要新的计算机上不存在带该标签的其他队列,所有操作都将成功地继续进行。如果存在带
该标签的其他队列,Send 方法将产生错误。
如下的代码显示了如何用路径引用一个已有的消息队列实例,或建立一个新的消息队列实例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 {
3 string qp = String.Empty; //消息队列路径
4 MessageQueue queue = null; //消息队列
5 qp = @".\private$\" + path;
6
7
8
9 //引用消息队列,如队列不存在,则建立消息队列
10 if (System.Messaging.MessageQueue.Exists(qp))
11 {
12 queue = new MessageQueue(qp);
13 }
14 else
15 {
16 queue = MessageQueue.Create(qp);
17 }
18
19 return queue;
20 }
21
请注意,这里建立的是私有队列
2.消息的发送及接收
有了消息队列的实例,我们就可以发送及接收数据了,消息的接收,我将分简单消息的发送及接收,事
务性消息的发送及接收,加密消息的发送及接收三部分来说明一下,希望对大家有所帮助:
简单消息的发送及接收
Message类提供对定义消息队列消息所需的属性的访问,
MessageQueue类提供发送及接受的方法,发送及接受的方法都同时提供同步和异步的,本文仅以同步的
方法为例进行说明
先说说Message类的几个重要的属性
在发送数据时,主要要设置的就是Body这个属性,这个属性保存了要发送的内容
如果发送的数据类型为复杂类型,接收时要拆箱的话,可以在Label属性里记录发送的数据类型
如下代码,显示了如何发送一个简单消息到队列
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// 发送简单消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void buttonSimpleSend_Click(object sender, EventArgs e)
{
MessageQueue queue = null; //消息队列
queue = MessageQueueUtil.GetMessageQueue(SIMQUEUE); //取得消息队列
//生成要发送的对象
Book book = new Book(this.nameTextBox.Text, issueDateDateTimePicker.Value,
publishNameTextBox.Text, authorTextBox.Text);
Publishing publish = new Publishing(this.publishNameTextBox1.Text,
this.telTextBox.Text, this.emailTextBox.Text, this.faxTextBox.Text,
this.addressTextBox.Text);
System.Messaging.Message bookMessage = new System.Messaging.Message();
//bookMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(Book) });
//声明消息格式化方式
bookMessage.Body = book;
//设置消息体
bookMessage.Label = "book";
//消息类型标识
System.Messaging.Message publishingMessage = new System.Messaging.Message();
//publishingMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof
(Publishing) }); //声明消息格式化方式
publishingMessage.Body = publish;
//设置消息体
publishingMessage.Label = "publish";
//消息类型标识
//消息回执
//消息接收后,指定队列send发送消息
publishingMessage.AcknowledgeType = AcknowledgeTypes.FullReceive;
publishingMessage.AdministrationQueue = new MessageQueue(@".\private$\send");
//将对象发送到消息队列
//发送消息
queue.Send(bookMessage);
queue.Send(publishingMessage);
queue.Close(); //关闭消息队列
}
简单消息的接收也很简单
MessageQueue提供了Receive方法来进行消息的接受,这个方法有个特殊的地方,如果队列里没有消息
的话,Receive方法会一直占用当前线程,直到队列里有了消息,或者过了超时时间,或者山崩地裂大海
咆哮...Receive有一个重载,参数是一个TimeSpan,设置了一个超时时间,但是,过了超时时间
它也不会放过你,它会抛出一个异常来...
以下代码显示了如何接收一个简单消息
注意,如果你的消息体是一个复杂类型,那么你在取数据时,要设置Message类的Formatter属性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 {
3 MessageQueue queue = null; //消息队列
4 string path = String.Empty; //消息队列的路径
5
6
7
8 queue = MessageQueueUtil.GetMessageQueue(SIMQUEUE);
9
10 System.Messaging.Message message = null;
11
12 try
13 {
14 message = queue.Receive(TimeSpan.FromSeconds(3));
15 }
16 catch
17 {
18 MessageBox.Show("无消息数据");
19 }
20
21
22 //接收数据
23 if (message.Label == "book")
24 {
25 message.Formatter = new XmlMessageFormatter(new Type[] { typeof(Book) });
26 Book book = (Book)message.Body;
27
28
29
30 this.nameReceivedTextBox.Text = book.Name;
31 this.authorReceivedTextBox.Text = book.Author;
32 this.issueDateReceivedDateTimePicker.Value = book.IssueDate.Value;
33 this.publishReceivedNameTextBox.Text = book.PublicName;
34
35 }
36 else if (message.Label == "publish")
37 {
38 message.Formatter = new XmlMessageFormatter(new Type[] { typeof(Publishing)
39
40 });
41
42 Publishing publish = (Publishing)message.Body;
43
44 this.faxReceivedTextBox.Text = publish.Fax;
45 this.publishReceivedNameTextBox1.Text = publish.PublishName;
46 this.emailReceviedTextBox.Text = publish.Email;
47 this.telReceivedTextBox.Text = publish.Tel;
48 this.addressReceivedTextBox.Text = publish.Address;
49
50
51 }
52
53 }
简单消息就是简单,下边我们来看看事务性消息的发送及接收
事务性消息的发送及接收
如下的代码显示了如何发送事务性消息
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 {
3 MessageQueue queue = null; //消息队列
4
5 queue = MessageQueueUtil.GetMessageQueue(TRAQUEUE); //取得消息队列
6
7
8 //生成消息发送对象
9 Book book = new Book(this.nameTextBox.Text, issueDateDateTimePicker.Value,
10
11 publishNameTextBox.Text, authorTextBox.Text);
12 Publishing publish = new Publishing(this.publishNameTextBox1.Text,
13
14 this.telTextBox.Text, this.emailTextBox.Text, this.faxTextBox.Text,
15
16 this.addressTextBox.Text);
17
18
19 System.Messaging.Message bookMessage = new System.Messaging.Message();
20 bookMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(Book) });
21
22 //声明消息格式化方式
23 bookMessage.Body = book;
24
25 //设置消息体
26 bookMessage.Label = "book";
27
28 //消息类型标识
29
30 System.Messaging.Message publishingMessage = new System.Messaging.Message();
31 publishingMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof
32
33 (Publishing) }); //声明消息格式化方式
34 publishingMessage.Body = publish;
35
36 //设置消息体
37 publishingMessage.Label = "publish";
38
39 //消息类型标识
40
41
42 //消息回执
43 publishingMessage.AcknowledgeType = AcknowledgeTypes.FullReceive;
44 publishingMessage.AdministrationQueue = new MessageQueue(@".\private$\send");
45
46
47
48
49 //建立消息队列事务对象
50 MessageQueueTransaction tran = new MessageQueueTransaction();
51
52 //开启事务
53 tran.Begin();
54 try
55 {
56
57 queue.Send(bookMessage, tran);
58
59
60 //可以发送之间抛出一些异常,已验证效果
61 queue.Send(publishingMessage, tran);
62
63 //提交事务
64 tran.Commit();
65 }
66 catch (Exception ee)
67 {
68 MessageBox.Show(ee.Message);
69
70 //取消并回滚事务
71 tran.Abort();
72 }
73
74
75 queue.Close();
76 }
可以看到,发送事务性消息和发送简单消息没有太大的区别,只是建立了一个
MessageQueueTransaction对象,并在事务性消息发送成功后调用了实例的Commit()方法,出现异常时
调用About()方法,取消并回滚了这个事务
没有做事务性消息接受的例子...
加密消息的发送及接收
这个相对来说比较复杂,在说明之前,先介绍一下加密及解密的相关知识:
对称加密:
对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥,即加密密钥也可以用
作解密密钥,这种方法在密码学中叫做对称加密算法,对称加密算法使用起来简单快捷,密钥较短,且
破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算法(IDEA),它比
DES的加密性好,而且对计算机功能要求也没有那么高。IDEA加密标准由PGP(Pretty Good Privacy)
系统使用。
非对称加密:
1976年,美国学者Dime和Henman为解决信息公开传送和密钥管理问题,提出一种新的密钥交换协议,允
许在不安全的媒体上的通讯双方交换信息,安全地达成一致的密钥,这就是“公开密钥系统”。相对于
“对称加密算法”这种方法也叫做“非对称加密算法”。
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥
(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密
钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密
使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其
它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存
的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信
息。
非对称加密算法的保密性比较好,它消除了最终用户交换密钥的需要,但加密和解密花费时间长、速度
慢,它不适合于对文件加密而只适用于对少量数据进行加密。
本文用非对称加密方式加密要传递的消息
首先,我们需要一个发布公钥的cer证书及保存私钥的pfx证书
cer证书:
可以使用framework 2.0 sdk自带的makecert.exe工具生成证书,makecert.exe工具的使用方式可参见
如下地址http://msdn.microsoft.com/zh-cn/library/bfsktky3(VS.80).aspx
命令如下 makecert -r -pe -n "CN=certificatetest" -sky exchange -sv 相应目录\certificate.pvk 相应目录\certificatetest.cer
pfx证书:
使用cert2spc工具生成.spc文件
命令如下cert2spc 相应目录\certificatetest.cer 相应目录\certificatetest.spc
再使用pvkimprt工具生成pfx证书
命令如下pvkimprt -pfx 相应目录\certificatetest.spc 相应目录\certificatetest.pvk
再按照提示即可生成相应的pfx文件
安装两个证书
如下代码显示了如何发送及接收加密的消息数据
发送
代码
2 /// 发送加密消息
3 /// </summary>
4 /// <param name="sender"></param>
5 /// <param name="e"></param>
6 private void button1_Click(object sender, EventArgs e)
7 {
8
9 MessageQueue queue = null; //消息队列
10
11 queue = MessageQueueUtil.GetMessageQueue(SECQUEUE);
12
13 CertificateUtil cer = CertificateUtil.CreateCertificateUtil
14
15 (StoreName.Root,"CN=qcert");
16
17 RC2CryptoServiceProvider rc = new RC2CryptoServiceProvider();
18
19
20 //加密密钥
21 byte[] key = cer.RSAEncrypt(rc.Key);
22 byte[] iv = cer.RSAEncrypt(rc.IV);
23
24
25 //将加密过的密钥保存到消息体
26 byte[] extary = new byte[256];
27 key.CopyTo(extary,0);
28 iv.CopyTo(extary,128);
29
30
31
32 Book book = new Book(this.nameTextBox.Text, issueDateDateTimePicker.Value,
33
34 publishNameTextBox.Text, authorTextBox.Text);
35 Publishing publish = new Publishing(this.publishNameTextBox1.Text,
36
37 this.telTextBox.Text, this.emailTextBox.Text, this.faxTextBox.Text,
38
39 this.addressTextBox.Text);
40
41 System.Messaging.Message bookMessage = new System.Messaging.Message();
42
43
44 bookMessage.Extension = extary;
45 bookMessage.UseEncryption = false;
46 bookMessage.EncryptionAlgorithm = EncryptionAlgorithm.Rc2;
47
48
49 System.Xml.Serialization.XmlSerializer ser = new
50
51 System.Xml.Serialization.XmlSerializer(typeof(Book));
52 System.Xml.XmlDocument xml = new System.Xml.XmlDocument();
53 MemoryStream stream = new MemoryStream();
54
55 ser.Serialize(stream,book);
56 stream.Position = 0;
57 xml.Load(stream);
58 stream.Close();
59
60 byte[] s = cer.Encrypt(rc, xml.InnerXml);
61
62
63
64 //bookMessage.Formatter = new XmlMessageFormatter(new Type[] { typeof(Book) });
65
66 //声明消息格式化方式
67
68 bookMessage.Formatter = new BinaryMessageFormatter();
69 bookMessage.Body = s;
70
71 //设置消息体
72 bookMessage.Label = "book";
73
74 //消息类型标识
75
76
77 System.Messaging.Message publishingMessage = new System.Messaging.Message();
78
79
80
81
82 ser = new System.Xml.Serialization.XmlSerializer(typeof(Publishing));
83 xml = new System.Xml.XmlDocument();
84 stream = new MemoryStream();
85
86 ser.Serialize(stream, publish);
87 stream.Position = 0;
88 xml.Load(stream);
89 stream.Close();
90
91 s = cer.Encrypt(rc, xml.InnerXml);
92
93 publishingMessage.Body = s;
94 publishingMessage.Label = "publish";
95
96 //消息类型标识
97
98 //消息回执
99 publishingMessage.AcknowledgeType = AcknowledgeTypes.FullReceive;
100 publishingMessage.AdministrationQueue = new MessageQueue(@".\private$\send");
101
102
103
104
105 //将对象发送到消息队列
106
107
108 MessageQueueTransaction tran = new MessageQueueTransaction();
109
110 queue.Send(bookMessage);
111 queue.Send(publishingMessage);
112
113
114 queue.Close();
115
116
117 }
接收
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 {
3 MessageQueue queue = null; //消息队列
4 string path = String.Empty; //消息队列的路径
5
6 queue = MessageQueueUtil.GetMessageQueue(SECQUEUE);
7
8 queue.MessageReadPropertyFilter.Extension = true;
9
10 System.Messaging.Message message = new System.Messaging.Message();
11
12 try
13 {
14 message = queue.Receive(TimeSpan.FromSeconds(3));
15 }
16 catch
17 {
18 MessageBox.Show("无消息数据");
19 }
20
21
22 //接收数据
23 if (message.Label == "book")
24 {
25
26 Book book = (Book)GetDecryptedMessage(message);
27
28 this.nameReceivedTextBox.Text = book.Name;
29 this.authorReceivedTextBox.Text = book.Author;
30 this.issueDateReceivedDateTimePicker.Value = book.IssueDate.Value;
31 this.publishReceivedNameTextBox.Text = book.PublicName;
32
33 }
34 else if (message.Label == "publish")
35 {
36 Publishing publish = (Publishing)GetDecryptedMessage(message);
37
38 this.faxReceivedTextBox.Text = publish.Fax;
39 this.publishReceivedNameTextBox1.Text = publish.PublishName;
40 this.emailReceviedTextBox.Text = publish.Email;
41 this.telReceivedTextBox.Text = publish.Tel;
42 this.addressReceivedTextBox.Text = publish.Address;
43
44
45 }
46
47 }
累死了,有机会再详细加解一下加密及解密的问题
posted on 2010-02-05 17:24 我不是冷狐冲,我就是一酒壶 阅读(2171) 评论(4) 编辑 收藏 举报