开源 C# 快速开发(十七)进程--消息队列MSMQ

文章的目的为了记录使用C# 开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

推荐链接:

开源 C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

开源 C# .net mvc 开发(六)发送邮件、定时以及CMD编程-CSDN博客

开源 C# .net mvc 开发(七)动态图片、动态表格和json数据生成-CSDN博客

开源 C# .net mvc 开发(八)IIS Express轻量化Web服务器的配置和使用-CSDN博客

开源 C# .net mvc 开发(九)websocket--服务器与客户端的实时通信-CSDN博客

本章节主要内容是:进程间 Windows消息队列通信,基于MSMQ 。

核心概念

  1. 消息队列

    • 就像一个"邮箱"。发送方进程将消息发送到这个队列,接收方进程从同一个队列中读取消息。

    • 队列由消息队列服务器管理,消息被持久化存储(通常在磁盘上),确保了可靠性。

  2. 异步通信

    • 发送方和接收方不需要同时在线

    • 发送方发送完消息后就可以继续自己的工作,无需等待接收方立即处理。

    • 接收方可以在自己方便的时候再去队列中获取并处理消息。这实现了进程间的 "解耦"

  3. 可靠传递

    • 因为消息被存储在持久化介质中(如硬盘),即使发送后接收方程序崩溃、网络中断或计算机关机,消息也不会丢失。当接收方程序重新启动后,它仍然可以从队列中收到那条消息。

MSMQ 的工作模式

  • 点对点模式:一个发送方将消息发送到一个特定的队列,一个(且只有一个)接收方从该队列中取出消息进行处理。

  • 多播模式:一个发送方发送一条消息,可以被多个订阅了该消息的接收方同时接收。

主要特点和优势

  • 解耦:发送和接收进程相互独立,不需要知道对方的存在、状态或位置。

  • 可靠性:通过持久化保证消息不丢失。

  • 异步性:提高系统的响应能力和吞吐量。

  • 削峰填谷:当短时间内产生大量请求时,队列可以将其缓冲起来,让接收方按照自己的能力逐步处理,防止系统被压垮。

典型应用场景

  1. 工作流处理:用户提交一个订单,系统向队列发送一条"新订单"消息。后端的库存管理、物流计算等系统可以各自从队列中获取消息并进行处理,无需用户等待所有流程完成。

  2. 系统集成:连接两个不同的、不易直接通信的旧系统。系统A将数据发送到队列,系统B从队列中读取。

  3. 延迟操作:例如,一个网站需要在用户注册24小时后发送一封提醒邮件。它可以将"发送邮件"消息放入队列,并设置其24小时后才可被接收。

总结

MSMQ 不是一种实时、同步的通信方式(像管道或WCF的NetTcp那样),而是一种强调可靠性、稳定性和系统解耦的异步通信机制。 它适用于那些你希望确保消息最终一定能被处理,但不要求立即得到响应的场景。

目录:

1.源码分析

2.所有源码

3.效果演示

一、源码分析

启用MSMQ Windows功能

Windows 10/11 启用步骤:

  1. Win + R 键,输入 optionalfeatures.exe 回车

  2. 在"Windows功能"对话框中,找到 "Microsoft Message Queue (MSMQ) 服务器"

  3. 展开这个选项,勾选所有子项

  4. 点击"确定",等待安装完成

  5. 重启计算机

添加引用System.Messaging;

服务器端函数分析

  1. 构造函数 ServerForm()

    public ServerForm()
    {
    InitializeComponent();
    InitializeMSMQ();
    }

功能:窗体初始化

调用界面组件初始化

启动MSMQ初始化过程

是整个服务器应用的入口点

  1. InitializeMSMQ()

    private void InitializeMSMQ()
    {
    // 创建或连接到消息队列
    if (!MessageQueue.Exists(QueuePath))
    {
    mq = MessageQueue.Create(QueuePath);
    AppendLog("创建新的消息队列: " + QueuePath);
    }
    else
    {
    mq = new MessageQueue(QueuePath);
    AppendLog("连接到现有消息队列: " + QueuePath);
    }

    复制代码
     // 设置消息格式
     mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
    
     // 异步开始接收消息
     StartReceiving();

    }

功能:MSMQ系统初始化

队列存在性检查:检查.\Private$\MyTestQueue是否存在

队列创建/连接:不存在则创建,存在则连接

消息格式化:设置为XML格式,只处理string类型消息

启动消息监听:开始异步接收消息

日志记录:记录初始化过程

  1. StartReceiving()

    private void StartReceiving()
    {
    mq.BeginReceive(TimeSpan.FromSeconds(1), mq, OnMessageReceived);
    }

功能:启动异步消息接收

异步操作:使用BeginReceive非阻塞接收

超时设置:1秒超时,避免永久阻塞

回调指定:收到消息时调用OnMessageReceived

状态对象:传递mq对象作为状态参数

  1. OnMessageReceived(IAsyncResult result)

    private void OnMessageReceived(IAsyncResult result)
    {
    try
    {
    System.Messaging.Message message = mq.EndReceive(result);

    复制代码
         // 在UI线程上更新界面
         this.Invoke(new Action(() =>
         {
             string messageBody = message.Body.ToString();
             AppendLog($"收到消息: {messageBody}");
             txtReceivedMessages.AppendText(messageBody + Environment.NewLine);
    
             // 自动回复
             if (chkAutoReply.Checked)
             {
                 SendReply($"自动回复: 已收到你的消息 '{messageBody}'");
             }
         }));
    
         // 继续接收下一条消息
         StartReceiving();
     }
     catch (MessageQueueException ex)
     {
         this.Invoke(new Action(() =>
             AppendLog($"接收消息错误: {ex.Message}")));
     }

    }

功能:消息到达回调处理

消息提取:调用EndReceive完成异步操作,获取消息

线程安全:使用Invoke在UI线程更新界面

消息显示:在日志和接收消息框显示内容

自动回复:根据复选框状态自动回复客户端

循环接收:递归调用StartReceiving继续监听

异常处理:捕获MSMQ特定异常并记录

  1. SendReply(string replyText)

    private void SendReply(string replyText)
    {
    try
    {
    using (MessageQueue replyQueue = new MessageQueue(@".\Private\ClientQueue")) { if (!MessageQueue.Exists(@".\Private\ClientQueue"))
    {
    MessageQueue.Create(@".\Private$\ClientQueue");
    }

    复制代码
             System.Messaging.Message replyMessage = new System.Messaging.Message(replyText);
             replyQueue.Send(replyMessage);
             AppendLog($"发送回复: {replyText}");
         }
     }
     catch (Exception ex)
     {
         AppendLog($"发送回复失败: {ex.Message}");
     }

    }

功能:向客户端发送回复消息

队列准备:检查并创建客户端队列(如果需要)

消息创建:创建包含回复文本的消息对象

消息发送:通过客户端队列发送消息

资源管理:使用using语句确保队列正确释放

错误处理:捕获发送过程中的异常

  1. SendToClient(string message)

    private void SendToClient(string message)
    {
    try
    {
    using (MessageQueue clientQueue = new MessageQueue(@".\Private\ClientQueue")) { if (!MessageQueue.Exists(@".\Private\ClientQueue"))
    {
    MessageQueue.Create(@".\Private$\ClientQueue");
    }

    复制代码
             System.Messaging.Message msg = new System.Messaging.Message(message);
             clientQueue.Send(msg);
             AppendLog($"发送到客户端: {message}");
         }
     }
     catch (Exception ex)
     {
         AppendLog($"发送到客户端失败: {ex.Message}");
     }

    }

功能:主动向客户端发送消息

手动发送:不同于自动回复,这是服务器主动发起的通信

队列管理:同样确保客户端队列存在

消息构建发送:创建并发送消息

日志记录:记录发送操作

  1. AppendLog(string log)

    private void AppendLog(string log)
    {
    string timestamp = DateTime.Now.ToString("HH:mm:ss");
    txtLog.AppendText($"[{timestamp}] {log}{Environment.NewLine}");
    }

功能:日志记录工具函数

时间戳:为每条日志添加当前时间

格式统一:统一的日志格式 [时间] 内容

界面更新:直接追加到日志文本框

客户端函数分析

  1. CheckMSMQAvailability()

    private void CheckMSMQAvailability()
    {
    try
    {
    if (!IsMSMQAvailable())
    {
    // 错误处理和用户指导
    AppendLog("MSMQ功能未安装!");
    AppendLog("请启用Windows MSMQ功能:");
    // ... 详细指导信息
    btnSendToServer.Enabled = false;
    btnCheckServer.Enabled = false;
    txtMessageToServer.Enabled = false;
    return;
    }

    复制代码
         AppendLog("MSMQ功能可用,正在初始化...");
         InitializeMSMQ();
     }
     catch (Exception ex)
     {
         AppendLog($"检查MSMQ可用性失败: {ex.Message}");
     }

    }

功能:MSMQ环境预检查

功能检测:调用IsMSMQAvailable()检测MSMQ是否可用

用户引导:提供详细的安装指导

界面控制:禁用相关功能按钮

错误处理:捕获检测过程中的异常

  1. IsMSMQAvailable()

    private bool IsMSMQAvailable()
    {
    try
    {
    string testPath = @".\Private$\MSMQTestQueue_" + Guid.NewGuid().ToString("N");
    bool exists = MessageQueue.Exists(testPath);

    复制代码
         if (!exists)
         {
             using (var testQueue = MessageQueue.Create(testPath, false))
             {
                 // 创建成功
             }
             MessageQueue.Delete(testPath);
         }
         else
         {
             MessageQueue.Delete(testPath);
         }
         return true;
     }
     catch
     {
         return false;
     }

    }

功能:MSMQ功能可用性测试

唯一性测试:使用GUID创建唯一测试队列名,避免冲突

完整流程测试:测试队列创建、存在性检查、删除等完整操作

异常检测:通过try-catch检测任何MSMQ相关异常

资源清理:确保测试队列被正确清理

  1. CheckServerStatus()

    private void CheckServerStatus()
    {
    try
    {
    if (MessageQueue.Exists(ServerQueuePath))
    {
    AppendLog("服务器状态: ✅ 在线");
    btnSendToServer.Enabled = true;
    txtMessageToServer.Enabled = true;
    lblServerStatus.Text = "服务器状态: 在线";
    lblServerStatus.ForeColor = Color.Green;
    }
    else
    {
    AppendLog("服务器状态: ❌ 离线");
    btnSendToServer.Enabled = false;
    lblServerStatus.Text = "服务器状态: 离线";
    lblServerStatus.ForeColor = Color.Red;
    }
    }
    catch (Exception ex)
    {
    AppendLog($"检查服务器状态失败: {ex.Message}");
    }
    }

功能:服务器连接状态检查

队列存在性检查:检查服务器队列是否存在

界面状态更新:更新按钮启用状态和标签显示

视觉反馈:使用颜色区分在线/离线状态

日志记录:记录状态检查结果

  1. SendToServer(string message)

    private void SendToServer(string message)
    {
    try
    {
    if (!MessageQueue.Exists(ServerQueuePath))
    {
    AppendLog("错误:服务器队列不存在,请先启动服务器");
    MessageBox.Show("服务器队列不存在,请先启动服务器程序", "错误",
    MessageBoxButtons.OK, MessageBoxIcon.Error);
    btnSendToServer.Enabled = false;
    return;
    }

    复制代码
         using (MessageQueue serverQueue = new MessageQueue(ServerQueuePath))
         {
             serverQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });
    
             System.Messaging.Message msg = new System.Messaging.Message(message);
             serverQueue.Send(msg);
             AppendLog($"发送到服务器: {message}");
    
             // 添加到发送历史
             txtSentMessages.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}{Environment.NewLine}");
             txtSentMessages.SelectionStart = txtSentMessages.Text.Length;
             txtSentMessages.ScrollToCaret();
         }
     }
     catch (MessageQueueException ex)
     {
         AppendLog($"发送到服务器失败 - 队列错误: {ex.Message}");
         btnSendToServer.Enabled = false;
     }
     catch (Exception ex)
     {
         AppendLog($"发送到服务器失败: {ex.Message}");
     }

    }

功能:向服务器发送消息

前置检查:发送前确认服务器队列存在

格式设置:确保消息格式化器正确设置

消息发送:创建并发送消息到服务器队列

发送历史:记录到发送历史文本框

自动滚动:滚动到最新消息

错误处理:区分MSMQ异常和一般异常

  1. 重试机制函数

    private void StartReceiving()
    {
    try
    {
    mq.BeginReceive(TimeSpan.FromSeconds(1), mq, OnMessageReceived);
    }
    catch (Exception ex)
    {
    AppendLog($"开始接收消息失败: {ex.Message}");
    // 3秒后重试
    System.Threading.Timer timer = null;
    timer = new System.Threading.Timer(_ =>
    {
    StartReceiving();
    timer?.Dispose();
    }, null, 3000, System.Threading.Timeout.Infinite);
    }
    }

功能:带重试的消息接收启动

异常恢复:接收启动失败时自动重试

延迟重试:3秒后重新尝试启动接收

资源管理:使用一次性定时器,完成后立即释放

避免循环:确保重试逻辑不会无限循环

二、所有源码

服务器端ServerForm.cs源码

复制代码
using System;
using System.Messaging;
using System.Windows.Forms;
using System.Text;

namespace MSMQServer
{
    public partial class ServerForm : Form
    {
        private MessageQueue mq;
        private const string QueuePath = @".\Private$\MyTestQueue";

        public ServerForm()
        {
            InitializeComponent();
            InitializeMSMQ();
        }

        private void InitializeMSMQ()
        {
            // 创建或连接到消息队列
            if (!MessageQueue.Exists(QueuePath))
            {
                mq = MessageQueue.Create(QueuePath);
                AppendLog("创建新的消息队列: " + QueuePath);
            }
            else
            {
                mq = new MessageQueue(QueuePath);
                AppendLog("连接到现有消息队列: " + QueuePath);
            }

            // 设置消息格式
            mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

            // 异步开始接收消息
            StartReceiving();
        }

        private void StartReceiving()
        {
            mq.BeginReceive(TimeSpan.FromSeconds(1), mq, OnMessageReceived);
        }

        private void OnMessageReceived(IAsyncResult result)
        {
            try
            {
                System.Messaging.Message message = mq.EndReceive(result);

                // 在UI线程上更新界面
                this.Invoke(new Action(() =>
                {
                    string messageBody = message.Body.ToString();
                    AppendLog($"收到消息: {messageBody}");
                    txtReceivedMessages.AppendText(messageBody + Environment.NewLine);

                    // 自动回复
                    if (chkAutoReply.Checked)
                    {
                        SendReply($"自动回复: 已收到你的消息 '{messageBody}'");
                    }
                }));

                // 继续接收下一条消息
                StartReceiving();
            }
            catch (MessageQueueException ex)
            {
                this.Invoke(new Action(() =>
                    AppendLog($"接收消息错误: {ex.Message}")));
            }
        }

        private void SendReply(string replyText)
        {
            try
            {
                using (MessageQueue replyQueue = new MessageQueue(@".\Private$\ClientQueue"))
                {
                    if (!MessageQueue.Exists(@".\Private$\ClientQueue"))
                    {
                        MessageQueue.Create(@".\Private$\ClientQueue");
                    }

                    System.Messaging.Message replyMessage = new System.Messaging.Message(replyText);
                    replyQueue.Send(replyMessage);
                    AppendLog($"发送回复: {replyText}");
                }
            }
            catch (Exception ex)
            {
                AppendLog($"发送回复失败: {ex.Message}");
            }
        }

        private void btnSendToClient_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtMessageToClient.Text))
            {
                SendToClient(txtMessageToClient.Text);
                txtMessageToClient.Clear();
            }
        }

        private void SendToClient(string message)
        {
            try
            {
                using (MessageQueue clientQueue = new MessageQueue(@".\Private$\ClientQueue"))
                {
                    if (!MessageQueue.Exists(@".\Private$\ClientQueue"))
                    {
                        MessageQueue.Create(@".\Private$\ClientQueue");
                    }

                    System.Messaging.Message msg = new System.Messaging.Message(message);
                    clientQueue.Send(msg);
                    AppendLog($"发送到客户端: {message}");
                }
            }
            catch (Exception ex)
            {
                AppendLog($"发送到客户端失败: {ex.Message}");
            }
        }

        private void AppendLog(string log)
        {
            string timestamp = DateTime.Now.ToString("HH:mm:ss");
            txtLog.AppendText($"[{timestamp}] {log}{Environment.NewLine}");
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            mq?.Close();
            mq?.Dispose();
            base.OnFormClosing(e);
        }
    }
}

服务器端ServerForm.designer.cs源码

复制代码
using System;
using System.Windows.Forms;

namespace MSMQServer
{

    partial class ServerForm
    {
        private System.ComponentModel.IContainer components = null;
        private TextBox txtLog;
        private TextBox txtReceivedMessages;
        private TextBox txtMessageToClient;
        private Button btnSendToClient;
        private CheckBox chkAutoReply;

        private void InitializeComponent()
        {
            this.txtLog = new TextBox();
            this.txtReceivedMessages = new TextBox();
            this.txtMessageToClient = new TextBox();
            this.btnSendToClient = new Button();
            this.chkAutoReply = new CheckBox();

            // 布局代码
            this.SuspendLayout();

            // txtLog - 显示操作日志
            this.txtLog.Multiline = true;
            this.txtLog.ScrollBars = ScrollBars.Vertical;
            this.txtLog.Location = new System.Drawing.Point(12, 12);
            this.txtLog.Size = new System.Drawing.Size(400, 150);
            this.txtLog.ReadOnly = true;

            // txtReceivedMessages - 显示接收到的消息
            this.txtReceivedMessages.Multiline = true;
            this.txtReceivedMessages.ScrollBars = ScrollBars.Vertical;
            this.txtReceivedMessages.Location = new System.Drawing.Point(12, 180);
            this.txtReceivedMessages.Size = new System.Drawing.Size(400, 100);
            this.txtReceivedMessages.ReadOnly = true;

            // 发送消息区域
            this.txtMessageToClient.Location = new System.Drawing.Point(12, 300);
            this.txtMessageToClient.Size = new System.Drawing.Size(300, 20);

            this.btnSendToClient.Location = new System.Drawing.Point(320, 298);
            this.btnSendToClient.Size = new System.Drawing.Size(75, 23);
            this.btnSendToClient.Text = "发送到客户端";
            this.btnSendToClient.Click += new EventHandler(this.btnSendToClient_Click);

            this.chkAutoReply.Location = new System.Drawing.Point(12, 330);
            this.chkAutoReply.Size = new System.Drawing.Size(150, 20);
            this.chkAutoReply.Text = "自动回复客户端";
            this.chkAutoReply.Checked = true;

            this.Text = "MSMQ 服务器";
            this.Size = new System.Drawing.Size(450, 400);
            this.Controls.AddRange(new Control[] {
            txtLog, txtReceivedMessages, txtMessageToClient,
            btnSendToClient, chkAutoReply
        });

            this.ResumeLayout(false);
        }
    }
}

客户端ClientForm.cs源码

复制代码
using System;
using System.Messaging;
using System.Windows.Forms;
using System.Drawing;
using System.Text;

namespace MSMQClient
{
    public partial class ClientForm : Form
    {
        private MessageQueue mq;
        private const string QueuePath = @".\Private$\ClientQueue";
        private const string ServerQueuePath = @".\Private$\MyTestQueue";

        public ClientForm()
        {
            InitializeComponent();
            CheckMSMQAvailability();
        }

        private void CheckMSMQAvailability()
        {
            try
            {
                if (!IsMSMQAvailable())
                {
                    AppendLog("MSMQ功能未安装!");
                    AppendLog("请启用Windows MSMQ功能:");
                    AppendLog("1. 按 Win+R,输入 optionalfeatures.exe");
                    AppendLog("2. 找到并启用 'Microsoft Message Queue (MSMQ) 服务器'");
                    AppendLog("3. 重启计算机后重新运行程序");

                    btnSendToServer.Enabled = false;
                    btnCheckServer.Enabled = false;
                    txtMessageToServer.Enabled = false;

                    MessageBox.Show(
                        "MSMQ功能未安装!\n\n" +
                        "请按以下步骤启用:\n" +
                        "1. 按 Win+R,输入 optionalfeatures.exe\n" +
                        "2. 找到并启用 'Microsoft Message Queue (MSMQ) 服务器'\n" +
                        "3. 重启计算机后重新运行程序",
                        "MSMQ未安装",
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Warning);
                    return;
                }

                AppendLog("MSMQ功能可用,正在初始化...");
                InitializeMSMQ();
            }
            catch (Exception ex)
            {
                AppendLog($"检查MSMQ可用性失败: {ex.Message}");
            }
        }

        private bool IsMSMQAvailable()
        {
            try
            {
                // 尝试使用MessageQueue来检测MSMQ是否可用
                string testPath = @".\Private$\MSMQTestQueue_" + Guid.NewGuid().ToString("N");
                bool exists = MessageQueue.Exists(testPath);

                if (!exists)
                {
                    // 尝试创建测试队列
                    using (var testQueue = MessageQueue.Create(testPath, false))
                    {
                        // 创建成功
                    }
                    // 删除测试队列
                    MessageQueue.Delete(testPath);
                }
                else
                {
                    // 如果已存在,删除它
                    MessageQueue.Delete(testPath);
                }

                return true;
            }
            catch
            {
                return false;
            }
        }

        private void InitializeMSMQ()
        {
            try
            {
                // 创建或连接到客户端消息队列
                if (!MessageQueue.Exists(QueuePath))
                {
                    mq = MessageQueue.Create(QueuePath);
                    AppendLog("创建客户端消息队列: " + QueuePath);
                }
                else
                {
                    mq = new MessageQueue(QueuePath);
                    AppendLog("连接到现有客户端消息队列: " + QueuePath);
                }

                // 设置消息格式
                mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

                // 检查服务器队列是否存在
                CheckServerStatus();

                // 异步开始接收消息
                StartReceiving();

                AppendLog("客户端初始化完成,可以开始通信");
            }
            catch (Exception ex)
            {
                AppendLog($"初始化MSMQ失败: {ex.Message}");
            }
        }

        private void StartReceiving()
        {
            try
            {
                mq.BeginReceive(TimeSpan.FromSeconds(1), mq, OnMessageReceived);
            }
            catch (Exception ex)
            {
                AppendLog($"开始接收消息失败: {ex.Message}");
                // 3秒后重试
                System.Threading.Timer timer = null;
                timer = new System.Threading.Timer(_ =>
                {
                    StartReceiving();
                    timer?.Dispose();
                }, null, 3000, System.Threading.Timeout.Infinite);
            }
        }

        private void OnMessageReceived(IAsyncResult result)
        {
            try
            {
                System.Messaging.Message message = mq.EndReceive(result);

                // 在UI线程上更新界面
                this.Invoke(new Action(() =>
                {
                    string messageBody = message.Body.ToString();
                    AppendLog($"收到服务器回复: {messageBody}");
                    txtReceivedMessages.AppendText($"[{DateTime.Now:HH:mm:ss}] {messageBody}{Environment.NewLine}");

                    // 滚动到最新消息
                    txtReceivedMessages.SelectionStart = txtReceivedMessages.Text.Length;
                    txtReceivedMessages.ScrollToCaret();
                }));

                // 继续接收下一条消息
                StartReceiving();
            }
            catch (MessageQueueException ex)
            {
                this.Invoke(new Action(() =>
                    AppendLog($"接收消息错误: {ex.Message}")));
            }
            catch (Exception ex)
            {
                this.Invoke(new Action(() =>
                    AppendLog($"处理消息错误: {ex.Message}")));
            }
        }

        private void btnSendToServer_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(txtMessageToServer.Text.Trim()))
            {
                SendToServer(txtMessageToServer.Text.Trim());
                txtMessageToServer.Clear();
                txtMessageToServer.Focus();
            }
            else
            {
                MessageBox.Show("请输入要发送的消息", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private void SendToServer(string message)
        {
            try
            {
                if (!MessageQueue.Exists(ServerQueuePath))
                {
                    AppendLog("错误:服务器队列不存在,请先启动服务器");
                    MessageBox.Show("服务器队列不存在,请先启动服务器程序", "错误",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                    btnSendToServer.Enabled = false;
                    return;
                }

                using (MessageQueue serverQueue = new MessageQueue(ServerQueuePath))
                {
                    serverQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) });

                    System.Messaging.Message msg = new System.Messaging.Message(message);
                    serverQueue.Send(msg);
                    AppendLog($"发送到服务器: {message}");

                    // 添加到发送历史
                    txtSentMessages.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}{Environment.NewLine}");
                    txtSentMessages.SelectionStart = txtSentMessages.Text.Length;
                    txtSentMessages.ScrollToCaret();
                }
            }
            catch (MessageQueueException ex)
            {
                AppendLog($"发送到服务器失败 - 队列错误: {ex.Message}");
                btnSendToServer.Enabled = false;
            }
            catch (Exception ex)
            {
                AppendLog($"发送到服务器失败: {ex.Message}");
            }
        }

        private void txtMessageToServer_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)Keys.Enter)
            {
                btnSendToServer_Click(sender, e);
                e.Handled = true;
            }
        }

        private void btnClearLog_Click(object sender, EventArgs e)
        {
            txtLog.Clear();
            AppendLog("日志已清空");
        }

        private void btnClearReceived_Click(object sender, EventArgs e)
        {
            txtReceivedMessages.Clear();
            AppendLog("接收消息已清空");
        }

        private void btnClearSent_Click(object sender, EventArgs e)
        {
            txtSentMessages.Clear();
            AppendLog("发送历史已清空");
        }

        private void btnCheckServer_Click(object sender, EventArgs e)
        {
            CheckServerStatus();
        }

        private void CheckServerStatus()
        {
            try
            {
                if (MessageQueue.Exists(ServerQueuePath))
                {
                    AppendLog("服务器状态: ✅ 在线");
                    btnSendToServer.Enabled = true;
                    txtMessageToServer.Enabled = true;
                    lblServerStatus.Text = "服务器状态: 在线";
                    lblServerStatus.ForeColor = Color.Green;
                }
                else
                {
                    AppendLog("服务器状态: ❌ 离线");
                    btnSendToServer.Enabled = false;
                    lblServerStatus.Text = "服务器状态: 离线";
                    lblServerStatus.ForeColor = Color.Red;
                }
            }
            catch (Exception ex)
            {
                AppendLog($"检查服务器状态失败: {ex.Message}");
            }
        }

        private void AppendLog(string log)
        {
            string timestamp = DateTime.Now.ToString("HH:mm:ss");
            if (txtLog.TextLength > 10000) // 防止日志过长
            {
                txtLog.Clear();
            }
            txtLog.AppendText($"[{timestamp}] {log}{Environment.NewLine}");

            // 自动滚动到最新日志
            txtLog.SelectionStart = txtLog.Text.Length;
            txtLog.ScrollToCaret();
        }

        private void btnReconnect_Click(object sender, EventArgs e)
        {
            AppendLog("重新连接MSMQ...");
            CheckMSMQAvailability();
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            try
            {
                mq?.Close();
                mq?.Dispose();
            }
            catch (Exception ex)
            {
                MessageBox.Show($"关闭时发生错误: {ex.Message}", "错误",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            base.OnFormClosing(e);
        }
    }
}

客户端ClientForm.designer.cs源码

复制代码
using System;
using System.Drawing;
using System.Windows.Forms;

namespace MSMQClient
{
    partial class ClientForm
    {
        private System.ComponentModel.IContainer components = null;

        private TextBox txtLog;
        private TextBox txtReceivedMessages;
        private TextBox txtSentMessages;
        private TextBox txtMessageToServer;
        private Button btnSendToServer;
        private Button btnClearLog;
        private Button btnClearReceived;
        private Button btnClearSent;
        private Button btnCheckServer;
        private Button btnReconnect;
        private Label lblMessageToServer;
        private Label lblReceivedMessages;
        private Label lblSentMessages;
        private Label lblLog;
        private Label lblServerStatus;
        private Label lblTitle;
        private Panel panel1;
        private Panel panel2;
        private Panel panel3;
        private Panel panel4;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(784, 661);
            this.Text = "MSMQ 客户端";
            this.StartPosition = FormStartPosition.CenterScreen;
            this.MinimumSize = new Size(800, 700);

            InitializeControls();
            SetupLayout();
        }

        private void InitializeControls()
        {
            // 标题
            this.lblTitle = new Label();
            this.lblTitle.Text = "MSMQ 客户端通信程序";
            this.lblTitle.Font = new Font("微软雅黑", 14F, FontStyle.Bold);
            this.lblTitle.ForeColor = Color.DarkBlue;
            this.lblTitle.TextAlign = ContentAlignment.MiddleCenter;
            this.lblTitle.Location = new Point(12, 10);
            this.lblTitle.Size = new Size(760, 30);

            // 服务器状态标签
            this.lblServerStatus = new Label();
            this.lblServerStatus.Text = "服务器状态: 检测中...";
            this.lblServerStatus.Font = new Font("微软雅黑", 10F, FontStyle.Bold);
            this.lblServerStatus.ForeColor = Color.Orange;
            this.lblServerStatus.Location = new Point(12, 45);
            this.lblServerStatus.Size = new Size(200, 20);

            // 日志文本框
            this.txtLog = new TextBox();
            this.txtLog.Multiline = true;
            this.txtLog.ScrollBars = ScrollBars.Vertical;
            this.txtLog.Location = new Point(12, 80);
            this.txtLog.Size = new Size(760, 120);
            this.txtLog.ReadOnly = true;
            this.txtLog.BackColor = Color.White;
            this.txtLog.Font = new Font("Consolas", 9F);

            // 接收到的消息文本框
            this.txtReceivedMessages = new TextBox();
            this.txtReceivedMessages.Multiline = true;
            this.txtReceivedMessages.ScrollBars = ScrollBars.Vertical;
            this.txtReceivedMessages.Location = new Point(12, 235);
            this.txtReceivedMessages.Size = new Size(760, 120);
            this.txtReceivedMessages.ReadOnly = true;
            this.txtReceivedMessages.BackColor = Color.LightYellow;
            this.txtReceivedMessages.Font = new Font("微软雅黑", 9F);

            // 已发送的消息文本框
            this.txtSentMessages = new TextBox();
            this.txtSentMessages.Multiline = true;
            this.txtSentMessages.ScrollBars = ScrollBars.Vertical;
            this.txtSentMessages.Location = new Point(12, 390);
            this.txtSentMessages.Size = new Size(760, 120);
            this.txtSentMessages.ReadOnly = true;
            this.txtSentMessages.BackColor = Color.LightCyan;
            this.txtSentMessages.Font = new Font("微软雅黑", 9F);

            // 消息输入框
            this.txtMessageToServer = new TextBox();
            this.txtMessageToServer.Location = new Point(12, 545);
            this.txtMessageToServer.Size = new Size(500, 23);
            this.txtMessageToServer.Font = new Font("微软雅黑", 10F);


            // 标签
            this.lblLog = new Label();
            this.lblLog.Text = "操作日志:";
            this.lblLog.Location = new Point(12, 60);
            this.lblLog.Size = new Size(100, 20);
            this.lblLog.Font = new Font("微软雅黑", 9F, FontStyle.Bold);

            this.lblReceivedMessages = new Label();
            this.lblReceivedMessages.Text = "从服务器接收的消息:";
            this.lblReceivedMessages.Location = new Point(12, 215);
            this.lblReceivedMessages.Size = new Size(200, 20);
            this.lblReceivedMessages.Font = new Font("微软雅黑", 9F, FontStyle.Bold);

            this.lblSentMessages = new Label();
            this.lblSentMessages.Text = "已发送到服务器的消息:";
            this.lblSentMessages.Location = new Point(12, 370);
            this.lblSentMessages.Size = new Size(200, 20);
            this.lblSentMessages.Font = new Font("微软雅黑", 9F, FontStyle.Bold);

            this.lblMessageToServer = new Label();
            this.lblMessageToServer.Text = "发送消息到服务器:";
            this.lblMessageToServer.Location = new Point(12, 525);
            this.lblMessageToServer.Size = new Size(150, 20);
            this.lblMessageToServer.Font = new Font("微软雅黑", 9F, FontStyle.Bold);

            // 按钮
            this.btnSendToServer = new Button();
            this.btnSendToServer.Text = "发送";
            this.btnSendToServer.Location = new Point(520, 543);
            this.btnSendToServer.Size = new Size(70, 27);
            this.btnSendToServer.BackColor = Color.LightBlue;
            this.btnSendToServer.Font = new Font("微软雅黑", 9F, FontStyle.Bold);
            this.btnSendToServer.UseVisualStyleBackColor = false;

            this.btnCheckServer = new Button();
            this.btnCheckServer.Text = "检查服务器";
            this.btnCheckServer.Location = new Point(600, 543);
            this.btnCheckServer.Size = new Size(80, 27);
            this.btnCheckServer.BackColor = Color.LightGreen;
            this.btnCheckServer.Font = new Font("微软雅黑", 9F);
            this.btnCheckServer.UseVisualStyleBackColor = false;

            this.btnReconnect = new Button();
            this.btnReconnect.Text = "重新连接";
            this.btnReconnect.Location = new Point(690, 543);
            this.btnReconnect.Size = new Size(80, 27);
            this.btnReconnect.BackColor = Color.LightSalmon;
            this.btnReconnect.Font = new Font("微软雅黑", 9F);
            this.btnReconnect.UseVisualStyleBackColor = false;

            this.btnClearLog = new Button();
            this.btnClearLog.Text = "清空日志";
            this.btnClearLog.Location = new Point(690, 200);
            this.btnClearLog.Size = new Size(80, 25);
            this.btnClearLog.Font = new Font("微软雅黑", 8F);
            this.btnClearLog.UseVisualStyleBackColor = true;

            this.btnClearReceived = new Button();
            this.btnClearReceived.Text = "清空接收";
            this.btnClearReceived.Location = new Point(690, 355);
            this.btnClearReceived.Size = new Size(80, 25);
            this.btnClearReceived.Font = new Font("微软雅黑", 8F);
            this.btnClearReceived.UseVisualStyleBackColor = true;

            this.btnClearSent = new Button();
            this.btnClearSent.Text = "清空发送";
            this.btnClearSent.Location = new Point(690, 510);
            this.btnClearSent.Size = new Size(80, 25);
            this.btnClearSent.Font = new Font("微软雅黑", 8F);
            this.btnClearSent.UseVisualStyleBackColor = true;
        }

        private void SetupLayout()
        {
            // 添加控件到表单
            this.Controls.Add(this.lblTitle);
            this.Controls.Add(this.lblServerStatus);

            this.Controls.Add(this.lblLog);
            this.Controls.Add(this.txtLog);
            this.Controls.Add(this.lblReceivedMessages);
            this.Controls.Add(this.txtReceivedMessages);
            this.Controls.Add(this.lblSentMessages);
            this.Controls.Add(this.txtSentMessages);
            this.Controls.Add(this.lblMessageToServer);
            this.Controls.Add(this.txtMessageToServer);

            this.Controls.Add(this.btnSendToServer);
            this.Controls.Add(this.btnCheckServer);
            this.Controls.Add(this.btnReconnect);
            this.Controls.Add(this.btnClearLog);
            this.Controls.Add(this.btnClearReceived);
            this.Controls.Add(this.btnClearSent);

            // 设置事件处理
            this.btnSendToServer.Click += new EventHandler(this.btnSendToServer_Click);
            this.btnCheckServer.Click += new EventHandler(this.btnCheckServer_Click);
            this.btnReconnect.Click += new EventHandler(this.btnReconnect_Click);
            this.btnClearLog.Click += new EventHandler(this.btnClearLog_Click);
            this.btnClearReceived.Click += new EventHandler(this.btnClearReceived_Click);
            this.btnClearSent.Click += new EventHandler(this.btnClearSent_Click);
            this.txtMessageToServer.KeyPress += new KeyPressEventHandler(this.txtMessageToServer_KeyPress);
        }


    }
}

三、效果演示

先打开服务器,发送 Server to client,关闭以后打开客户端,可以看到接收到数据。

客户端发送 client to server,打开服务器,可以看到接收到数据。

相关推荐
Python私教9 小时前
5分钟上手 MongoDB:从零安装到第一条数据插入(Windows / macOS / Linux 全平台图解)
windows·mongodb·macos
ApacheSeaTunnel9 小时前
从小时级到分钟级:多点DMALL如何用Apache SeaTunnel把数据集成成本砍到1/3?
大数据·开源·数据集成·seatunnel·技术分享
独立开发者Pony10 小时前
【开源】假期爆肝我做了一个全新的Ai写小说工具模式
人工智能·开源·node.js
Yana_Zeng11 小时前
win10安装spark3.1详细流程(小白用)
hadoop·windows·spark
星光一影11 小时前
【OA办公系统】神点企业OA办公助手/全开源
mysql·nginx·开源·php·源代码管理
静谧之心11 小时前
在 K8s 上可靠运行 PD 分离推理:RBG 的设计与实现
云原生·容器·golang·kubernetes·开源·pd分离
sukalot11 小时前
windows显示驱动开发-浮点、围栏支持、资源管理
windows·驱动开发
ajassi200012 小时前
开源 C++ QT QML 开发(十一)通讯--TCP服务器端
c++·qt·开源