linux 下消息队列

文章目录

  • [📨 Linux System V 消息队列实战](#📨 Linux System V 消息队列实战)
    • [一、消息队列核心概念 💡](#一、消息队列核心概念 💡)
      • [1. 消息队列特点 🌟](#1. 消息队列特点 🌟)
      • [2. 生命周期 🔄](#2. 生命周期 🔄)
    • 二、项目概述
    • 三、完整代码实现
      • [1. 公共头文件 `common.hpp`](#1. 公共头文件 common.hpp)
      • [2. 发送端 `sender.cpp`](#2. 发送端 sender.cpp)
      • [3. 接收端 `receiver.cpp`](#3. 接收端 receiver.cpp)
    • 三、编译与运行指南
      • [1. 编译命令](#1. 编译命令)
      • [2. 运行顺序](#2. 运行顺序)
    • [四、代码分解与核心函数 🛠️](#四、代码分解与核心函数 🛠️)
      • [1. 公共头文件 `common.hpp` 📁](#1. 公共头文件 common.hpp 📁)
      • [2. 发送端代码解析 📤](#2. 发送端代码解析 📤)
        • [关键点 🔑](#关键点 🔑)
      • [3. 接收端代码解析 📥](#3. 接收端代码解析 📥)
        • [关键点 🔍](#关键点 🔍)
    • [五、常见问题与调试技巧 🚨](#五、常见问题与调试技巧 🚨)
      • [1. 系统命令 💻](#1. 系统命令 💻)

📨 Linux System V 消息队列实战


一、消息队列核心概念 💡

1. 消息队列特点 🌟

  • 📦 结构化数据 :消息包含类型标识(mytype)和正文(data),支持分类处理
  • 异步通信:发送方和接收方无需同时在线
  • 🔒 持久性:消息队列在内核中持久存在,直到显式删除
  • 🔑 访问控制 :通过权限标志(如0666)管理读写权限

2. 生命周期 🔄

  • 创建发送/接收销毁
  • ❗若不主动销毁,队列会持续占用内核资源(通过ipcs -q可查看)

二、项目概述

本示例通过 System V 消息队列 实现跨进程通信,包含三个核心文件:

  • common.hpp:消息队列公共配置
  • sender.cpp:消息生产者(发送端)
  • receiver.cpp:消息消费者(接收端)

三、完整代码实现

1. 公共头文件 common.hpp

cpp 复制代码
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cstring>
#include <cstdlib>

// 消息队列标识配置
const char* pathname = "/home"; // ftok路径参数(需真实存在)
const int proj_id = 666;        // 项目ID(取值范围0-255)

// 消息结构体(必须包含long类型字段)
struct message {
    long mytype;    // 消息类型标识(必须 > 0)
    char data[100]; // 消息正文(最大99字符)
};

// 错误码枚举
enum {
    MSGGET_ERROR = 1,
};

// 通用队列创建/获取函数
int Msgqueue(int flag) {
    key_t key = ftok(pathname, proj_id);
    if (key < 0) {
        perror("ftok failed");
        exit(MSGGET_ERROR);
    }

    int msgid = msgget(key, flag);
    if (msgid < 0) {
        perror("msgget failed");
        exit(MSGGET_ERROR);
    }
    return msgid;
}

// 创建新队列(服务端)
int CreateMsg() {
    return Msgqueue(IPC_CREAT | IPC_EXCL | 0666);
}

// 获取已有队列(客户端)
int Getmsg() {
    return Msgqueue(IPC_CREAT | 0666);
}

2. 发送端 sender.cpp

cpp 复制代码
#include "common.hpp"

int main() {
    // 创建消息队列
    int msgid = CreateMsg();
    std::cout << " 消息队列创建成功! ID: " << msgid << std::endl;

    // 构造消息
    message msg;
    msg.mytype = 1; // 消息类型标识
    snprintf(msg.data, sizeof(msg.data), "hello from sender");

    // 发送消息(阻塞模式)
    if (msgsnd(msgid, &msg, sizeof(msg.data), 0) < 0) {
        perror(" 消息发送失败");
        exit(1);
    }
    std::cout << " 消息已发送: " << msg.data << std::endl;

    return 0;
}

3. 接收端 receiver.cpp

cpp 复制代码
#include "common.hpp"

int main() {
    // 获取消息队列
    int msgid = Getmsg();
    std::cout << " 连接到消息队列 ID: " << msgid << std::endl;

    // 接收消息(阻塞等待类型为1的消息)
    message msg;
    if (msgrcv(msgid, &msg, sizeof(msg.data), 1, 0) < 0) {
        perror(" 消息接收失败");
        exit(1);
    }
    std::cout << " 收到消息: " << msg.data << std::endl;

    // 销毁队列(生产环境慎用!)
    if (msgctl(msgid, IPC_RMID, nullptr) < 0) {
        perror(" 队列删除失败");
    } else {
        std::cout << "消息队列已销毁" << std::endl;
    }

    return 0;
}

三、编译与运行指南

1. 编译命令

bash 复制代码
# 生成发送端可执行文件
g++ sender.cpp -o sender -std=c++11

# 生成接收端可执行文件
g++ receiver.cpp -o receiver -std=c++11

2. 运行顺序

bash 复制代码
# 终端1:运行发送端(创建队列)
./sender

# 终端2:运行接收端(消费消息)
./receiver

四、代码分解与核心函数 🛠️

1. 公共头文件 common.hpp 📁

消息结构体定义
cpp 复制代码
struct message {
    long mytype;    // 🔢 消息类型(必须 > 0)
    char data[100]; // 📝 消息正文(最大长度可调整)
};
Key 生成与队列创建
cpp 复制代码
key_t key = ftok(pathname, proj_id); // 🗝️ 生成唯一键值
int msgid = msgget(key, flag);       // 🚪 创建/获取队列
  • ftok参数
    • 📂 pathname:任意存在的文件路径(本文使用/home
    • 🆔 proj_id:项目标识符(确保不同应用使用不同值)
封装函数
  • 🆕 CreateMsg() :创建新队列(IPC_CREAT | IPC_EXCL确保唯一性)
  • 🔍 Getmsg():获取已有队列(若不存在则创建)

2. 发送端代码解析 📤

cpp 复制代码
int main() {
    int msgid = CreateMsg(); // 🏗️ 创建队列
    message msg;
    msg.mytype = 1; // 🏷️ 设置消息类型
    snprintf(msg.data, sizeof(msg.data), "hello from sender\n");
    
    // ✈️ 发送消息(阻塞模式)
    msgsnd(msgid, &msg, sizeof(msg.data), 0); 
    std::cout << "Message sent: " << msg.data << std::endl;
    return 0;
}
关键点 🔑
  • 🎯 消息类型 :接收端通过mytype筛选消息
  • 🚦 发送模式
    • 🛑 0:阻塞发送(队列满时等待)
    • 🚀 IPC_NOWAIT:非阻塞发送(立即返回错误)

3. 接收端代码解析 📥

cpp 复制代码
int main() {
    int msgid = Getmsg(); // 🔍 获取队列
    message msg;
    
    // 📭 接收类型为1的消息(阻塞模式)
    msgrcv(msgid, &msg, sizeof(msg.data), 1, 0); 
    std::cout << msg.data << std::endl;
    
    msgctl(msgid, IPC_RMID, NULL); // 🗑️ 销毁队列
    return 0;
}
关键点 🔍
  • 🎯 消息过滤msgrcv的第4个参数指定接收的消息类型
    • 🎯 1:仅接收类型为1的消息
    • 🎲 0:接收队列中第一条消息
    • 🔍 -3:接收类型 ≤3 的最小消息
  • 🧹 资源释放IPC_RMID立即删除队列

五、常见问题与调试技巧 🚨

1. 系统命令 💻

bash 复制代码
ipcs -q          # 🔍 查看所有消息队列
ipcrm -q <msqid> # 🗑️ 手动删除队列
相关推荐
Irissgwe1 小时前
c++类型转换
c++·类型转换·explicit·static_cast·const_cast·dynamic_cast·rtti
智者知已应修善业2 小时前
【51单片机用T0定时器方式1,实现0.5S的时间间隔实现第一次一个灯亮、第二次二个灯亮,直到全部灯亮,然后重复整个过程】2023-12-29
c++·经验分享·笔记·算法·51单片机
一起逃去看海吧2 小时前
dify-03
java·linux·开发语言
fengyehongWorld2 小时前
Linux 根据端口进行的相关查询
linux
lihao lihao2 小时前
linux匿名管道
linux·运维·服务器
うちは止水2 小时前
weston出图调试
linux·wayland·weston
STDD2 小时前
Farming Simulator 25(模拟农场 25) Linux 专服搭建完全指南
linux·运维·javascript
社交怪人2 小时前
【范围判断】信息学奥赛一本通C语言解法(题号2052)
c语言
智者知已应修善业2 小时前
【51单片机4位静态数码管显示1234】2023-11-14
c++·经验分享·笔记·算法·51单片机