C#隊列學習筆記:MSMQ入門二

    1、引言

    按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程序才能夠調用的隊列,有些狀況下爲了安全起見定義爲私有隊列。因此剛開始的時候認爲,要想訪問遠程消息隊列,只能使用公共隊列。可是後來發現,公共隊列依賴Domain Controller(域控),在實際部署的時候,要求使用消息隊列的應用必定要在某個域中,有些太苛刻!後來發現,私有隊列也是能夠遠程訪問的。(很困惑爲何私有隊列只能本地訪問,這句話,處處都能看到?!)html

    2、工做組下的本地C/S

    2.一、項目創建

    新建4個項目:安全

    2.二、項目代碼

    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  //刪除
    }
MqMessage.cs

    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;
        }
    }
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.三、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.四、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.三、運行測試

    客戶端發送hello,world:ui

    服務端接收到的信息:spa

    3、工做組下的遠程C/S

    3.一、代碼調整

    工做組下的遠程C/S,代碼已經在上面的示例中提供,將Common\MqManager.cs下的:.net

    private readonly LinkType linkType = LinkType.LocalHost;改爲private readonly LinkType linkType = LinkType.RemoteServer;便可。線程

    3.二、訪問權限

    既然要與遠程服務器交互(發送/接收)隊列信息,首當其衝的是訪問權限問題,沒有權限,一切免談。

    下面講一下遠程服務器(代碼中的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是否是被容許的?

    3.三、運行測試

    客戶端發送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

    http://www.javashuo.com/article/p-xfnhlwee-gg.html

相關文章
相關標籤/搜索