Reading message from nontranscational remote MSMQ in workgroup

MSMQ is very useful for building a distributed, offline application. In my recent project the customer requires reading message remotely from a MSMQ queue. In this blog I will show you the solution.

1. MSMQ API Get Started

Accessing MSMQ

Accessing a queue via .NET is done with the System.Messaging.MessageQueue object. The following code demonstrates how to access a public queue named TechRepublic on a computer named "SRV-MESSAGING":

MessageQueue queue = new MessageQueue("SRV-MESSAGINGTechRepublic");

Create a Queue

MessageQueue queue = null;
string queueName = "SRV-MESSAGINGTechRepublic";
if (MessageQueue.Exists(queueName))
{
queue = new MessageQueue(queueName);
}
else
{
queue = MessageQueue.Create(queueName, false);
}

Send Message To a Queue

queue.Send("My message body", "Message Label");

Reading From a Queue

System.Messaging.Message[] messages = queue.GetAllMessages();
foreach (System.Messaging.Message message in messages)
{
//Do something with the message.
}

The above code will pull down all messages into local memory. For very large queues we should use GetMessageEnumerator2 method as below.

MessageEnumerator enumerator = queue.GetMessageEnumerator2();
while (enumerator.MoveNext())
{
enumerator.RemoveCurrent();
}

If you only want to retrieve the first message in a queue you should use the MessageQueue.Receive method as below.

System.Messaging.Message message = queue.Receive();

Note that the Receive() method will remove the curent message from the queue. If you want to leave a message copy on the queue then write code like below.

System.Messaging.Message message = queue.Peek();

Sending/Receiving Serialized Objects

[Serializable]
public class MessageContent
{
private DateTime _creationDate = DateTime.Now;
private string _messageText;

public MessageContent() { }

public MessageContent(string messageText)
{
_messageText = messageText;
}

}

MessageContent message = new MessageContent("Hello world!"); queue.Send(message, "Sample Message");

As you can see, this is very similar to the code we used earlier to send the message with a string as its body. Receiving a message that contains a serialized object is a little more difficult. What we need to do is tell the message what kind of object it contains.
To indicate to the message what kind object it contains we must setup the message's formatter. This is done by assigning a System.Messaging.XmlMessageFormatter object to the message's Formatter property. Since our message contains a MessageContent object, we will want to configure the XmlMessageFormatter for that type.

message.Formatter = new System.Messaging.XmlMessageFormatter( new Type[1] { typeof(MessageContent) } );

MessageContent content = (MessageContent)message.Body;

At this point, the "content" variable is a deserialized version of the original MessageContent object that we sent to the queue, and all of the original object's properties and values are accessible.

Setting Message Priority

In a normal situation messages are accessed in the queue in a first in, first out order. However there are instances where you will want a message to move to the front of the line because it is of higher importance than the other messages. To accomplish this type of functionality you will need to set message priorities.
The priority of a message is determined by the value of the Message.Priority property. The following are valid values for this property (all from the MessagePriority enum):
1. Highest
2. VeryHigh
3. High
4. AboveNormal
5. Normal
6. Low
7. VeryLow
8. Lowest

System.Messaging.Message queueMessage = new System.Messaging.Message();
//Set the priority to Highest.
queueMessage.Priority = MessagePriority.Highest;
//Create our MessageContent object.
MessageContent messageContent = new MessageContent("Hello world - IMPORTANT!");
//Set the body as the messageContent object.
queueMessage.Body = messageContent;
//Send the message.
queue.Send(queueMessage, "HIGH PRIORITY");

2. Create a Sample Application

Go Through the Application

My smaple application consists of 4 project as shown below.

msmq-remote-read-solution.jpg

MsmqLibrary is a Class Library project, which contains business logic of msmq message sending and receiving, and message is type of business object.

MsmqRetrieveService is a Windows Service project, which runs as a long-time task, read message from a remote msmq in a certain interval, and handle with the messages read out from the queue.

RetrieveMessageClientApplication is a Windows Form project, in which you can test the function of receiving messages from a remote queue manually.

MsmqRetrieveServiceSetup is a setup project for MsmqRetrieveService, which builds and packages the MsmqRetrieveService into an EXE file.You run it ot setup the windows service application.

Now let's take a look at the core code.

QueueMessageSender

 
 

public class QueueMessageSender { public void Send() { // Get MSMQ queue name from app settings in configuration
// use the direct format for remote queue like :
// FormatName:Direct=Tcp:remoteServerIPprivate$non-transactional-queue
string queueName = ConfigurationManager.AppSettings["queueName"];

// make sure the queue path exists
MessageQueue queue = new MessageQueue(queueName);

// XmlMessageFormatter is default Formatter
//(queue.Formatter as XmlMessageFormatter).TargetTypes = new Type[] {typeof (PurchaseOrder)};

// Create the purchase order
PurchaseOrder po = new PurchaseOrder();
po.CustomerId = "somecustomer.com";
po.PONumber = Guid.NewGuid().ToString();

PurchaseOrderLineItem lineItem1 = new PurchaseOrderLineItem();
lineItem1.ProductId = "Blue Widget";
lineItem1.Quantity = 54;
lineItem1.UnitCost = 29.99F;

PurchaseOrderLineItem lineItem2 = new PurchaseOrderLineItem();
lineItem2.ProductId = "Red Widget";
lineItem2.Quantity = 890;
lineItem2.UnitCost = 45.89F;

po.orderLineItems = new PurchaseOrderLineItem[2];
po.orderLineItems[0] = lineItem1;
po.orderLineItems[1] = lineItem2;

po.PlaceDate = DateTime.Parse(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
bool isTransactional = false;
try
{
isTransactional = queue.Transactional;
}
catch
{
// queue.Transactional is not available for remote queue in Workgroup mode
}
// If a queue is transactional, it accepts only messages that are sent as part of a transaction. However,
// a non-transactional message can be sent or received from a local transaction queue
// without explicitly using transactional Begin, Commit, and Abort syntax.
// If a non-transactional message is sent to a transactional queue,
// this component creates a single-message transaction for it,
// except in the case of referencing a queue on a remote computer using a direct format name.
// In this situation, if you do not specify a transaction context when sending a message,
// one is not created for you and the message will be sent to the dead-letter queue.
if (isTransactional)
{
using (MessageQueueTransaction MQTransaction = new MessageQueueTransaction())
{
try
{
MQTransaction.Begin();
queue.Send(po, MQTransaction);
MQTransaction.Commit();
}
catch (Exception)
{
MQTransaction.Abort();
}
}
}
else
{
queue.Send(po);
}

queue.Close();
}
}

QueueMessageReceiver

 
 

public class QueueMessageReceiver
{
public bool isRunning = false;

public void Receive()
{
// Get MSMQ queue name from app settings in configuration.
// use the direct format for remote queue like :
// FormatName:Direct=Tcp:remoteServerIPprivate$non-transactional-queue
string queueName = ConfigurationManager.AppSettings["queueName"];

MessageQueue queue = new MessageQueue(queueName);
// XmlMessageFormatter.TargetTypes is not required if retrieve from Message.BodyStream
//(queue.Formatter as XmlMessageFormatter).TargetTypes = new Type[] { typeof(Object) };
MessageEnumerator enumerator = queue.GetMessageEnumerator2();
while (enumerator.MoveNext())
{
Message message = queue.Peek();
try
{
// retrieve message body here...
byte[] buffer = new byte[message.BodyStream.Length];
message.BodyStream.Read(buffer, 0, (int)message.BodyStream.Length);

// handler the message depending on your business here...
CreateFileOnShareLocation(@"E:log", @"PuchaseOrder.xml", buffer);

// if the message has been processed successfully then remove it from the queue.
enumerator.RemoveCurrent();

// An enumerator can only move in a forward direction.
// Use this method to start over at the beginning of the queue.
enumerator.Reset();
}
catch (Exception ex)
{
enumerator.MoveNext();
}
}
}

private void CreateFileOnShareLocation(string shareLocation, string fileName, byte[] content)
{
string filePath = Path.Combine(shareLocation, fileName);
using (FileStream stream = File.Open(filePath, FileMode.Append))
{
stream.Write(content, 0, content.Length);
}
}
}

3. Config the MSMQ and Firewall

So far we finished coding job, but the application can not work as this point. We need to do some addtinal job to make it work. Follow me please.

1) Create a Remote Non-Transactional Queue

Open the Computer Management, as shown below.

msmq-create-queue.jpg

Expands "Services and Applications - Message Queuing", right-click "Private Queues", select "New -> Private Queue", input your queue name for example "non-transactional-queue1", make sure uncheck the Transactional check-box, click "OK" button.

2) Config the Queue Access Permissions

Right-click the queue "non-transactional-queue1", click "Properties" menu to open the queue properties window, then select "Security" tab, select "ANONYMOUS LOGON" uuder "Group or usernames" and check "Receive Message","Peek Message" and "Send Message" permissions on "Allow" column under permissions list, then click "OK" button to apply the setting.

3) Config MSMQ to Enable RPC calls

In the "Computer Manaegement" Console Panel, right-click "Message Queing" on the left frame,click "Properties" menu to open MSMQ Queuing Properties window, select "Server Security" tab, uncheck the check-box "Disable un-authenticated RPC calls", then click "Apply" button to apply the new setting.

4. Deployment and Test

Now we are ready to deploy our sample application. We need 2 machines, I named them "MsmqServer" and "MsmqAppServer".

MsmqServer is where the MSMQ installed and the remote queue located in. We also deploy RetrieveMessageClientApplication on this machine to send message to MSMQ locally.

MsmqAppServer is where the MSMQ client application runs on, in our sample application it is MsmqRetrieveService or RetrieveMessageClientApplication.

5. Summary

By now we have a complete solution for reading message from a remote non-transactional queue in workgoup network environment. This soloution can work correctly, but because it allows "ANONYMOUS LOGON" access, so it is not very strong in security. How to make a secured remote MSMQ read is still a question.

[@more@]

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13651903/viewspace-1033985/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/13651903/viewspace-1033985/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值