消息队列实现进程间通信

进程间通信

IPC(InterProcess Communication)

进程间通信是不同的进程通过一个或多个文件来传递信息。经常用到的通信方式有两种,一种是通过管道来实现两个进程间的通信,管道机制是创建的不同进程通过调用管道函数在内核中开辟一块空间来实现。而还有一种方式就是使用system V标准,来实现不同进程间的通信,下来就浅谈一下system V标准中的第一种通信方式——消息队列。

一、什么是消息队列

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息来避免命名管道的同步和阻塞问题。


二、消息队列的特点      


1.消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.     

2.消息队列允许一个或多个进程向它写入与读取消息.      

3.消息队列的生命周期随内核。

4.消息队列可以实现双向通信。


三、消息队列的创建,删除,和属性控制


1、创建消息队列

调用的函数msgget()。函数原型 int msgget(key_t key, int msgflg);


第一个参数key:可以认为是一个端口号,也可以由函数ftok生成。它是一个唯一标识的IPC(相当于身份证号一样),这里的key可以有ftok函数调用生成。

第二个参数msgflg:msgflg有两个标志,IPC_CREAT和IPC_EXCL。当IPC_CREAT单独使用的时候,若消息队列不存在,则创建一支;若存在打开并返回。


下来用代码来实现

key_t _key = ftok(PATHNAME,PROJ_ID);//ftok参数有两个,第一个是路径变量,第二个是projectID
    if(_key < 0){
        perror("ftok");
        return -1;;
    }
    int msgid = msgget(_key,flags);
    if(msgid < 0){
        perror("msgget");
        return -2;
    }
    return msgid;


2、删除消息队列


当一个消息队列被创建出来之后,肯定要删除它,删除的方式可以是用代码来删除,也可以使用命令来删,现在先来实现以下第一种删除方式,命令删除后面在写
删除所调用的函数是msgctl()。函数原型 int msgctl(int msqid, int cmd, struct msqid_ds *buf);



这里的msgctl不仅仅是个删除时用的函数,它是控制型函数。先来看参数,第一个参数msgid一看就是所要删除的消息队列ID,第二个参数是IPC_RMID,第三个直接设置为NULL就行!调用成功返回0,失败返回-1.


下面用代码来实现!

if(msgctl(msgid, IPC_RMID, NULL) < 0){
        perror("msgctl");
        return -1;
    }
    return 0;

3、消息队列的属性控制

既然消息队列是用来实现进程间通信的,那它必然就会有读和写功能。这里的读和写不同,system V是系统提供的第三方接口,和管道不一样,它的其中之一特点是可以实现双向通信。

读写所调用的函数是msgsnd()和msgrcv

msgsnd将数据放到消息队列中:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 

msgrcv从队列中取⽤消息:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int 

msgflg);   



  msqid:消息队列的标识码
        msgp:指向消息缓冲区的指针,此位置是来暂时存储发送和接收的消息,是一个用户可定义的通用结构

msgsz:消息的大小。
        msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。

函数如果调用成功则返回0,失败则返回-1;

代码实现如下:

int sendMsg(int msgid, int type, const char *msg)
{
    struct msgbuf _mb;
    _mb.mtype = type;
    strcpy(_mb.mtext,msg);
    if(msgsnd(msgid, &_mb,sizeof(_mb.mtext),0)<0){
        perror("msgsnd");
        return -1;
    }
    return 0;
}
int recvMsg(int msgid, int type, char *out)
{
    struct msgbuf _mb;
    if(msgrcv(msgid, &_mb, sizeof(_mb.mtext),type,0)<0){
        perror("msgrcv");
        return -1;
    }

    strcpy(out,_mb.mtext);
    return 0;
}

接下来就来整体实现一下通过消息队列两个进程间的通信,首先消息队列是调用相关函数来实现不同的功能的,所以可以用接口的封装来让消息队列直接调用。下面是接口封装函数

#ifndef _COMM_H_
#define _COMM_H_ 

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVICE_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf{
    long mtype;
    char mtext[1024];
};
int creatMsgQueue();
int getMsgQueue();
int destroyMsgQueue(int msgid);
int sendMsg(int msgid, int type, const char *msg);
int recvMsg(int msgid, int type, char *out);




#endif


#include "comm.h"
static int commMsgQueue(int flags)
{
    key_t _key = ftok(PATHNAME,PROJ_ID);//ftok参数有两个,第一个是路径变量,第二个是projectID
    if(_key < 0){
        perror("ftok");
        return -1;;
    }
    int msgid = msgget(_key,flags);
    if(msgid < 0){
        perror("msgget");
        return -2;
    }
    return msgid;
}

int creatMsgQueue()
{
    return commMsgQueue(IPC_CREAT | IPC_EXCL|0666);
}

int getMsgQueue()
{   
    return commMsgQueue(IPC_CREAT);
}

int destroyMsgQueue(int msgid)
{
    if(msgctl(msgid, IPC_RMID, NULL) < 0){
        perror("msgctl");
        return -1;
    }
    return 0;
}
int sendMsg(int msgid, int type, const char *msg)
{
    struct msgbuf _mb;
    _mb.mtype = type;
    strcpy(_mb.mtext,msg);
    if(msgsnd(msgid, &_mb,sizeof(_mb.mtext),0)<0){
        perror("msgsnd");
        return -1;
    }
    return 0;
}
int recvMsg(int msgid, int type, char *out)
{
    struct msgbuf _mb;
    if(msgrcv(msgid, &_mb, sizeof(_mb.mtext),type,0)<0){
        perror("msgrcv");
        return -1;
    }

    strcpy(out,_mb.mtext);
    return 0;
}


接下来就来实现以下不同的进程来调用它了,这point-to-point的方式来实现

#include "comm.h"
int main()
{
    int msgid = creatMsgQueue();//service创建消息队列,client不用继续创建
    char buf[1024];
    while(1){
        buf[0] = 0;
        recvMsg(msgid, CLIENT_TYPE, buf);
        printf("client say# %s\n", buf);
        printf("Please Enter# ");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s>0){
            buf[s-1] = 0;
            sendMsg(msgid, SERVICE_TYPE, buf);//往msgid里发,发SERVICE_TYPE类型的数据,发BUF
        }
    }
    destroyMsgQueue(msgid);//销毁消息队列
    return 0;
}
int main()
{
    int msgid = getMsgQueue();//service创建消息队列,client不用继续创建
    char buf[1024];
    while(1){
        buf[0] = 0;
        printf("client Enter#");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf)-1);
        if(s>0){
            buf[s-1] = 0;
            sendMsg(msgid, CLIENT_TYPE, buf);//往msgid里发,发SERVICE_TYPE类型的数据,发BUF
        }
        recvMsg(msgid, SERVICE_TYPE, buf);
        printf("server say# %s\n", buf);
    }

    return 0;
}




当创建一条消息队列之后,如果想再次使用时,会出错。因为在创建消息队列时用到的是IPC_CREAT和IPC_EXCL

,所以再次创建时会出错返回。接下来就用要使用一条指令来删除它


查看系统中的消息队列的命令:ipcs -q

删除系统中的消息队列的命令:ipcrm -q 消息队列id号







“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 1

打赏作者

Atom丶pro

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值