进程间通信,消息队列编程和案例 mq_open mq_close mq_unlink mq_setattr mq_getattr mq_send mq_receive

一、介绍

mq_open mq_close mq_unlink mq_setattr  mq_getattr mq_send mq_receive

是 POSIX 消息队列(POSIX message queues)中用于发送和接收消息的函数。POSIX 消息队列是一种进程间通信(IPC)机制,允许进程以消息的形式交换数据。

哈哈哈哈,先了解一下函数,最后来个案例。

二、mq_open

功能:打开(如果已存在)或创建一个消息队列。

#include <mqueue.h>  
#include <fcntl.h>  
#include <sys/stat.h>  

mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
  • name:消息队列的名称,必须是以斜杠(/)开头的绝对路径名。
  • oflag:操作标志,可以是 O_NONBLOCK(非阻塞模式)和 O_CREAT(如果队列不存在则创建)的组合。
  • mode:如果 O_CREAT 被设置,则指定新创建队列的权限。
  • attr:指向 mq_attr 结构体的指针,用于指定队列的属性(如最大消息大小和队列容量)。如果为 NULL,则使用默认属性。

返回值:成功时返回消息队列描述符,失败时返回 (mqd_t)-1 并设置 errno

三、mq_send

功能:用于将一条消息发送到指定的消息队列中。

#include <mqueue.h>  

int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio);

    mqdes:消息队列描述符,由 mq_open 返回。
    msg_ptr:指向要发送的消息的指针。
    msg_len:消息的长度(以字节为单位),必须小于或等于消息队列的 mq_msgsize 属性。
    msg_prio:消息的优先级,一个无符号整数,值越大优先级越高。

如果函数成功,返回 0;如果失败,返回 -1 并设置 errno 以指示错误。

四、mq_receive

mq_receive 函数用于从指定的消息队列中接收一条消息。

#include <mqueue.h>  
  
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio);

    mqdes:消息队列描述符。
    msg_ptr:指向接收消息的缓冲区的指针。
    msg_len:缓冲区的长度(以字节为单位),应足够大以容纳可能接收到的最大消息。
    msg_prio:如果非 NULL,则用于存储接收到的消息的优先级。

如果成功,mq_receive 返回接收到的消息的实际长度(以字节为单位)。如果失败,则返回 -1 并设置 errno。

五、mq_close

功能:关闭消息队列描述符。

#include <mqueue.h>  

int mq_close(mqd_t mqdes);
  • mqdes:消息队列描述符。

返回值:成功时返回 0,失败时返回 -1 并设置 errno

六. mq_unlink

功能:删除消息队列。

#include <mqueue.h>  

int mq_unlink(const char *name);

    name:消息队列的名称。

返回值:成功时返回 0,失败时返回 -1 并设置 errno。


七、mq_setattr

功能:设置消息队列的属性。

#include <mqueue.h>  

int mq_setattr(mqd_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);

    mqdes:消息队列描述符。
    newattr:指向新的 mq_attr 结构体的指针。
    oldattr:如果非 NULL,则用于存储旧属性的副本。

返回值:成功时返回 0,失败时返回 -1 并设置 errno。


八、 mq_getattr

功能:获取消息队列的属性。

#include <mqueue.h>  

int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);

    mqdes:消息队列描述符。
    mqstat:指向 mq_attr 结构体的指针,用于存储队列的属性。

返回值:成功时返回 0,失败时返回 -1 并设置 errno。

九、消息队列案例

接收端代码

#include <iostream>
#include <fcntl.h>           // For O_* constants
#include <sys/stat.h>        // For mode constants
#include <mqueue.h>

const char* QUEUE_NAME = "/my_message_queue";
const int MAX_MSG_SIZE = 1024;

int main() {

    int rc;
    struct mq_attr mqAttr;

    printf ("Bringing up server.\n");
    rc = mq_unlink (QUEUE_NAME);
    if (rc < 0) {
        printf ("   Warning on server mq_unlink.\n");
    }

    mqAttr.mq_maxmsg = 10;
    mqAttr.mq_msgsize = 1024;

    // 创建消息队列
    mqd_t mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, S_IWUSR|S_IRUSR, &mqAttr);
    if (mq == -1) {
        perror("mq_open");
        return 1;
    }

    // 阻塞通信阶段
    std::cout << "Starting in blocking mode." << std::endl;
    char buffer[MAX_MSG_SIZE];
    ssize_t bytesRead;

    // 尝试从消息队列读取消息(阻塞)
    bytesRead = mq_receive(mq, buffer, MAX_MSG_SIZE, nullptr);
    if (bytesRead == -1) {
        perror("mq_receive in blocking mode");
        mq_close(mq);
        return 1;
    }
    std::cout << "Received in blocking mode: " << buffer << std::endl;


    // 获取当前属性
    struct mq_attr attr;
    if (mq_getattr(mq, &attr) == -1) {
        perror("mq_getattr");
        mq_close(mq);
        return 1;
    }

    // 切换到非阻塞通信阶段
    std::cout << "Switching to non-blocking mode." << std::endl;
    attr.mq_flags |= O_NONBLOCK;
    if (mq_setattr(mq, &attr, nullptr) == -1) {
        perror("mq_setattr");
        mq_close(mq);
        return 1;
    }

    // 尝试从消息队列读取消息(非阻塞)
    bytesRead = mq_receive(mq, buffer, MAX_MSG_SIZE, nullptr);
    if (bytesRead == -1) {
        if (errno == EAGAIN) {
            std::cout << "No message available in the queue in non-blocking mode." << std::endl;
        } else {
            perror("mq_receive in non-blocking mode");
        }
    } else {
        std::cout << "Received message in non-blocking mode: " << buffer << std::endl;
    }


    // 开始销毁消息队列
    std::cout << "开始销毁消息队列" << std::endl;

    // 关闭消息队列
    mq_close(mq);

    // 删除消息队列(可选)
    mq_unlink(QUEUE_NAME);

    return 0;
}

发送端代码 

#include <iostream>
#include <fcntl.h>           // For O_* constants
#include <sys/stat.h>        // For mode constants
#include <mqueue.h>
#include <string.h>

const char* QUEUE_NAME = "/my_message_queue";
const int MAX_MSG_SIZE = 1024;

int main() {
    // 创建消息队列
    mqd_t mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0666, nullptr);
    if (mq == -1) {
        perror("mq_open");
        return 1;
    }

    // 阻塞通信阶段
    std::cout << "Starting in blocking mode." << std::endl;
    char buffer[MAX_MSG_SIZE];
    ssize_t bytesRead;


    // 发送消息到消息队列(阻塞)
    const char* messageToSend = "Hello from sender in blocking mode!";
    if (mq_send(mq, messageToSend, strlen(messageToSend), 0) == -1) {
        perror("mq_send in blocking mode");
        mq_close(mq);
        return 1;
    }

    // 获取当前属性
    struct mq_attr attr;
    if (mq_getattr(mq, &attr) == -1) {
        perror("mq_getattr");
        mq_close(mq);
        return 1;
    }

    // 切换到非阻塞通信阶段
    std::cout << "Switching to non-blocking mode." << std::endl;
    attr.mq_flags |= O_NONBLOCK;
    if (mq_setattr(mq, &attr, nullptr) == -1) {
        perror("mq_setattr");
        mq_close(mq);
        return 1;
    }


    // 发送消息到消息队列(非阻塞)
    const char* anotherMessageToSend = "Hello again in non-blocking mode!";
    if (mq_send(mq, anotherMessageToSend, strlen(anotherMessageToSend), 0) == -1) {
        if (errno == EAGAIN) {
            std::cout << "Queue is full. Cannot send message in non-blocking mode." << std::endl;
        } else {
            perror("mq_send in non-blocking mode");
        }
    }

    // 开始销毁消息队列
    std::cout << "开始销毁消息队列" << std::endl;

    // 关闭消息队列
    mq_close(mq);

    // 删除消息队列(可选)
    mq_unlink(QUEUE_NAME);

    return 0;
}

要运行这段代码实现进程间通信,可以按照以下步骤进行:

  1. 编译代码:
    使用 C++ 编译器(如 g++)编译代码。
g++ mqsend.cpp -o mqsend
g++ mqrecv.cpp -o mqrecv

     2. 运行程序:
         打开两个终端窗口,分别代表两个不同的进程。
          在一个终端中运行编译后的程序:

    ./mqrecv

     3. 在另一个终端中,稍等片刻后再次运行编译后的程序mqsend。这样两个进程就会尝试通过消息队列进行通信。 

    ./mqsend

十、Message too long 和 Invalid argument错误处理

 10.1 Message too long

mq_recv出现如下错误 Message too long

怎么解决的呢,大家对比一下前面 发送端案例 和接收端案例 时的 mq_open 。出现错误时, 发送端案例 和接收端案例 mq_open 设置都是如下。

int main() {
    // 创建消息队列
    mqd_t mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0666, nullptr);

 接收端案例 mq_open设置修改为如下后,就可以跑了。据说是mq_maxmsg和mq_msgsize的设置导致的。

int main() {

    in rc;
    struct mq_attr mqAttr;

    printf ("Bringing up server.\n");
    rc = mq_unlink (QUEUE_NAME);
    if (rc < 0) {
        printf ("   Warning on server mq_unlink.\n");
    }

    mqAttr.mq_maxmsg = 10;
    mqAttr.mq_msgsize = 1024;

    // 创建消息队列
    mqd_t mq = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, S_IWUSR|S_IRUSR, &mqAttr);

10.2 Invalid argument 

当我把mqAttr.mq_maxmsg = 10;改为mqAttr.mq_maxmsg = 100;就报下面的错

改回去就好。

mq_maxmsg限定消息队列中的最大消息数,mq_msgsize限定每个消息的最大字节数。

10.3 数据不全和数据乱码

消息队列数据传输时,出现了数据不全或数据乱码的情况。

将代码中的sizeof修改为strlen就解决了。sizeof和strlen的区别参考这

获取char*字符串指针指向的数组长度时,记得用strlen,而不是sizeof-CSDN博客

 十一、结果

当然在一个终端上可以收到另一个终端的消息啦

queue - mq_receive:消息太长 - Stack Overflow

  • 27
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值