消息队列功能

  • 消息队列提供了一个从一个进程向另一个进程发送一块有效数据块的方法
  • 每个数据块都被认为是有一个类型,接受者进程接受的数据块可以有不同的类型值
  • 消息队列也有管道一样的不足,就是每个消息的最大长度是有上限(MSGMAX),每个消息队列的总的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

IPC对象数据结构/usr/include/linux/ipc.h

  • 内核为每个IPC对象维护一个数据结构

【Linux】消息队列
key(唯一性)内容是什么并不关心
mode是设置的权限

消息队列结构/usr/include/linux/msg.h

【Linux】消息队列

消息队列在内核中的表示

【Linux】消息队列

消息队列相关的函数

1.ftok:创造消息队列的key

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

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

2.msgget:用来创造和访问一个消息队列

       #include <sys/msg.h>

       int msgget(key_t key, int msgflg);

参数:
key:某个消息队列的名字
msgflg:由9个权限标志组成,它们的用法和创建文件时使用的 mode(权限)模式编址是一样的

msgflg:O_CREAT与O_EXCL
1.如果同时被设置创建消息队列创建时已经有了则出错返回,如果成功创建可保证此消息队列是全新的。
2.单独O_CREAT没有创建则创建,创建了就打开(open获取)
O_CREAT与O_EXCL都是宏,源于不同的库,它们的比特位不同使用时用| 运算。
返回值:成功就返回一个非负整数,即该消息队列的标识码,失败返回-1.

3.msgctl:创建队列的控制函数

         #include <sys/msg.h>

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

参数
msqid:msget函数返回的消息队列标识码
cmd:是将要采取的动作(有三个可取值)
【Linux】消息队列
返回值:成功返回0,失败返回-1

4.msgsnd函数:把一条消息添加到消息队列

        #include <sys/msg.h>

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

参数:

msqid:由msgget函数返回的消息队列标识符
msgp:是一个指针,指针指向准备发送的消息
msqsz:是msqp指向的消息长度,这个长度不含保存消息类型的long int长整型
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事情
msgflg=IPC_NOWAIT表示队满不等待,返回EAGAIN错误
返回值:成功返回0,失败返回-1。
消息结构在两方面受到制裁

  • 首先,它必须小于系统规定的上限值
  • 其次,它必须以一个long int长整数开始,接受者函数开始利用这个长整数消息的类型
    消息结构的参考形式如下
struct msgbuf{
   long mtype;
   char mtext[1]
   }

5.msgrcv函数:从一个消息队列接受信息

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

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

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

参数
msgid:由msgget函数返回的消息队列标识符
msgp:是一个指针,指针指向准备接受的消息
msgsz:是msqp指向的消息长度,这个长度不含保存消息类型的那个long int 长整型
msgtype:它可以实现接受优先级的简单形式
msgflg:控制着队列中没有响相应类型的消息可供接受时将要发生的事
返回值:成功返回实际放到接受缓存区里面去的字符个数,成功返回-1
说明
【Linux】消息队列

实例

1.创建文件:client是用户端,server是服务端
【Linux】消息队列

2.makefile文件内容

.PHONY:all
all:client server
client:client.c comm.c
	gcc -o [email protected] $^
server:server.c comm.c
	gcc -o [email protected] $^

.PHONY:clean
clean:
	rm -rf client server

3.comm.h文件内容

#ifndef _COMM_H__
#define _COMM_H__ 


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

#define PATHNAME "."
#define PROJ_ID  0x6366 

#define SERVER_TYPE 1
#define CLIENT_TYPE 2



int CreatMsgQueue();
int GetMsgQueue();
int DestoryMsgQueue(int msgid);
int SendMsg(int msgid, int type, char *msg);
int RecvMsg(int msgid, int type , char out[]);

//消息队列结构体
struct  buf{
  long mtype;
  char mtext[1024];
};

#endif 

4.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 = msgget(_key,flags);
   if(msgid < 0)
   {
     perror("msgget");
   }
   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 SendMsg(int  msgid ,int type,char *msg)
{
  struct buf message;
  message.mtype = type;
  strcpy(message.mtext,msg);

  if(msgsnd(msgid,(void*)&message,sizeof(message.mtext),0)<0){
   perror("msgsnd");
   return -1;
  }
  return 0;
}

int RecvMsg(int msgid,int type,char out[])
{
  struct buf message;
  if(msgrcv(msgid,(void*)&message,sizeof(message.mtext),type,0)<0){
    perror("msgrve");
    return -1;
  }
  strcpy(out,message.mtext);
  return 0;
}

5.server.c文件

#include"comm.h"


int main()
{
  int  msgid = CreatMsgQueue();

  char buf[1028];
  while(1){
    buf[0]= 0;
    RecvMsg(msgid,CLIENT_TYPE,buf);
    printf("client# %s\n",buf);

    printf("Please Enter# ");
    fflush(stdout);
    ssize_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");
    }
  }



  DestoryMsgQueue(msgid);
  return 0;
}

6.client.c文件

#include"comm.h"


int main()
{
  int msgid = GetMsgQueue();
  

  char buf[1028];
  while(1){
    buf[0] = 0;
    printf("Please Enter# ");
    fflush(stdout);
    ssize_t s =read(0,buf,sizeof(buf));
    if(s>0){
      buf[s-1]= 0;
      SendMsg(msgid,CLIENT_TYPE,buf);
      printf("send done ,wait ....\n");
    }


    RecvMsg(msgid,SERVER_TYPE,buf);
    printf("send# %s\n",buf);
  }
  return 0;
}

7.先打开server端,再次打开client端,
【Linux】消息队列
【Linux】消息队列

错误报告

我们发现执行一次后,第二层执行会失败,因为已经存在一个消息队列,我们使用ipcs -q 可以查看消息队列,使用ipcrm -q id可以删除该消息队列,删除后便可再次成功执行。

相关文章: