一. 消息队列的简介

消息队列是一个链表,保存在内核中,通过消息队列的引用标识符来访问。只有重启内核(重启操作系统)或显示地删除一个消息队列时,该消息队列才会被删除。

1. 消息形式

struct  msgbuf
{
    long  mtype;
    char  mcontext[n];
};

参数
mtype: 消息类型
mcontext: 消息内容

内核数据结构

struct msqid_ds {
    struct           ipc_perm msg_perm;		/*所有权和权限*/
    time_t           msg_stime;		        /*上一次发送时间 */
    time_t           msg_rtime;		        /*最后接收时间*/
    time_t           msg_ctime;		        /*上次修改时间*/
    unsigned long 	 __msg_cbytes;	        /*当前输入的字节数队列*/
    msgqnum_t	     msg_qnum;	            /*当前消息数在队列中*/
    msglen_t	     msg_qbytes;		    /*最大字节数允许在队列*/
    pid_t            msg_lspid;		        /*上一次发送的PID  */
    pid_t            msg_lrpid;		        /*上一次接收的PID */
};
struct ipc_perm {
    key_t           __ key;		/*提供给键值*/
    uid_t           uid;		/*所有者的有效UID */
    gid_t           gid;		/* 所有者的有效GID */
    uid_t           cuid;		/*创建者的有效UID */
    gid_t           cgid;		/*创建者的有效GID */
    unsigned short mode;        /*权限*/
    unsigned short __seq;	    /*序列号*/
};

2. 消息队列键值

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

参数
pathname: 存在且程序范围的文件
proj_id: 整数,只有8个bit位有效,大于255,后8bit有效

返回值

成功执行返回键值,失败返回-1,返回的键值理论上随pathname和proj_id(自行指定)唯一确定,但是实际要找到pathname对应的inode,因此文件必须存在,然后获取其中的部分信息(unix下好像是设备信息和inode号的一部分),由于文件在某些修改情况下,路径名不变但inode会变(比如删去前n行,实际相当于创建一个新文件替换原文件,inode会变),从而相同路径产生不同key;又或者是不同路径其inode部分相同,生成相同的key,所以ftok生成的键不是很可靠。建议key自己指定。

3. 消息队列创建

int msgget(key_t key, int msgflg);

参数
key: 键值,由ftok()函数创建,或自定义。

  • 0(IPC_PRIVATE):会建立新的消息队列。

msflg:

  • 0:取消息队列标识符,若不存在则函数会报错
  • IPC_CREAT:当msgflg&IPC_CREAT为真时,无key消息队列,则新建;如果存在,返回消息队列的标识符
  • IPC_CREAT|IPC_EXCL:无key消息队列,则新建;如果存在,则报错

返回值

成功执行返回消息队列标识符,失败返回-1

4. 消息队列发送

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

参数
msqid: 消息队列标识符
msgp: 发送消息的内容
msgsz: 消息内容的大小,不包含消息类型所占的4个字节
msgflg:

  • 0:队列满时,msgsnd将会阻塞,直到消息能写进消息队列
  • IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回
  • IPC_NOERROR:消息大于size字节,则消息截断,截断部分被丢弃,且不通知发送进程。

返回值

成功执行返回0,失败返回-1

5. 消息队列的接收

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

参数
msqid: 消息队列标识符
msgp: 接收消息的内容
msgsz: 接收消息内容的大小,不包含消息类型所占的4个字节
msgtype:

  • 0:接收第一个消息
  • 大于0: 接收类型等于msgtyp的第一个消息
  • 小于0: 接收类型等于或者小于msgtyp绝对值的第一个消息

msgflg:

  • 0: 阻塞式接收消息,没有该类型的消息msgrcv阻塞
  • IPC_NOWAIT:如果没有返回条件的消息调用立即返回
  • IPC_EXCEPT:与msgtype配合使用返回队列中第一个类型不为msgtype的消息
  • IPC_NOERROR:如果队列中满足条件的消息内容大于所请求的size字节,则把该消息截断,截断部分将被丢弃

返回值

成功执行返回消息大小,失败返回-1

6. 消息队列的属性设置和获取

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

参数
msqid: 消息队列标识符
cmd:

  • IPC_STAT:将与msqid关联的内核数据结构中的信息复制到buf所指向的msqid_ds结构中。调用方必须对消息队列具有读权限。(获取)
  • IPC_SET:将buf所指向的msqid_ds结构的某些成员的值写入与之相关的内核数据结构消息队列。(设置)
  • IPC_RMID:删除消息队列,(NULL)

返回值

成功返回0,失败返回-1

二、通信demo

client客户端

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
struct msgbuf 
{
    long mtype;
    char context[10];
};
 
void result()
{
    key_t key;
    int msqid;
    struct msgbuf to_server = {888,"hello"};
    struct msgbuf from_server;
    key = ftok(".",127);
    if(key  == -1)
    {
        printf("create key fail\n");
        perror("why");
        exit(-1);
    }
    msqid = msgget(key,IPC_CREAT|0666);
    if(msqid == -1)
    {
        printf("create message queue fail\n");
        perror("why");
        exit(-1);
    }
    msgsnd(msqid,&to_server,sizeof(to_server.context),0);
    msgrcv(msqid,&from_server,sizeof(from_server.context),889,0);
    printf("from server type :%ld ,from server context: %s\n",from_server.mtype,from_server.context);
}
 
int main()
{
    result();
    return 0;
}

服务端 server

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
struct msgbuf 
{
    long mtype;
    char context[10];
};
 
void result()
{
    key_t key;
    int msqid;
    struct msgbuf to_client = {889,"thanks"};
    struct msgbuf from_client;
    key = ftok(".",127);
    if(key == -1)
    {
         printf("create key fail\n");
         perror("why");
         exit(-1);
    }
    msqid = msgget(key,IPC_CREAT|0666);
    if(msqid == -1)
    {
        printf("create message queue fail\n");
        perror("why");
        exit(-1);
    }
    msgrcv(msqid,&from_client,sizeof(from_client.context),888,0);
    printf("from client type :%ld ,from client context: %s\n",from_client.mtype,from_client.context);
    msgsnd(msqid,&to_client,sizeof(to_client.context),0);
}
 
int main()
{
    result();
    return 0;
}