进程间通信-----消息队列

更多linux知识:linux目录索引


1. 什么是消息队列

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。

个人理解:消息队列就是一个数据结构(也就是上面提到的数据块),好比是一个箱子,用来存消息,例如:进程一想要给进程二发送一个消息,就将消息装进箱子里面,进程二想要看消息是什么,就从这个箱子里面来取消息就可以了

2.消息队列的结构

1. 结构:
struct  msgbuf
{
    long chanell;//通道号,(也就是上面提到的数据块中包含的类型)
    char mstext[100];//用来装消息的地方
}

2.通道号:不是真实存在的,准确来说是将发送来的消息进行分类,
往相同chanell值发送的消息归为一类,例如:chanell有12345,
我们就说消息队列的通道就有5个,往1号通道发送的消息就称为类型
为1的消息;



3. 消息队列的操作

对于怎样实现两个进程间的通信,Linux提供了一系列消息队列的函数接口来让我们方便地使用它来实现进程间的通信。

3.1 创建消息队列

msgget函数

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
       //如果消息队列不存在,就创建一个,如果存在,就打开消息队列
       int msgget(key_t key, int msgflg);

参数:

key:标识消息队列,且是唯一的,key就是消息队列的关键字 ;根据key可以识别消息队列是否存在

msgflg:一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。方法: IPC_OREAT | 权限;当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。

返回值:

创建(打开)消息队列成功,返回创建/打开的消息队列标识符id,失败返回-1

3.2 key的生成

系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值(关键字key)。通常情况下,该ID值(关键字key)通过ftok函数得到

扫描二维码关注公众号,回复: 165394 查看本文章
       #include <sys/types.h>
       #include <sys/ipc.h>
        //
       key_t ftok(const char *pathname, int proj_id);
       //pathname就是你指定的文件名(路径),要求文件必须存在,一般使用当前目录,如:
    key_t key;
    key = ftok(".", 1);//这样就是将pathname设为当前目录
    //id是子序号,虽然是int类型,但是只使用8bits(1-255)
    //返回值:失败返回-1,成功返回key_t值

实例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <string.h>
//int msgget( key_t key,文件名
//              int flags,IPC_CREAT | 0644  创建文件和权限

int main ()
{
    key_t key;
    key = ftok(".",'0');//获取key
    int id = msgget( key,IPC_CREAT | 0644);//创建消息队列
    if ( id == -1)
    {
        perror( "mssget");
        exit(1);
    }

    return 0;
}

查看消息队列:ipcs -q

这里写图片描述


3.2 向消息队列发送消息

msgsnd函数

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

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

msqid:消息队列标识符,表示你想要往哪个消息队列里写 消息
msgp:临时消息队列的结构体指针,也就是临时装消息的结构体;
msgsz:要写入的消息的大小,不包括通道号,即char mtext[100]的大小
msgflg:0表示阻塞方式,设置IPC_NOWAIT 表示非阻塞方式

怎样实现将消息写入刚创建的消息队列:

  1. 打开一个已经创建好的消息队列
  2. 先创建一个临时的消息队列,将消息先写入临时的消息队列当中
  3. 再临时消息队列里的消息放入“msqid对应的消息队列”中

实例:

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

struct buf
{
    long chanell;
    char mtext[100];
};//临时存放消息的结构体

int main()
{
    int id = msgget(0x30022526, 0);//打开刚才创建的消息队列
    if(id == 0)
    {
        perror("mssget");
        exit(1);
    }
    struct buf ms;

    printf("chanell:");
    scanf("%d",&ms.chanell);
    printf("msg:");
    scanf("%s",ms.mtext);

   if( msgsnd(id,&ms,strlen(ms.mtext),0) == -1)
       perror("msgsnd");
       exit(1);

    return 0;
}

结果:

这里写图片描述


3.3 从消息队列读消息

msgrcv函数

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

      ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

参数:

msqid:消息队列标识符,表示你想要从哪个消息队列读消息
msgp:临时消息队列的结构体指针,也就是临时装消息的结构体;
msgsz:要读的消息的大小,不包括通道号,即char mtext[100]的大小
msgtyp:想接收的消息所在的通道号,通道号如果写0,表示按照顺序接收消息队列
msgflg:0表示阻塞方式,设置IPC_NOWAIT 表示非阻塞方式

怎样实现从消息队列里读出消息:

  1. 打开一个已经创建好的消息队列
  2. 先创建一个临时的消息队列,准备存放从消息队列里读取的消息
  3. 在从刚才打开的消息队列中读取想要的内容

实例:

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

struct buf
{
    long chanell;
    char mtext[100];
};//临时存放消息的结构体

int main()
{
    int id = msgget(0x30022526, 0);//打开刚才创建的消息队列
    if(id == 0)
    {
        perror("mssget");
        exit(1);
    }
    struct buf ms;
    int chanell;//从消息队列的那个通道里读
    printf("chanell:");
    scanf("%d",&chanell);

    ssize_t  rd = msgrcv( id,&ms, 100,chanell,0);
    if(rd == -1)
    {
        perror("msgrcv");
        exit(1);
    }

    printf("%s\n",ms.mtext);
   return 0;
}

结果:

这里写图片描述


3.2 删除消息队列

msgctl函数

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

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数:

msqid:消息队列标识符
cmd:包含以下三种

 1. IPC_STAT
        读取消息队列的数据结构msqid_ds,并将其存储在buf指定的地址中。
 2. IPC_SET
        设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
 3. IPC_RMID
        从系统内核中移走消息队列,即删除消息队列

buf:配合IPC_RMID使用,设为0,即buf为NULL;其他两个情况暂不讨论

实例:

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

int main ()
{
    int id = msgget(1234,IPC_CREAT | 0644);
    if(id == -1)
        perror("msgget");
        exit(1);
    int cl = msgctl(id,IPC_RMID,0);
    if(cl == -1)
        perror("msgctl");
        exit(1);
    return 0;
}

案例:客户端和服务器端进行通信

功能:

  • 客户端向服务器端发送消息,服务器端将收到的消息返回给客户端

  • 服务器端向客户端发送消息,客户端将收到的消息返回给服务器端

思路:

  • 服务器端每次从客户端的通道号里读取消息,将读取的消息都发送到自己的通道号,以此将客户端发来的消息返回给客户端

  • 客户端每次都往自己的通道号里发送消息,并且每次都从服务器端的通道号里读取消息

代码:

comm.h

#ifndef _COMM_H__ 
#define _COMM_H__ 

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

#define PATHNAME "." //ftok的路径
#define PROJ_ID 0x6666 //ftok的第二个参数

#define SERVER_TYPE 1//服务器的通道号
#define CLIENT_TYPE 2 //客户端的通道号

struct msgbuf{//临时存放消息的结构体
    long mtype;
    char mtext[1024];
};

int creatMsgQueue();//创建
int getMsgQueue();//打开
int destoryMsgQueue(int msgid);//销毁
int recvMsg(int msgid,int who,char out[]);//收消息
int sendMsg(int msgid,int who,const char* msg);//发消息

#endif //_COMM_H__

comm.c

#include"comm.h"

static int commMsgQueue(int flags)
{
    key_t _key = ftok(PATHNAME, PROJ_ID);
    if(_key < 0 ){
        perror("ftok");
        return -1;
    }
    int msgid = 0;
    if((msgid = msgget(_key, flags)) < 0){
        perror("msgget");
        return -1;
    }
    return msgid;
}

int creatMsgQueue()
{
    return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()
{
    return commMsgQueue(IPC_CREAT);
}
int destoryMsgQueue(int msgid)
{
    if( msgctl(msgid, IPC_RMID, NULL) < 0){
        perror("msgctl");
        return -1;
    }
    return 0;
}


int rcvMsg(int msgid,int who,char out[])
{
    struct msgbuf _mb;

    if(msgrcv(msgid,&_mb,sizeof(_mb.mtext),who,0) < 0){
        perror("msgrcv");
        return -1;
    }

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

server.c

#include"comm.h"

//服务器端,从客户端接受到消息,在将消息返回给客户端
#include"comm.h"

int main()
{
    int msgid = creatMsgQueue();
    char buf[1024];

    while(1){

        rcvMsg(msgid,CLIENT_TYPE,buf); 
        printf("客户:%s\n",buf);

        printf("服务器:");
        fflush(stdout);
        ssize_t s= read(0,buf,sizeof(buf)-1);
        if(s > 0){
            buf[s-1] = 0;
            sendMsg(msgid,SERVER_TYPE,buf);
            printf("发送完毕,等待回应。。。\n");
        }
    }
    destoryMsgQueue(msgid);
    return 0;
}

client.c

//客户端向客户端发送消息,服务器端返回
#include"comm.h"

int main()
{
    int msgid = getMsgQueue();
    char buf[1024];

    while(1){


        printf("客户:");
        fflush(stdout);
        ssize_t s= read(0,buf,sizeof(buf)-1);
        if(s > 0){
            buf[s-1] = 0;
            sendMsg(msgid,CLIENT_TYPE,buf);
            printf("发送完毕,等待回应。。\n");
        }
        rcvMsg(msgid,SERVER_TYPE,buf);
        printf("服务器:%s\n",buf);
    }
    return 0;
}

结果:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/zhangye3017/article/details/80081470