当前位置: 首页 > news >正文

实用指南:开源 C# 快速开发(十四)进程--内存映射

         文章的目的为了记录使用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博客

本章节主要内容是:内存映射文件在C#进程间进行的数据通信,服务器端和客户端之间的通讯。

内存映射文件(Memory-Mapped Files)是一种将磁盘文件或共享内存区域直接映射到进程虚拟地址空间的技术。通过这种技术,应用程序可以像访问普通内存一样访问文件内容,而无需使用传统的文件I/O操作。

目录:

1.源码分析

2.所有源码

3.效果演示

一、源码分析

服务器端代码分析

1. 服务器初始化 InitializeServer()

private void InitializeServer()
{
    try
    {
        // 创建或打开内存映射文件
        mmf = MemoryMappedFile.CreateOrOpen(MapName, Capacity);
        accessor = mmf.CreateViewAccessor();
        // 初始化共享内存:在位置0写入0(数据长度)
        accessor.Write(0, 0);
        // 启动读取线程
        isRunning = true;
        readThread = new Thread(ReadFromSharedMemory);
        readThread.IsBackground = true;  // 设为后台线程,主线程退出时自动终止
        readThread.Start();
        UpdateStatus("服务器已启动 - 等待客户端连接...");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"服务器启动失败: {ex.Message}");
    }
}


关键点:

CreateOrOpen:如果文件不存在则创建,存在则打开

前4字节用于存储数据长度,初始化为0

启动后台线程持续监听消息

2. 读取线程函数 ReadFromSharedMemory()

private void ReadFromSharedMemory()
{
    while (isRunning)  // 循环直到停止标志为false
    {
        try
        {
            // 读取前4字节获取数据长度
            int dataLength = accessor.ReadInt32(0);
            if (dataLength > 0)  // 有数据需要读取
            {
                // 读取实际数据(从第4字节开始)
                byte[] buffer = new byte[dataLength];
                accessor.ReadArray(4, buffer, 0, dataLength);
                string receivedMessage = Encoding.UTF8.GetString(buffer);
                // 在UI线程上安全更新界面
                Invoke(new Action(() =>
                {
                    lstReceivedMessages.Items.Add($"[{DateTime.Now:HH:mm:ss}] 收到: {receivedMessage}");
                    lstReceivedMessages.TopIndex = lstReceivedMessages.Items.Count - 1;
                }));
                // 清空数据长度标记,表示数据已处理
                accessor.Write(0, 0);
            }
            Thread.Sleep(100); // 降低CPU使用率
        }
        catch (ThreadAbortException)
        {
            break;  // 线程被中止时退出循环
        }
        catch (Exception ex)
        {
            // 在UI线程显示错误信息
            Invoke(new Action(() =>
                UpdateStatus($"读取错误: {ex.Message}")));
            Thread.Sleep(1000);
        }
    }
}


数据格式:text
[0-3字节] 数据长度 (int32)
[4-...字节] 实际数据 (UTF-8编码的字符串)


3. 发送消息 SendMessage(string message)

private void SendMessage(string message)
{
    if (string.IsNullOrWhiteSpace(message))
    {
        MessageBox.Show("请输入要发送的消息");
        return;
    }
    try
    {
        // 将字符串转换为字节数组
        byte[] buffer = Encoding.UTF8.GetBytes(message);
        int dataLength = buffer.Length;
        // 检查消息长度是否超出容量
        if (dataLength > Capacity - 4) // 减去存储长度的4字节
        {
            MessageBox.Show("消息太长,无法发送");
            return;
        }
        // 写入数据:先写长度,再写数据
        accessor.Write(0, dataLength);
        accessor.WriteArray(4, buffer, 0, dataLength);
        // 更新发送历史
        lstSentMessages.Items.Add($"[{DateTime.Now:HH:mm:ss}] 发送: {message}");
        lstSentMessages.TopIndex = lstSentMessages.Items.Count - 1;
        txtMessage.Clear();
        txtMessage.Focus();
    }
    catch (Exception ex)
    {
        MessageBox.Show($"发送失败: {ex.Message}");
    }
}



4. 事件处理函数
btnSend_Click

private void btnSend_Click(object sender, EventArgs e)
{
    SendMessage(txtMessage.Text);  // 调用发送消息函数
}


txtMessage_KeyPress

private void txtMessage_KeyPress(object sender, KeyPressEventArgs e)
{
    if (e.KeyChar == (char)Keys.Enter)  // 按Enter键发送
    {
        btnSend_Click(sender, e);
        e.Handled = true;  // 阻止系统处理Enter键
    }
}


ServerForm_FormClosing

private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
{
    isRunning = false;           // 设置停止标志
    readThread?.Join(1000);      // 等待读取线程结束,最多等待1秒
    // 释放资源
    accessor?.Dispose();
    mmf?.Dispose();
}



5. 辅助函数 UpdateStatus(string status)

private void UpdateStatus(string status)
{
    lblStatus.Text = status;  // 更新状态标签
}



客户端代码分析
客户端的函数与服务器端非常相似,主要区别在于初始化部分:

1. 客户端初始化 InitializeClient()

private void InitializeClient()
{
    try
    {
        // 连接到现有的内存映射文件(必须已由服务器创建)
        mmf = MemoryMappedFile.OpenExisting(MapName);
        accessor = mmf.CreateViewAccessor();
        isRunning = true;
        readThread = new Thread(ReadFromSharedMemory);
        readThread.IsBackground = true;
        readThread.Start();
        UpdateStatus("客户端已连接");
    }
    catch (FileNotFoundException)
    {
        MessageBox.Show("服务器未启动,请先启动服务器端程序");
        UpdateStatus("连接失败 - 服务器未启动");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"客户端启动失败: {ex.Message}");
        UpdateStatus($"连接失败: {ex.Message}");
    }
}


关键区别:

使用 OpenExisting 而不是 CreateOrOpen

需要处理 FileNotFoundException(服务器未启动的情况)

二、所有源码

ServerForm.cs文件源码

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace MemoryMappedFileDemo
{public partial class ServerForm : Form{private MemoryMappedFile mmf;private MemoryMappedViewAccessor accessor;private const string MapName = "MySharedMemory";private const int Capacity = 1024; // 1KB共享内存private Thread readThread;private bool isRunning = false;public ServerForm(){InitializeComponent();InitializeServer();}private void InitializeServer(){try{// 创建内存映射文件mmf = MemoryMappedFile.CreateOrOpen(MapName, Capacity);accessor = mmf.CreateViewAccessor();// 初始化共享内存accessor.Write(0, 0); // 写入数据长度isRunning = true;readThread = new Thread(ReadFromSharedMemory);readThread.IsBackground = true;readThread.Start();UpdateStatus("服务器已启动 - 等待客户端连接...");}catch (Exception ex){MessageBox.Show($"服务器启动失败: {ex.Message}");}}private void ReadFromSharedMemory(){while (isRunning){try{// 读取数据长度int dataLength = accessor.ReadInt32(0);if (dataLength > 0){// 读取实际数据byte[] buffer = new byte[dataLength];accessor.ReadArray(4, buffer, 0, dataLength);string receivedMessage = Encoding.UTF8.GetString(buffer);// 在UI线程上更新接收到的消息Invoke(new Action(() =>{lstReceivedMessages.Items.Add($"[{DateTime.Now:HH:mm:ss}] 收到: {receivedMessage}");lstReceivedMessages.TopIndex = lstReceivedMessages.Items.Count - 1;}));// 清空数据长度标记,表示数据已读取accessor.Write(0, 0);}Thread.Sleep(100); // 降低CPU使用率}catch (ThreadAbortException){break;}catch (Exception ex){Invoke(new Action(() =>UpdateStatus($"读取错误: {ex.Message}")));Thread.Sleep(1000);}}}private void btnSend_Click(object sender, EventArgs e){SendMessage(txtMessage.Text);}private void SendMessage(string message){if (string.IsNullOrWhiteSpace(message)){MessageBox.Show("请输入要发送的消息");return;}try{byte[] buffer = Encoding.UTF8.GetBytes(message);int dataLength = buffer.Length;if (dataLength > Capacity - 4) // 减去存储长度的4字节{MessageBox.Show("消息太长,无法发送");return;}// 写入数据长度和实际数据accessor.Write(0, dataLength);accessor.WriteArray(4, buffer, 0, dataLength);lstSentMessages.Items.Add($"[{DateTime.Now:HH:mm:ss}] 发送: {message}");lstSentMessages.TopIndex = lstSentMessages.Items.Count - 1;txtMessage.Clear();txtMessage.Focus();}catch (Exception ex){MessageBox.Show($"发送失败: {ex.Message}");}}private void UpdateStatus(string status){lblStatus.Text = status;}private void ServerForm_FormClosing(object sender, FormClosingEventArgs e){isRunning = false;readThread?.Join(1000);accessor?.Dispose();mmf?.Dispose();}private void txtMessage_KeyPress(object sender, KeyPressEventArgs e){if (e.KeyChar == (char)Keys.Enter){btnSend_Click(sender, e);e.Handled = true;}}#region Windows Form Designer generated codeprivate System.ComponentModel.IContainer components = null;private TextBox txtMessage;private Button btnSend;private ListBox lstReceivedMessages;private ListBox lstSentMessages;private Label lblStatus;private Label label1;private Label label2;private Label label3;protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}private void InitializeComponent(){this.txtMessage = new System.Windows.Forms.TextBox();this.btnSend = new System.Windows.Forms.Button();this.lstReceivedMessages = new System.Windows.Forms.ListBox();this.lstSentMessages = new System.Windows.Forms.ListBox();this.lblStatus = new System.Windows.Forms.Label();this.label1 = new System.Windows.Forms.Label();this.label2 = new System.Windows.Forms.Label();this.label3 = new System.Windows.Forms.Label();this.SuspendLayout();// txtMessagethis.txtMessage.Location = new System.Drawing.Point(12, 30);this.txtMessage.Name = "txtMessage";this.txtMessage.Size = new System.Drawing.Size(300, 20);this.txtMessage.TabIndex = 0;this.txtMessage.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtMessage_KeyPress);// btnSendthis.btnSend.Location = new System.Drawing.Point(318, 28);this.btnSend.Name = "btnSend";this.btnSend.Size = new System.Drawing.Size(75, 23);this.btnSend.TabIndex = 1;this.btnSend.Text = "发送";this.btnSend.UseVisualStyleBackColor = true;this.btnSend.Click += new System.EventHandler(this.btnSend_Click);// lstReceivedMessagesthis.lstReceivedMessages.FormattingEnabled = true;this.lstReceivedMessages.HorizontalScrollbar = true;this.lstReceivedMessages.Location = new System.Drawing.Point(12, 80);this.lstReceivedMessages.Name = "lstReceivedMessages";this.lstReceivedMessages.Size = new System.Drawing.Size(381, 160);this.lstReceivedMessages.TabIndex = 2;// lstSentMessagesthis.lstSentMessages.FormattingEnabled = true;this.lstSentMessages.HorizontalScrollbar = true;this.lstSentMessages.Location = new System.Drawing.Point(12, 270);this.lstSentMessages.Name = "lstSentMessages";this.lstSentMessages.Size = new System.Drawing.Size(381, 160);this.lstSentMessages.TabIndex = 3;// lblStatusthis.lblStatus.AutoSize = true;this.lblStatus.Location = new System.Drawing.Point(12, 450);this.lblStatus.Name = "lblStatus";this.lblStatus.Size = new System.Drawing.Size(55, 13);this.lblStatus.TabIndex = 4;this.lblStatus.Text = "状态: 离线";// label1this.label1.AutoSize = true;this.label1.Location = new System.Drawing.Point(12, 10);this.label1.Name = "label1";this.label1.Size = new System.Drawing.Size(58, 13);this.label1.TabIndex = 5;this.label1.Text = "发送消息:";// label2this.label2.AutoSize = true;this.label2.Location = new System.Drawing.Point(12, 60);this.label2.Name = "label2";this.label2.Size = new System.Drawing.Size(82, 13);this.label2.TabIndex = 6;this.label2.Text = "收到的消息:";// label3this.label3.AutoSize = true;this.label3.Location = new System.Drawing.Point(12, 250);this.label3.Name = "label3";this.label3.Size = new System.Drawing.Size(82, 13);this.label3.TabIndex = 7;this.label3.Text = "发送的消息:";// ServerFormthis.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(405, 480);this.Controls.Add(this.label3);this.Controls.Add(this.label2);this.Controls.Add(this.label1);this.Controls.Add(this.lblStatus);this.Controls.Add(this.lstSentMessages);this.Controls.Add(this.lstReceivedMessages);this.Controls.Add(this.btnSend);this.Controls.Add(this.txtMessage);this.Name = "ServerForm";this.Text = "内存映射文件 - 服务器端";this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ServerForm_FormClosing);this.ResumeLayout(false);this.PerformLayout();}#endregion}
}

ClientForm.cs文件源码

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace MemoryClientDemo
{public partial class ClientForm : Form{private MemoryMappedFile mmf;private MemoryMappedViewAccessor accessor;private const string MapName = "MySharedMemory";private const int Capacity = 1024;private Thread readThread;private bool isRunning = false;public ClientForm(){InitializeComponent();InitializeClient();}private void InitializeClient(){try{// 连接到现有的内存映射文件mmf = MemoryMappedFile.OpenExisting(MapName);accessor = mmf.CreateViewAccessor();isRunning = true;readThread = new Thread(ReadFromSharedMemory);readThread.IsBackground = true;readThread.Start();UpdateStatus("客户端已连接");}catch (FileNotFoundException){MessageBox.Show("服务器未启动,请先启动服务器端程序");UpdateStatus("连接失败 - 服务器未启动");}catch (Exception ex){MessageBox.Show($"客户端启动失败: {ex.Message}");UpdateStatus($"连接失败: {ex.Message}");}}private void ReadFromSharedMemory(){while (isRunning){try{// 读取数据长度int dataLength = accessor.ReadInt32(0);if (dataLength > 0){// 读取实际数据byte[] buffer = new byte[dataLength];accessor.ReadArray(4, buffer, 0, dataLength);string receivedMessage = Encoding.UTF8.GetString(buffer);// 在UI线程上更新接收到的消息Invoke(new Action(() =>{lstReceivedMessages.Items.Add($"[{DateTime.Now:HH:mm:ss}] 收到: {receivedMessage}");lstReceivedMessages.TopIndex = lstReceivedMessages.Items.Count - 1;}));// 清空数据长度标记,表示数据已读取accessor.Write(0, 0);}Thread.Sleep(100);}catch (ThreadAbortException){break;}catch (Exception ex){Invoke(new Action(() =>UpdateStatus($"读取错误: {ex.Message}")));Thread.Sleep(1000);}}}private void btnSend_Click(object sender, EventArgs e){SendMessage(txtMessage.Text);}private void SendMessage(string message){if (string.IsNullOrWhiteSpace(message)){MessageBox.Show("请输入要发送的消息");return;}try{byte[] buffer = Encoding.UTF8.GetBytes(message);int dataLength = buffer.Length;if (dataLength > Capacity - 4){MessageBox.Show("消息太长,无法发送");return;}// 写入数据长度和实际数据accessor.Write(0, dataLength);accessor.WriteArray(4, buffer, 0, dataLength);lstSentMessages.Items.Add($"[{DateTime.Now:HH:mm:ss}] 发送: {message}");lstSentMessages.TopIndex = lstSentMessages.Items.Count - 1;txtMessage.Clear();txtMessage.Focus();}catch (Exception ex){MessageBox.Show($"发送失败: {ex.Message}");}}private void UpdateStatus(string status){lblStatus.Text = status;}private void ClientForm_FormClosing(object sender, FormClosingEventArgs e){isRunning = false;readThread?.Join(1000);accessor?.Dispose();mmf?.Dispose();}private void txtMessage_KeyPress(object sender, KeyPressEventArgs e){if (e.KeyChar == (char)Keys.Enter){btnSend_Click(sender, e);e.Handled = true;}}#region Windows Form Designer generated codeprivate System.ComponentModel.IContainer components = null;private TextBox txtMessage;private Button btnSend;private ListBox lstReceivedMessages;private ListBox lstSentMessages;private Label lblStatus;private Label label1;private Label label2;private Label label3;protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}private void InitializeComponent(){this.txtMessage = new System.Windows.Forms.TextBox();this.btnSend = new System.Windows.Forms.Button();this.lstReceivedMessages = new System.Windows.Forms.ListBox();this.lstSentMessages = new System.Windows.Forms.ListBox();this.lblStatus = new System.Windows.Forms.Label();this.label1 = new System.Windows.Forms.Label();this.label2 = new System.Windows.Forms.Label();this.label3 = new System.Windows.Forms.Label();this.SuspendLayout();// txtMessagethis.txtMessage.Location = new System.Drawing.Point(12, 30);this.txtMessage.Name = "txtMessage";this.txtMessage.Size = new System.Drawing.Size(300, 20);this.txtMessage.TabIndex = 0;this.txtMessage.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtMessage_KeyPress);// btnSendthis.btnSend.Location = new System.Drawing.Point(318, 28);this.btnSend.Name = "btnSend";this.btnSend.Size = new System.Drawing.Size(75, 23);this.btnSend.TabIndex = 1;this.btnSend.Text = "发送";this.btnSend.UseVisualStyleBackColor = true;this.btnSend.Click += new System.EventHandler(this.btnSend_Click);// lstReceivedMessagesthis.lstReceivedMessages.FormattingEnabled = true;this.lstReceivedMessages.HorizontalScrollbar = true;this.lstReceivedMessages.Location = new System.Drawing.Point(12, 80);this.lstReceivedMessages.Name = "lstReceivedMessages";this.lstReceivedMessages.Size = new System.Drawing.Size(381, 160);this.lstReceivedMessages.TabIndex = 2;// lstSentMessagesthis.lstSentMessages.FormattingEnabled = true;this.lstSentMessages.HorizontalScrollbar = true;this.lstSentMessages.Location = new System.Drawing.Point(12, 270);this.lstSentMessages.Name = "lstSentMessages";this.lstSentMessages.Size = new System.Drawing.Size(381, 160);this.lstSentMessages.TabIndex = 3;// lblStatusthis.lblStatus.AutoSize = true;this.lblStatus.Location = new System.Drawing.Point(12, 450);this.lblStatus.Name = "lblStatus";this.lblStatus.Size = new System.Drawing.Size(55, 13);this.lblStatus.TabIndex = 4;this.lblStatus.Text = "状态: 离线";// label1this.label1.AutoSize = true;this.label1.Location = new System.Drawing.Point(12, 10);this.label1.Name = "label1";this.label1.Size = new System.Drawing.Size(58, 13);this.label1.TabIndex = 5;this.label1.Text = "发送消息:";// label2this.label2.AutoSize = true;this.label2.Location = new System.Drawing.Point(12, 60);this.label2.Name = "label2";this.label2.Size = new System.Drawing.Size(82, 13);this.label2.TabIndex = 6;this.label2.Text = "收到的消息:";// label3this.label3.AutoSize = true;this.label3.Location = new System.Drawing.Point(12, 250);this.label3.Name = "label3";this.label3.Size = new System.Drawing.Size(82, 13);this.label3.TabIndex = 7;this.label3.Text = "发送的消息:";// ClientFormthis.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(405, 480);this.Controls.Add(this.label3);this.Controls.Add(this.label2);this.Controls.Add(this.label1);this.Controls.Add(this.lblStatus);this.Controls.Add(this.lstSentMessages);this.Controls.Add(this.lstReceivedMessages);this.Controls.Add(this.btnSend);this.Controls.Add(this.txtMessage);this.Name = "ClientForm";this.Text = "内存映射文件 - 客户端";this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ClientForm_FormClosing);this.ResumeLayout(false);this.PerformLayout();}#endregion}
}

三、效果演示

http://www.zskr.cn/news/15626.html

相关文章:

  • 实用指南:ArcGIS JSAPI 高级教程 - 高亮效果优化之开启使用多高亮样式
  • 10月北京中学集训随笔
  • 使用100%缩放比例重新启动Visual Studio 界面模糊的解决方案
  • 4_查询flutter版本信息
  • 3_flutter简单教程
  • 2_gradle配置加速
  • 九月回忆
  • US$88 BW9 Key Clamp SN-CP-JJ-15 for BMW Motor Keys for SEC-E9 Key Cutting Machine
  • 数论中的欧拉函数
  • 何为“类”?(Java基础语法) - 教程
  • NOI 七
  • 三霍尔BLDC——已知霍尔元件输出与相线输入电压的关系表,如何写程序
  • ZSH 安装配置
  • Spring事务管理:-rollbackFor
  • 微信图片批量保存的办法
  • 从DQN到Double DQN:分离动作选择与价值评估,解决强化学习中的Q值过估计问题
  • CF1916G Optimizations From Chelsu
  • 【游记】北京师范大学讲课
  • Vue之刷新页面会触发的生命周期函数
  • 深入解析:App Store 上架完整流程解析,iOS 应用发布步骤、ipa 文件上传工具、TestFlight 测试与苹果审核经验
  • 傅里叶的一生
  • 实用指南:AI Agent开发平台如何设计?核心架构与工作流实战案例详解
  • 实用指南:OpenAI Sora 2重磅发布:AI视频生成进入“GPT-3.5时刻”
  • 题解:AT_agc038_f [AGC038F] Two Permutations
  • 详细介绍:Java基础
  • 20250929给PRO-RK3566开发板在Buildroot系统下裁剪内核【已关闭摄像头ov4689为例子】 - 指南
  • 解码红黑树
  • 为什么词嵌入可以和位置编码相加
  • 实用指南:软件设计师——04 操作系统
  • 多模态大语言模型OISA - 详解