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> # 🗑️ 手动删除队列
相关推荐
双叶8361 小时前
(C语言)写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和(递归函数)
c语言·开发语言·数据结构·算法·游戏
木子欢儿1 小时前
Debian系统清理垃圾
linux·运维·服务器·debian
翻滚吧键盘1 小时前
查看debian的版本信息
运维·chrome·debian
Blockchina1 小时前
Python自动化脚本:2分钟快速搭建MTProto代理服务(支持多端口负载均衡)
运维·自动化·代理模式
字节源流1 小时前
【SpringMVC】常用注解:@SessionAttributes
java·服务器·前端
谷晓光1 小时前
python中print函数的flush如何使用
linux·服务器·数据库
Hacker_Albert1 小时前
Linux 内核模块签名
linux
点PY1 小时前
构建rknn的docker镜像
运维·docker·容器
贫道绝缘子2 小时前
Leetcode-132.Palindrome Partitioning II [C++][Java]
java·c++·算法·leetcode
AdrichPro2 小时前
4、linux c 进程
linux·运维·服务器·c语言