按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程序才能夠調用的隊列,有些狀況下爲了安全起見定義爲私有隊列。因此剛開始的時候認爲,要想訪問遠程消息隊列,只能使用公共隊列。可是後來發現,公共隊列依賴Domain Controller(域控),在實際部署的時候,要求使用消息隊列的應用必定要在某個域中,有些太苛刻!後來發現,私有隊列也是能夠遠程訪問的。(很困惑爲何私有隊列只能本地訪問,這句話,處處都能看到?!)html
新建4個項目:安全
2.2.一、Model項目服務器
/// <summary> /// 消息隊列實體 /// </summary> [Serializable] public class MqMessage { /// <summary> /// 對應Message的Label /// </summary> public string Label { get; set; } /// <summary> /// 對應Message的Body,CommandType爲操做類型,List<string>爲操做列表。 /// </summary> public Dictionary<CommandType, List<string>> Body { get; set; } = new Dictionary<CommandType, List<string>>(); /// <summary> /// 無參構造函數 /// </summary> public MqMessage() { } /// <summary> /// 有參構造函數 /// </summary> /// <param name="label"></param> /// <param name="body"></param> public MqMessage(string label, Dictionary<CommandType, List<string>> body) { Label = label; Body = body; } } /// <summary> /// 操做類型 /// </summary> public enum CommandType { Create = 1, //建立 Update = 2, //更新 Delete = 3 //刪除 }
2.2.二、Common項目ide
/// <summary> /// 日誌幫助類 /// </summary> public static class LogHelper { private static readonly string errLogSavePath = ConfigurationManager.AppSettings["ErrLogSavePath"] ?? AppDomain.CurrentDomain.BaseDirectory; /// <summary> /// 異常日誌方法重載 /// </summary> /// <param name="ex">異常信息</param> public static void WriteLog(Exception ex) { WriteLog(GetErrMsg(ex)); } /// <summary> /// 異常日誌方法重載 /// </summary> /// <param name="message">日誌內容</param> public static void WriteLog(string message) { WriteLog(errLogSavePath, message); } /// <summary> /// 異常日誌方法重載 /// </summary> /// <param name="filepath">日誌文件路徑</param> /// <param name="message">日誌內容</param> public static void WriteLog(string filepath, string message) { try { if (!Directory.Exists(filepath)) { Directory.CreateDirectory(filepath); } string filename = DateTime.Now.ToString("yyyy-MM-dd") + ".txt"; using (StreamWriter sw = new StreamWriter(filepath + "\\" + filename, true)) { sw.WriteLine("--------------------------------------------"); sw.WriteLine($"{DateTime.Now.ToLongTimeString()}:{DateTime.Now.Millisecond}\t{message}"); sw.Close(); } } catch (Exception ex) { throw new Exception(GetErrMsg(ex)); } } /// <summary> /// 獲取異常詳細信息 /// </summary> /// <param name="ex"></param> /// <returns></returns> private static string GetErrMsg(Exception ex) { string errMessage = ""; for (Exception tempException = ex; tempException != null; tempException = tempException.InnerException) { errMessage += tempException.Message + Environment.NewLine + Environment.NewLine; } errMessage += ex.ToString(); return errMessage; } }
/// <summary> /// 消息隊列管理器 /// </summary> public class MqManager : IDisposable { private MessageQueue _mq = null; private readonly LinkType linkType = LinkType.LocalHost; //連接類型,遠程時使用LinkType.RemoteServer。 private readonly string remoteServer = "192.168.2.165"; //遠程服務器IP地址 public static MqManager LinkServer { get; } = new MqManager(); /// <summary> /// 初始化函數 /// </summary> /// <param name="linkType">連接類型</param> public void MqManagerInit(LinkType linkType) { if (_mq == null) { string _path; if (linkType == LinkType.LocalHost) { _path = @".\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "HelloWorld"); } else { _path = "FormatName:DIRECT=TCP:" + remoteServer + @"\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "HelloWorld"); } _mq = new MessageQueue(_path) { Formatter = new BinaryMessageFormatter() }; } } /// <summary> /// 有參構造函數 /// </summary> public MqManager() { MqManagerInit(linkType); } /// <summary> /// 發送消息隊列(事務) /// </summary> /// <param name="message"></param> public void Send(MqMessage message) { MessageQueueTransaction transaction = new MessageQueueTransaction(); transaction.Begin(); _mq.Send(message.Body, message.Label, transaction); transaction.Commit(); } /// <summary> /// 接收消息隊列 /// </summary> /// <returns></returns> public Message Receive() { Message msg = null; try { msg = _mq.Receive(new TimeSpan(0, 0, 1)); } catch (Exception ex) { throw new Exception(ex.Message); } return msg; } /// <summary> /// 釋放資源 /// </summary> public void Dispose() { if (_mq != null) { _mq.Close(); _mq.Dispose(); _mq = null; } } } /// <summary> /// 連接類型 /// </summary> public enum LinkType { LocalHost = 1, //本地服務器 RemoteServer = 2 //遠程服務器 }
2.2.三、Send項目函數
class Program { static void Main(string[] args) { MqMessage mqMessage = new MqMessage(); List<string> list = new List<string>(); Console.WriteLine("請輸入內容按回車發送,多個內容請用英文逗號隔開,退出請輸入Exit。"); string receiveKey = Console.ReadLine(); while (receiveKey.ToLower() != "exit") { if (receiveKey.Length > 0) { mqMessage.Label = Guid.NewGuid().ToString(); list.Clear(); list = receiveKey.Split(new char[] { ',' }).ToList(); mqMessage.Body.Clear(); mqMessage.Body.Add(CommandType.Create, list); try { MqManager.LinkServer.Send(mqMessage); Console.WriteLine("內容已發送成功。"); } catch (Exception ex) { Console.WriteLine(ex.Message); LogHelper.WriteLog(ex); } } receiveKey = Console.ReadLine(); } MqManager.LinkServer.Dispose(); } }
2.2.四、Receive項目測試
/// <summary> /// 接收消息隊列管理(線程) /// </summary> public class ReceiveManager : IDisposable { private Thread _thread = null; public static ReceiveManager Instance { get; set; } = new ReceiveManager(); /// <summary> /// 開始 /// </summary> public void Start() { StartReceive(); } /// <summary> /// 接收線程 /// </summary> private void StartReceive() { _thread = new Thread(new ThreadStart(Receive)) { Name = "ReceiveThread", IsBackground = true }; _thread.Start(); } /// <summary> /// 接收線程調用方法 /// </summary> private void Receive() { Message msg = null; while (true) { try { msg = MqManager.LinkServer.Receive(); if (msg != null) { Console.WriteLine("----------------------------------------------------"); Console.WriteLine("Lable: " + msg.Label); Dictionary<CommandType, List<string>> keyValuePairs = msg.Body as Dictionary<CommandType, List<string>>; Console.WriteLine("Body CommandType: " + keyValuePairs.Keys.First()); Console.WriteLine("Body Details: "); foreach (var item in keyValuePairs.Values.First()) { Console.WriteLine(item); } Console.WriteLine("----------------------------------------------------"); } } catch (Exception ex) { Console.WriteLine(ex.Message); LogHelper.WriteLog(ex); } Thread.Sleep(1000); } } /// <summary> /// 結束 /// </summary> public void Stop() { Dispose(); } /// <summary> /// 釋放資源 /// </summary> public void Dispose() { try { if (_thread != null) { _thread.Abort(); _thread.Join(); _thread = null; } MqManager.LinkServer.Dispose(); } catch (Exception ex) { Console.WriteLine(ex.Message); } } }
class Program { static void Main(string[] args) { ReceiveManager.Instance.Start(); Console.WriteLine("退出請輸入Exit"); string receiveKey = Console.ReadLine(); while (receiveKey.ToLower() != "exit") { receiveKey = Console.ReadLine(); } ReceiveManager.Instance.Stop(); Console.Read(); } }
客戶端發送hello,world:ui
服務端接收到的信息:spa
工做組下的遠程C/S,代碼已經在上面的示例中提供,將Common\MqManager.cs下的:.net
private readonly LinkType linkType = LinkType.LocalHost;改爲private readonly LinkType linkType = LinkType.RemoteServer;便可。線程
既然要與遠程服務器交互(發送/接收)隊列信息,首當其衝的是訪問權限問題,沒有權限,一切免談。
下面講一下遠程服務器(代碼中的192.168.2.165,Win7系統)要設置的內容:
3.2.一、在運行中輸入compmgmt.msc->服務和應用程序->消息隊列->右鍵屬性->服務器安全性->禁用未經身份驗證的 RPC 調用->把勾勾去掉->應用。
3.2.二、在消息隊列->專用隊列->新建一個代碼中用到的HelloWorld隊列,勾上事務性->肯定。
爲何要手工建HelloWorld消息隊列?由於要對這個隊列進行匿名訪問受權,後面會講到。至於事務性這個勾,這個要與代碼相一致。由於本示例中使用了MessageQueueTransaction來發送事務信息,因此必須得勾上這個勾,否則的話,發送時沒有任何的報錯信息,可是服務器就是收不到隊列信息。
3.2.三、專用隊列->HelloWorld->右鍵屬性->安全->ANONYMOUS LOGON->徹底控制->應用。
3.2.四、在運行中輸入regedit->HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSMQ\Parameters\security->新建兩個DWORD值:AllowNonauthenticatedRpc、NewRemoteReadServerDenyWorkgroupClient->分別雙擊將數值數據改爲1。
3.2.五、關於防火牆,我是關閉了的,假如您的電腦防火牆是打開了的話,請檢查一下Message Queuing是否是被容許的?
客戶端發送A,B,C,D:
服務器端接收到的信息:
參考自:
https://www.cnblogs.com/xinhaijulan/archive/2010/08/22/1805768.html
http://www.javashuo.com/article/p-yumuwztq-gd.html
https://blog.csdn.net/jiyiqinlovexx/article/details/17803857