IPC--消息队列

消息队列的特点

消息队列是生命周期是随进程的,同时消息队列可以实现的是消息的传递方向是双向的。接受者和发送者时通过发送一个数据块来进性消息传递的,每个消息的数据类型不一样依次来进行区分我们该取哪个数据。消息队列是基于消息的,并不是像管道一样基于字节流,因此我可以一次放入或者取出一个数据块,另外也不是先进先出的。但是消息队列也有一个最大的存储上限(MSGMAX)。

IPC对象 数据结构

我们可以理解,每个消息队列也是一个数据存储空间,系统 为了维护这个存储空间,定义了一个数据结构用于维护它的各种信息

struct ipc_perm {
key_t __key; /* Key supplied to xxxget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};

简单的解释其中的几个数据
__key:类似于内存描述符pc一样,__key也是一个标志某一个消息队列的唯一标识符,唯一的身份ID。
cuid:创建用户id
cgid:创建用户组id
mode:消息队列相关权限

消息队列结构(/usr/include/linux/msg.h)
struct msqid_ds
{
    struct ipc_perm msg_perm;     /* Ownership and permissions */
    time_t          msg_stime;    /* Time of last msgsnd(2) */
    time_t          msg_rtime;    /* Time of last msgrcv(2) */
    time_t          msg_ctime;    /* Time of last change */
    unsigned long   __msg_cbytes; /* Current number of bytes in
                                                queue (nonstandard) */
    msgqnum_t       msg_qnum;     /* Current number of messages
                                                in queue */
    msglen_t        msg_qbytes;   /* Maximum number of bytes
                                                allowed in queue */
    pid_t           msg_lspid;    /* PID of last msgsnd(2) */
    pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};

我们可以看到消息队列结构中的第一个内容就是IPC数据的对象结构体,可以这样简单的理解,我们的所有的消息队列用一个队列进行维护,这个队列中就放置了很多关于消息队列的各个信息。

消息队列的创建
#include<sys/types.h>
#include<msg.h>
#include<ipc.h>
int msgget(key_t key, int msgflg);

其中的key可以理解为一个端口号,我们可以这样理解消息队列也是一个存储空间,把它比作是一个房间,而中国key就是房间的门,就是一个端口。接下来会介绍 一个函数ftok,由他来生成key。msgflg是操作消息队列的方式,有两个参数IPC_CREAT和IPC_EXCL,当我们单独的使用IPC_CREAT如果IPC不存在创建一个新的IPC,如果已经存在则打开它;通常IPC_EXCL不单独使用,我们是结合着IPC_CREAT一起使用,这样做的结果是保证所得的对象是新建的,而不是已经打开的。

程序详解

下面的这个程序,写了一个客户端一个服务端,然后利用消息队列进行进程间通信

封装函数

#include"comm.h"

int commMsgQueue(int flag)   //这里封装了一个关于消息队列的一个函数,通过传入参数即IPC_CREAT和IPC_EXCL来完成创建消息队列和得到消息队列的操作,这里加上static只是想让这个函数在本文件中有效
{
    key_t key = ftok(".",0);   //第一个参数是路径名,第二个参数是随意给的一个,这里我如果使用的是"./queue"就错误
    //这里一开始写0x6666,然后错了
    if(key < 0)
    {
        printf("ftok error\n");
        return -1;
    }
    int queue = msgget(key,flag);   //key是生成的一个端口,后面的两个宏时固有的参数,0666是访问 的权限
    if(queue == -1)
    {
        printf("msgget error\n");
        return -2;
    }
    return queue;   //这里我开始返回值是0,,这个是错误的,应该返回的是消息队列id,因为这个id以后会用到
}

int creatMsgQueue()
{
    int ret = commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
    if(ret < 0)
    {
        printf("creatMsgQueue error\n");
        return -1;
    }
    return ret;
}

int getMsgQueue()
{
    int ret = commMsgQueue(IPC_CREAT);
    if(ret < 0)
    {
        printf("getMsgQueue error\n");
        return -1;
    }
    return ret;
}

int desMsgQueue(int id)
{
    int flag = msgctl(id,IPC_RMID,NULL);
    if(flag < 0)
    {
        printf("delete error\n");
        return -1;
    }
    return 0;
}



int rcvMsg(int id,int who,char* recbuf)  //我们需要得到某一个队列中的消息,所以这里传入队列号,另外我们还需要传入一个类型,这个类型是确认某一个类型呢的
{
    msginfo buf;    
    int ret = msgrcv(id,&buf,sizeof(buf.mtext),who,0);
    strcpy(recbuf,buf.mtext);
    if(ret < 0)
    {
        printf("rcvMsg error\n");
        return -1;
    }
    return 0;
}


int sndMsg(int id,int who,char* sndbuf)  //标记要往哪个队列里面发,标记发送或者是接受的数据类型,who就是相当于标记数据类型
{
    int  ret;
    msginfo buf;
    buf.mtype = who;  //who是用来标记数据类型 的
    strcpy(buf.mtext,sndbuf);  //这个时候我们应该是从外面传入一个数据
    //int msqid = getMsgQueue();//这里不能这样使用,因为我们要往一个消息队列中发送数据的时候,应该在外面把这个队列的id传进来
    //
    ret = msgsnd(id,(void*)&buf,sizeof(buf.mtext),0);  //虽然在这个函数中没有使用到who,但是who传进来是为了标记
       //上面的那个buf.mtype的    发数据的时候只需要标记自己的类型就好了,所以这个函数中并没有who选项用来标记数据的
       //类型,但是在上面的取数据的时候却需要who这个选项 用来识别我应该取什么样子的数据类型
    if(ret < 0)
    {
        printf("sndMsg error\n");
        return -1;
    }
    return 0;
}

server

#include"comm.h"
int main()
{
    int id = creatMsgQueue();
    char  sndbuf[1024];
    char recbuf[1024];
    int i = 0;  
    printf("%d\n",id);
//  printf("creat queue success\n");
    while(i++ < 10)
    {
        printf("请输入:");
        scanf("%s",sndbuf);
    //  sleep(1);
    //  rcvMsg(id,CLIENT,recbuf);
    //  printf("%s",recbuf);
        //sleep(1);     
        sndMsg(id,SERVER,sndbuf);
        rcvMsg(id,CLIENT,recbuf);
        printf("client say # %s\n",recbuf);
    }
    desMsgQueue(id);
    return 0;
}

client

#include"comm.h"
int main()
{
//  int id = creatMsgQueue();
    char  sndbuf[1024];
    char recbuf[1024];
    int i = 0;
    int id = getMsgQueue(); 
//  printf("creat queue success\n");
    while(i++ < 10)
    {
        //printf("请输入:");
        //scanf("%s",sndbuf);
    //  sleep(1);
    //  rcvMsg(id,CLIENT,recbuf);
    //  printf("%s",recbuf);
        //sleep(1);     
        //sndMsg(id,CLIENT,sndbuf);
        rcvMsg(id,SERVER,recbuf);
        printf("server say # %s\n",recbuf);
        printf("请输入 #");
        scanf("%s",sndbuf);
        //sleep(1);
        sndMsg(id,CLIENT,sndbuf);
    }
    //desMsgQueue(id);
    return 0;
}

comm.h

#ifndef __COMM_H__
#define __COMM_H__

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

typedef struct _msginfo{
    long mtype;
    char mtext[1024];
}msginfo;

#define CLIENT 1
#define SERVER 2

int getMsgQueue();
int creatMsgQueue();
int commMsgQueue(int flag);
int desMsgQueue(int id);
int commMsg();
int sndMsg(int id,int who,char* sndbuf);
int rcvMsg(int id,int who,char* recbuf);


#endif
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值