问题:
下面是我的创建消息队列的代码,放在服务器端运行:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;
namespace MSMQBuilder
{
class Program
{
static void Main(string[] args)
{
MessageQueue mq = null;
//显示创建自身的消息队列
const string queueName = @".\Private$\jiyiqin";
if (!MessageQueue.Exists(queueName))
{
mq = System.Messaging.MessageQueue.Create(queueName);
Console.WriteLine("创建消息队列完成:" + queueName);
}
else
{
mq = new System.Messaging.MessageQueue(queueName);
}
mq.SetPermissions("Administrator", MessageQueueAccessRights.FullControl);
mq.SetPermissions("ANONYMOUS LOGON", MessageQueueAccessRights.FullControl);
mq.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
}
}
}
下面是我的发送消息和去远端服务器的消息队列查询消息的代码,在win7上面运行:
using System;
using System.Messaging;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using CBIP.CProxy.AsyncMsgSender;
namespace ClientNTService1
{
class Program
{
static void Main(string[] args)
{
SendMsg();
ReceiveMsg();
}
static void SendMsg()
{
const string queueName = @"FormatName:Direct=TCP:192.168.1.108\Private$\jiyiqin";
AsyncMessage message = new AsyncMessage
{
UserID = 352,
MessageReceiver = "某某某接受者",
MessageSender = "CMS Administrator",
MessageSendIP = "192.168.0.1",
SendTime = DateTime.Now.ToString(),
MessageCode = "109001001",
MessageBody = "CMS测试消息,内容包含超链接 http://bbs.jlitrdc.com/bbs/ ,点击跳转"
};
try
{
//将异步消息发往指定的消息队列
using (MessageQueue msmq = new MessageQueue(queueName))
{
msmq.Formatter = new XmlMessageFormatter(new Type[] { typeof(AsyncMessage) });
System.Messaging.Message msg = new Message() { Label = "业务模块异步消息", Body = message };
msmq.Send(msg);
}
}
catch (Exception ee)
{
Console.WriteLine(String.Format("消息发送失败,原因是:{0}", ee.Message));
}
Console.WriteLine("发送完成,按任意键退出...");
//Console.ReadKey();
}
static void ReceiveMsg()
{
const string queueName = @"FormatName:Direct=TCP:192.168.1.108\Private$\jiyiqin";
//while (true)
//{
MessageQueue MQ = new MessageQueue(queueName);
System.Messaging.Message message = null;
try
{
message = MQ.Receive(TimeSpan.FromSeconds(3));
}
catch (Exception ee)
{
message = null;
Console.WriteLine("从消息队列轮训消息出现异常,原因是:" + ee.Message);
//continue;
}
if (message != null)
{
message.Formatter = new XmlMessageFormatter(new Type[] { typeof(AsyncMessage) });
AsyncMessage msg = (AsyncMessage)message.Body;
Console.WriteLine("轮训线程接收到一个异步消息: UserID is " + msg.UserID);
}
//}
}
}
}
运行之后提示: Access to remote messaging queue is denied。 Windows Server 2003没有这个问题。
解决方法:
1、消息队列本身的权限:
右击你的消息队列选择属性,切换到安全tab页,看是不是ananymous logon,everyone,administrator都已经是完全控制了,如果不是勾选上。
2、Windows Server 2008的消息队列安全访问控制设置
(1)更改注册表项:
定位到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSMQ\Parameters\security 下面,新建一个DWIRD项: NewRemoteReadServerAllowNoneSecurityClient,设置值为1。
原因: http://technet.microsoft.com/zh-cn/library/cc759412(v=ws.10).aspx
(2)启用入站规则
原因:http://blogs.msdn.com/b/johnbreakwell/archive/2008/07/10/getting-msmq-messages-out-of-windows-server-2008.aspx
(3)修改消息队列属性,允许远端RPC访问:
右击消息队列选择属性。
将“禁用未经身份验证的RPC调用” 前面的勾去掉。
原因:http://blogs.msdn.com/b/johnbreakwell/archive/2008/02/12/msmq-4-0-what-s-new-in-computer-management.aspx
这样设置好了之后,可以发现防火墙允许的列表中多了消息队列,注册表项也多了一项:
然后我马上在win7上运行我前面贴出来的发送消息和读取消息的程序,发现消息发送不过去了。。。
然后我把消息发送的程序拷贝到windows server 2008上面运行,消息能够发进去,然后再回到win7运行发送消息和读取消息的程序,竟然能够工作了!!!
不知道是不是需要去发一下消息,激活一下。。。 好苍白的解释,不过总算是OK了。
猜想性提示:
如果按照上面做了,还是不行。
我想说的是我的windows server 2008虽然是虚拟机,但是和我的win7是在同一个网段内(与win7是桥接方式连接),工作组都是WORKGROUP,这些都有可能是导致我这里能够正常工作的原因。
如果你按照我上面介绍的设置了,还是不能正常工作,可能是因为两台机器不在同一个工作组,或者虽然能相互ping通,但是不在同一个子网等等,我不确定,只是提醒你注意一下可能是这些原因。
你也许可以在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSMQ\Parameters\security 下面加一个注册表项:NewRemoteReadServerDenyWorkgroupClient,并将其设置为 1,试一试。
如果你遇到按照我的设置了还是不能工作,但是最后解决了这个问题的,请留言或者邮件([email protected])告诉我,学习一下,谢谢。
====================== 2014-1-4补充:
果然不出我所料,如果win7和server08不在同一个域或者工作组中,上面讲的都无法工作。。。 这个问题还不知道怎么解决。
我的win7是加入到了公司的域中,而我的windows server 2008是加入到的默认的工作组WORKGROUP中,然后我反复在win7上运行我的上面贴出来的那个发送消息和接收消息的程序,下面两个错误交替出现:
错误1: Access to remote messaging queue is denied
错误2:下面的错误描述摘自网络,我和他一样:
I've asked this question in the MSDN forum, I got a lot of help there from John Breakwell, but I couldn't solve my issue and now I am running in circles (again). So maybe someone here can help me.
I have a remote private queue, a producer application and a consumer application. I am using Windows Server 2008 with MSMQ.
I can send the message using my producer app, but I can't read it using my consumer. Here is my consumer code
MessageQueue rmQ = new MessageQueue("FormatName:Direct=OS:<hostname>\\private$\\<queuename>");
System.Messaging.Message msg = rmQ.Receive();
I get an exception in the second line, the exception is null without any information, except for the stack trace
System.Messaging.MessageQueueException (0x80004005) at System.Messaging.MessageQueue.MQCacheableInfo.get_ReadHandle() at System.Messaging.MessageQueue.StaleSafeReceiveMessage(UInt32 timeout, Int3 2 action, MQPROPS properties, NativeOverlapped* overlapped, ReceiveCallback rece iveCallback, CursorHandle cursorHandle, IntPtr transaction) at System.Messaging.MessageQueue.ReceiveCurrent(TimeSpan timeout, Int32 actio n, CursorHandle cursor, MessagePropertyFilter filter, MessageQueueTransaction in ternalTransaction, MessageQueueTransactionType transactionType) at System.Messaging.MessageQueue.Receive() at consumer.Program.Main(String[] args) in C:\Users\usr\Documents\Visua l Studio 2010\Projects\loadQueueTest\consumer\Program.cs:line 20
I've read a lot of things, so I have already done somethings:
- I've set the anonymous logon to have full access on the queue
- I've set my domain user as the administrator on the server
- Opened the ports for MSMQ and RPC
- Disabled the Firewall
I also used rpcping and portqry to perform some diagnostics, I am not an expert, but it seems ok.
Does anyone knows what I am missing?
Thanks, Oscar
============================== 下面是我在MSDN找到的一段话,我来翻译一下:
Reading Messages from Remote Queues
3 out of 5 rated this helpful - Rate this topic
Although remote read operations are not part of the optimal messaging model, reading messages from remote nontransactional queues is fully supported. Remote reading is typically used to distribute the work load among servers. Remote reading is a high-overhead and therefore inefficient process. Including remote read operations in an application limits scaling.
Message Queuing supports sending transactional messages to remote queues, but does not support reading messages from a remote queue within a transaction. This means that reliable, exactly-once reception is not available from remote queues.
Messages can be retrieved from a remote private queue on an MSMQ 3.0 computer when either the local or remote computer is operating in workgroup mode only if the queue's default security descriptor is changed to grant the Receive Message permission to anonymous users.
Message Queuing does not support reading of messages from a remote queue if the remote queue resides in a different AD forest.
尽管远端的读操作不是最优的消息模式,但是从远端的非事务性队列读取消息仍然是支持的。
远端读取典型地应用于在多个服务器中分发工作负载。远端读取开销非常高,因此效率不高,而且远端读取操作会限制应用的可拓展性。(译者注解:主动到远端服务器读取消息可拓展性感觉上没有被动接收消息的可拓展性高)
消息队列支持发送事务性消息到远端队列,但是不支持从远端队列读取事务性消息,这意味着远端队列无法做到可靠的、无重复的消息工作模式。
在支持MSMQ3.0以上的计算机上,支持读取远端私有队列中的消息,但是这个本地的或者远端的计算机必须工作在“工作组模式”下,并且队列默认的安全描述符需要被修改来授予匿名用户接受消息的权限。
(译者注释:
1、我们都知道计算机可以工作在工作组模式、或者域模式下,它这里只提到了必须在工作组模式下,没有提到两台计算机都工作在域模式下,并且在同一个域中,是否还能够远端读取,这个我去试一试再来补充。
2、我今天将我的windows server2008原来的工作组从默认的和win7同一个的工作组WORKGROUP改为WORKGROUP1,然后就远程不上了,但是能够ping通,而且仍然能够在win7上对其进行远程消息读取。。。不知道是不是不论在什么工作组,只要在工作组模式就可以,或者与我的08服务器(虚拟机)和我的win7的网络设置是桥接方式有关)
)
假如两台机器在不同的AD(Active Directory)林中,那么消息队列不支持远端消息读取。
==========================还有一些问题需要进一步确认:
1、同一个AD林(域)中是不是可以远端访问私有队列?(我猜测可以,如果哪天发现不行了,再来修改这个答案)
2、不同AD林中不支持远端读取,私有队列是肯定的,但是公共队列呢?是否也不支持?(我猜测不可以,还是一样哪天发现支持,额再来修改这个答案)
3、关于创建公共队列:
MSDN有句话:可以在自己的计算机上或享有域或者企业管理访问权限的任何“消息队列”计算机上创建公共队列。还可以仅在本地计算机上创建专用队列。
意思就是只要加入到了域,就可以创建公共队列。反过来就是说,如果没有加入到域,就别想公共队列的事了。
首先可以确定的是要创建公共队列必须 将计算机加入到域中(但是加入到哪个域?任何一个域都可以么?),并且是不是加入到了域,然后安装消息队列的时候将“MSMQ Active Directory 域服务集成/目录服务集成”选项勾选上就可以创建公共队列了?
我想起来我的公司的电脑是已经加入到公司的域中了,明天上班去看看它上面是不是真的有公共队列,反正我现在身边所有电脑都没有,它们也都没有加入到任何域中。
============================ 关于windows server 2003上的消息队列:
我上面所讲的全部都是针对Windows Server 2008的。
如果是windows server 2003就完全不一样了。我已经试过了,就算是win7加入到了特定域,而windows server 2003没有加入任何域,还是默认的WORKGROUP工作组,也就是说它们两个不在同一个AD林(域)中,win7仍然能够远程读取windows server 2003上面私有队列的消息。
我猜想是windows server 2003针对消息队列没有引入安全访问控制机制,这应该是server 2008以后才有的机制。
============================ 总结:
既然MSDN上都说了,远端访问消息队列效率不高,然后还有这样那样限制,我们为什么一定要这样去访问呢。
目的都是去远端机器拿消息队列,可以通过RPC,让RPC服务在服务器上本地访问消息队列,不更好么!!!
参考:http://blogs.msdn.com/b/johnbreakwell/archive/2010/03/24/understanding-how-msmq-security-blocks-rpc-traffic.aspx