IPC之消息队列

来源:互联网 发布:ubuntu des 解密 编辑:程序博客网 时间:2024/05/21 10:51

一、什么是消息队列
消息队列:
消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法,每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型值,我们可以通过发送消息来避免命名管道的同步和阻塞问题,所以消息队列是基于消息的,管道是基于字节流的。
消息队列和命名管道有一样的不足就是:每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

msgmax,每个消息的最大长度是有上限的。
这里写图片描述

msgmnb,每个消息队列的最大字节数也是有上限的
这里写图片描述

msgmni,每个系统上可以存在多少个消息队列也有自己的限制
这里写图片描述

二、消息队列的结构
IPC是UNIX系统中进程间通信的统称,有三种称作XSI IPC:消息队列,信号量,共享内存。每创建这三种之间的任何一种,都相当于建立了一个XSI IPC对象。系统为每个IPC对象都维护了一种结构ipc_perm
这是消息队列的结构,在(/usr/include/linux)下有一个名为msg.h的头文件里。

struct msqid_ds {  struct ipc_perm msg_perm;  struct msg *msg_first;      /* first message on queue,unused  */  struct msg *msg_last;       /* last message in queue,unused */  __kernel_time_t msg_stime;  /* last msgsnd time */  __kernel_time_t msg_rtime;  /* last msgrcv time */  __kernel_time_t msg_ctime;  /* last change time */  unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */  unsigned long  msg_lqbytes; /* ditto */  unsigned short msg_cbytes;  /* current number of bytes on queue */  unsigned short msg_qnum;    /* number of messages in queue */  unsigned short msg_qbytes;  /* max number of bytes on queue */  __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */  __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */};

ipc_prem:

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,它的类型为key_t。这是系统对IPC资源的唯一标识符。在申请IPC资源前,先要获取此标识符。我们可以通过ftok函数来获得它
三、消息队列的函数
获取消息队列:

#include<sys/msg.h>int msgget(key_t key,int flag);

成功的话返回一个消息队列的msgid,作为进程对它的唯一标识;失败返回-1
key是一个端口号也可以由函数ftok生成,标识唯一一个消息队列。
ftok函数:

key_t ftok(const char *pathname, int proj_id);  

函数ftok把一个已存在的路径名和一个整数标识(与proj_id的低序8位)转换成一个key_t值,称为IPC键
flag为创建的方式(IPC_CREAT和IPC_EXCL):
IPC_CREAT单独使用,表示申请创建一个IPC资源,若申请的资源已经存在,则直接获取;若不存在,则创建新的IPC资源。

IPC_CREAT与IPC_EXCL一起使用,表示申请创建一个IPC资源,若申请的资源不存在,则创建新的IPC资源;若已经存在,则返回-1。

IPC_EXCL单独使用没有任何意义,他的存在就是与IPC_CREAT一起使用,从而保证得到的是一个新创建的资源。

销毁消息队列:

int msgctl(int msgid,int cmd,struct msqid_ds *buf)

第一个参数msgid就是msgget的返回值。第二个参数cmd指定了对该消息队列指定执行的命令。msgctl不仅仅有删除的功能。

cmd:
IPC_STAT,获取此消息队列的msqid_ds结构,并将其存放在buf指向的结构中。

IPC_SET,将参数buf中的某些字段复制到消息队列msqid_ds 结构中对应的字段。此命令只有以下两种用户可以执行,一种是其有效用户ID等于msg_perm.cuid ,msg_perm.uid。另一种是超级用户root。

IPC_RMID,从系统中删除该消息队列以及该消息队列的所有数据。删除立即生效。
所以我们执行删除动作的话,将cmd设置成IPC_RMID即可。

发送消息:

int msgsnd(int msqid,const void* ptr,size_t nbytes,int flag)

返回值:成功返回0,失败返回-1 nbytes为传输数据的大小
ptr是指向一个结构体,结构如下:

struct msgbuf{    long mtype;    char mtext[MYSIZE];};

mtype设置为要发送数据的数据类型,mtext存放发送的数据。

flag可设置称IPC_NOWAIT,也可以不设置(设为0)。IPC_NOWAIT类似于文件I/O的非阻塞I/O标志。当消息队列已满,指定IPC_NOWAIT后,msgsnd立即出错返回EAGAIN,如果没有指定,该进程将会一直阻塞。直到消息队列有空间容纳发送的消息或者系统删除了此消息队列或者收到哦了一个信号,从信号处理程序中返回。

四、消息队列的代码
comm.h:

#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 SERVER_TYPE 1  #define CLIENT_TYPE 2  struct msgbuf  {      long mtype;      char mtext[1024];  };  int CreateMsgQueue();   int GetMsgQueue();   int DestroyMsgQueue(int msgid);   int SendMsg(int msgid,int who,char in[]);    int RecvMsg(int msgid,int recvType,char out[]);   #endif  

comm.c:

#include "comm.h"  static int commMsgQueue(int flags)  {      key_t _key=ftok(PATHNAME,PROJ_ID);         if(_key<0)      {          perror("ftok");          return -1;      }      else      {          int msgid=msgget(_key,flags);             if(msgid<0)          {              perror("msgget");              return -2;          }          else          {              return msgid;          }      }  }  int CreateMsgQueue()  {      return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);  }  int GetMsgQueue()  {      return commMsgQueue(IPC_CREAT|0666);  }  int DestroyMsgQueue(int msgid) {      if(msgctl(msgid,IPC_RMID,NULL)<0)        {          perror("msgctl:Destroy");          return -1;      }      return 0;  }  int SendMsg(int msgid,int who,char in[])    {      struct msgbuf buf;      buf.mtype=who;      strcpy(buf.mtext,in);      if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)      {          perror("msgsnd");          return -1;      }      return 0;  }  int RecvMsg(int msgid,int recvType,char out[])    {      struct msgbuf buf;      if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType,0)<0)       {          perror("msgrcv");          return -1;      }      strcpy(out,buf.mtext);      return 1;  }  

发送端代码server.c:

#include "comm.h"  int main()  {      int msgid=CreateMsgQueue();       //printf("getting success:%d\n",msgid);      char buf[1024];      while(1)      {          int ret=RecvMsg(msgid,CLIENT_TYPE,buf);            if(ret<0)          {              break;          }          printf("CLIENT_TYPE#%s\n",buf);          buf[0]=0;          printf("PLease Enter#");          fflush(stdout);          size_t s=read(0,buf,sizeof(buf));          if(s>0)          {              buf[s-1]=0;              SendMsg(msgid,SERVER_TYPE,buf);               printf("send done,wait recv...\n");          }      }      DestroyMsgQueue(msgid);      return 0;  }  

接收端代码client.c:

#include "comm.h"  int main()  {      int msgid=GetMsgQueue();        //printf("getting success:%d\n",msgid);      char buf[1024];      while(1)      {          buf[0]=0;          printf("PLease Enter#");          fflush(stdout);          size_t s=read(0,buf,sizeof(buf));          if(s>0)          {              buf[s-1]=0;              SendMsg(msgid,CLIENT_TYPE,buf);                printf("send done,wait recv...\n");          }          RecvMsg(msgid,SERVER_TYPE,buf);           printf("server#%s\n",buf);      }      return 0;  }  
原创粉丝点击