一、引言 按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程式才可以調用的隊列,有些情況下為了安全起見定義為私有隊列。所以剛開始的時候認為,要想訪問遠程消息隊列,只能使用公共隊列。但是後來發現,公共隊列依賴Domain Controller(域控),在 ...
一、引言
按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程式才可以調用的隊列,有些情況下為了安全起見定義為私有隊列。所以剛開始的時候認為,要想訪問遠程消息隊列,只能使用公共隊列。但是後來發現,公共隊列依賴Domain Controller(域控),在實際部署的時候,要求使用消息隊列的應用一定要在某個域中,有些太苛刻!後來發現,私有隊列也是可以遠程訪問的。(很困惑為什麼私有隊列只能本地訪問,這句話,到處都能看到?!)
二、工作組下的本地C/S
2.1、項目建立
新建4個項目:
2.2、項目代碼
2.2.1、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 //刪除 }MqMessage.cs
2.2.2、Common項目
/// <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; } }LogHelper.cs
/// <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 //遠程伺服器 }MqManager.cs
2.2.3、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(); } }Program.cs
2.2.4、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); } } }ReceiveManage.cs
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(); } }Program.cs
2.3、運行測試
客戶端發送hello,world:
服務端接收到的信息:
三、工作組下的遠程C/S
3.1、代碼調整
工作組下的遠程C/S,代碼已經在上面的示例中提供,將Common\MqManager.cs下的:
private readonly LinkType linkType = LinkType.LocalHost;改成private readonly LinkType linkType = LinkType.RemoteServer;即可。
3.2、訪問許可權
既然要與遠程伺服器交互(發送/接收)隊列信息,首當其衝的是訪問許可權問題,沒有許可權,一切免談。
下麵講一下遠程伺服器(代碼中的192.168.2.165,Win7系統)要設置的內容:
3.2.1、在運行中輸入compmgmt.msc->服務和應用程式->消息隊列->右鍵屬性->伺服器安全性->禁用未經身份驗證的 RPC 調用->把勾勾去掉->應用。
3.2.2、在消息隊列->專用隊列->新建一個代碼中用到的HelloWorld隊列,勾上事務性->確定。
為什麼要手工建HelloWorld消息隊列?因為要對這個隊列進行匿名訪問授權,後面會講到。至於事務性這個勾,這個要與代碼相一致。因為本示例中使用了MessageQueueTransaction來發送事務信息,所以必須得勾上這個勾,不然的話,發送時沒有任何的報錯信息,但是伺服器就是收不到隊列信息。
3.2.3、專用隊列->HelloWorld->右鍵屬性->安全->ANONYMOUS LOGON->完全控制->應用。
3.2.4、在運行中輸入regedit->HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSMQ\Parameters\security->新建兩個DWORD值:AllowNonauthenticatedRpc、NewRemoteReadServerDenyWorkgroupClient->分別雙擊將數值數據改成1。
3.2.5、關於防火牆,我是關閉了的,假如您的電腦防火牆是打開了的話,請檢查一下Message Queuing是不是被允許的?
3.3、運行測試
客戶端發送A,B,C,D:
伺服器端接收到的信息:
參考自:
https://www.cnblogs.com/xinhaijulan/archive/2010/08/22/1805768.html
https://www.cnblogs.com/minily/p/7397746.html
https://blog.csdn.net/jiyiqinlovexx/article/details/17803857
https://www.cnblogs.com/mmbbflyer/p/7773303.html