消息队列实现进程间通信

实现AB进程对话 (消息队列加多进程实现)

代码A

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>

//定义消息包类型
struct msgbuf
{
    long mtype; //消息类型,必须 > 0
    char mtext[128]; //消息内容
};


//回收僵尸进程
void handler(int sig)
{
    while (waitpid(-1,NULL,WNOHANG) > 0); //此处WNOHANG不能填0
}


int main(int argc, char const *argv[])
{
    //捕获17号信号
    __sighandler_t s = signal(SIGCHLD,handler);
    if(SIG_ERR == s)
    {
        perror("signal");
        return -1;
    }
    //
    //计算Key
    key_t key = ftok("/home/ubuntu/",1);
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }
    printf("key = %#x\n",key);

    //消息队列创建
    int msqid = msgget(key,IPC_CREAT|0664);
    if (msqid < 0)
    {
        perror("msgget");
        return -1;
    }

    printf("msqid = %d\n",msqid);

    system("ipcs -q");

    //创建两个进程,一个进程往消息队列中发送数据,一个进程从队列中写数据


    pid_t cpid = fork();

    if(cpid > 0)
    {

        //子进程从msgtype为51的进程中读取数据
        struct msgbuf rcv;

        while (1)
        {
            memset(&rcv,0,sizeof(rcv));
            rcv.mtype = 51; //消息类型
            //从消息队列中读取数据
            //阻塞的方式,读取消息队列中的第一条消息
            if(msgrcv(msqid,&rcv,sizeof(rcv.mtext),51,0) < 0)
            {
                perror("msgrcv");
                return -1;
            }

            printf("A==读取到的消息内容:%s\n",rcv.mtext);

            if (strcasecmp(rcv.mtext,"quit")==0)
            {
                //父进程推出前杀死子进程
                kill(cpid,9);
                break;
            }
        }

    } else if(0==cpid)
    {
        struct msgbuf snd;
        //父进程向msgtype为50的进程中发送数据
        while (1)
        {
            memset(&snd,0,sizeof(snd));
            snd.mtype = 50; //消息类型
            //从终端获取消息内容
            //printf("A==清输入消息内容>>>");
            fgets(snd.mtext,sizeof(snd.mtext),stdin);
            snd.mtext[strlen(snd.mtext)-1] = 0;
            //向消息队列中发送数据
            if( msgsnd(msqid,&snd,sizeof(snd.mtext),0) < 0)
            {
                perror("msgsnd");
                return -1;
            }
            if (strcasecmp(snd.mtext,"quit")==0)
            {

                break;
            }
        }
    } else {
        perror("fork");
        return -1;
    }
    return 0;
}

代码B

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>

//定义消息包类型
struct msgbuf
{
    long mtype; //消息类型,必须 > 0
    char mtext[128]; //消息内容
};

//回收僵尸进程
void handler(int sig)
{
    while (waitpid(-1,NULL,WNOHANG) > 0); //此处WNOHANG不能填0
}

int main(int argc, char const *argv[])
{
    //捕获17号信号
    __sighandler_t s = signal(SIGCHLD,handler);
    if(SIG_ERR == s)
    {
        perror("signal");
        return -1;
    }

    //计算Key
    key_t key = ftok("/home/ubuntu/",1);
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }

    printf("key = %#x\n",key);

    //消息队列创建
    int msqid = msgget(key,IPC_CREAT|0664);
    if (msqid < 0)
    {
        perror("msgget");
        return -1;
    }

    printf("msqid = %d\n",msqid);

    system("ipcs -q");

    //创建两个进程,一个进程往消息队列中发送数据,一个进程从队列中写数据
    pid_t cpid = fork();

    if(cpid > 0)
    { 

        //子进程向从msgtype为50的进程中读取数据
        struct msgbuf rcv;
        while (1)
        {
            memset(&rcv,0,sizeof(rcv));
            rcv.mtype = 50; //消息类型
            //从消息队列中读取数据
            //阻塞的方式,读取消息队列中的第一条消息
            if(msgrcv(msqid,&rcv,sizeof(rcv.mtext),50,0) < 0)
            {
                perror("msgrcv");
                return -1;
            }
            printf("B==读取到的消息内容:%s\n",rcv.mtext);
            if (strcasecmp(rcv.mtext,"quit")==0)
            {
                kill(cpid,9);
                break;
            }
        }

    } else if(0==cpid)
    {
        //父进程向msgtype为51的消息队列发送数据
        struct msgbuf snd;
        //父进程向msgtype为50的进程中发送数据
        while (1)
        {
            memset(&snd,0,sizeof(snd));
            snd.mtype = 51; //消息类型
            //从终端获取消息内容
            //printf("B==清输入消息内容>>>");
            fgets(snd.mtext,sizeof(snd.mtext),stdin);
            snd.mtext[strlen(snd.mtext)-1] = 0;
            //向消息队列中发送数据
            if( msgsnd(msqid,&snd,sizeof(snd.mtext),0) < 0)
            {
                perror("msgsnd");
                return -1;
            }

            if (strcasecmp(snd.mtext,"quit")==0)
            {

                break;
            }
        }

    } else {
        perror("fork");
        return -1;
    }
    return 0;
}

结果为

<think>好的,我需要给用户介绍一下消息队列实现进程间通信。首先,我得回想一下消息队列的基本概念,然后整理出它的工作原理和特点。接下来,应该分步骤解释它是如何工作的,比如消息的发送、接收以及队列的管理。还要提到它的优势,比如解耦、异步通信、缓冲作用等。可能还需要对比其他进程间通信方式,比如管道、共享内存,说明消息队列的不同之处。要注意用户可能对技术细节不太熟悉,所以需要用简单易懂的语言,避免太专业的术语。同时,按照系统指令,数学公式需要用正确的LaTeX格式,不过这里可能涉及的不多,但如果有涉及计算或算法的地方要正确标注。最后,确保结构清晰,分点说明,让用户能够一步步理解。可能需要检查一下是否有遗漏的重要点,比如消息队列的典型应用场景或者潜在的问题,比如消息丢失的可能性或者性能考虑。总之,要全面但简洁地覆盖消息队列进程间通信中的角色和实现机制。</think>消息队列(Message Queue)是一种实现进程间通信(IPC)的核心技术。它通过**异步通信**和**数据暂存**机制,帮助不同进程(可能位于不同主机)高效、可靠地交换数据。以下是分步解析: --- ### 一、消息队列的核心原理 1. **队列结构** 消息队列本质是一个**先进先出(FIFO)**的缓冲区,由操作系统内核维护。发送方(生产者)将数据封装为消息并存入队列,接收方(消费者)按顺序取出处理。 $$ \text{Producer} \xrightarrow{\text{send(msg)}} \text{Queue} \xrightarrow{\text{receive()}} \text{Consumer} $$ 2. **消息格式** 每条消息包含两部分: - **类型标识**:用于区分不同用途的消息(例如:$type=1$ 表示日志,$type=2$ 表示控制命令) - **数据内容**:可以是文本、二进制数据或结构化数据(如JSON) 3. **通信模式** - **点对点**:一个消息仅被一个消费者处理 - **发布/订阅**:一个消息可被多个订阅者接收 --- ### 二、实现进程间通信的关键步骤 1. **创建或连接队列** 进程通过系统调用(如`msgget()`)创建新队列或连接已有队列,需指定唯一标识符(例如:`key=0x1234`)。 2. **发送消息** ```c msgsnd(queue_id, &message, msg_size, IPC_NOWAIT); ``` - `message`需包含类型和内容 - 若队列满,可选择阻塞等待或立即返回错误 3. **接收消息** ```c msgrcv(queue_id, &buffer, max_size, target_type, 0); ``` - 可按类型精准获取消息(如$target\_type=3$) - 若队列空,进程可阻塞等待 4. **权限与销毁** - 通过`msgctl()`设置访问权限(例如:`0666`) - 显式调用删除接口释放内核资源 --- ### 三、优势与典型场景 1. **核心优势** - **解耦**:生产者和消费者无需同时在线 - **削峰填谷**:突发流量时缓冲压力,避免系统崩溃 - **可靠性**:消息持久化(如RabbitMQ)可防止数据丢失 2. **应用场景** - 微服务间异步调用(如订单支付成功后通知物流系统) - 日志收集(多个服务将日志发送到统一队列) - 任务分发(主节点通过队列分配计算任务) --- ### 四、对比其他IPC方式 | 方式 | 数据格式 | 同步性 | 跨主机支持 | |-------------|----------|----------|--------| | 管道(Pipe) | 字节流 | 同步阻塞 | 不支持 | | 共享内存(SHM) | 原始内存 | 需同步机制 | 不支持 | | 消息队列 | 结构化消息 | 异步非阻塞 | 支持 | --- ### 五、潜在问题与解决方案 1. **消息堆积** - 增加消费者数量 - 设置消息过期时间(TTL) 2. **顺序保证** - 单消费者场景天然有序 - 多消费者时需业务层处理(如版本号) 3. **安全性** - 使用TLS加密通信 - 身份验证机制(如Token) --- 通过消息队列进程间通信变得更灵活可靠,尤其适合分布式系统和高并发场景。实际应用中可选用RabbitMQ、Kafka等成熟中间件简化开发。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值