当前位置: 首页 > 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.jsqmd.com/news/8084/

相关文章:

  • rqlite 集成sqlite-vec 简单说明
  • 英语_阅读_Water Sliding_待读
  • 实用指南:ArcGIS JSAPI 高级教程 - 高亮效果优化之开启使用多高亮样式
  • const在for用不了
  • 10月北京中学集训随笔
  • 使用100%缩放比例重新启动Visual Studio 界面模糊的解决方案
  • 某工程师入职华为,职级比较高,但还看不懂代码,有点尴尬
  • 使用Silobase在几分钟内快速部署后端API
  • 【光照】[各向异性]在UnityURP中的实现
  • 基于HAL库和中断的LED流水灯
  • 3GPP 常用协议标准
  • 从衡阳麻衣事件到AI元人文:用户端元人文实践的进化路径研究——声明ai研究
  • 5_flutter UI框架选型
  • 4_查询flutter版本信息
  • 3_flutter简单教程
  • 如何给 Claude 中的网页做截图
  • 2_gradle配置加速
  • 居必择乡,游必就士 1dd6c2e8d0dd80c2923cea711f1e63dc
  • AI元人文:岐金兰《悬鉴》起源
  • 九月回忆
  • linux commond order
  • PWN手成长之路-07-bjdctf_2020_babystack2-栈溢出+整型溢出
  • MySQL.Data.DLL 官网下载方法 2025
  • 趣题记
  • 执行一次 git commit 后,本地的这次提交能同时推送到 GitHub 和 Gitee 两个远程仓库
  • 【一起学rust | 基础篇】环境配置
  • 免费代理池
  • 白箱算力突破:WBUC与「可能性工程」的诞生(AI元人文系列文章)
  • WPF Microsoft.Extensions.DependencyInjection, Microsoft.Extensions.
  • Linux 中 awk命令提取偶数列数据