一. 消息队列的简介
消息队列是一个链表,保存在内核中,通过消息队列的引用标识符来访问。只有重启内核(重启操作系统)或显示地删除一个消息队列时,该消息队列才会被删除。
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;
}