####消息队列概述
#####消息队列是消息的链接表,存放在内核中并由消息队列标识符表示。主要涉及到msgget(),msgsnd(),msgrcv(),msgctl()四个函数,在具体使用时可将四个函数再次封装进行使用。
####消息队列特点:
#####1、由于是一个链接表,所以消息队列是一个全双工的通信方式,两端都可读可写;
#####2、对于进程无关系要求(pipe要求必须有亲缘关系,FIFO无要求);
#####3、内嵌同步互斥机制;
#####4、生命周期随内核,也就是说当进程结束,所创建的消息队列还存在于内核中,可通过ipcs -q命令查看此时存在的消息队列信息;所以在程序结束时记得删除消息队列,否则要不重启之后消失,要不就在外侧通过命令删除,但是拿命令删除万一误删就又出麻烦了。。
####关于消息队列的命令:
#####1、ipcs -q 查看当前所创建的消息队列信息;
#####2、ipcrm -q +msgid 删除指定msgid的消息队列
#####3、ipcrm -a 删除当前所有的消息队列
####消息队列结构
####消息队列函数:
msgget用于创建一个新队列或打开一个现存的队列(取决于参数flag);
msgsnd将新消息添加到队列尾端,每个消息包含一个正长整型类型字段,一个非负长度以及实际数据字节,所有这些都在将消息添加到队列时,传送给msgsnd。
msgrcv用于从队列中取消息。
msgctl用于对队列执行多种操作。
####一、msgget
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
//返回值:成功返回msgid(消息队列标识符),失败返回-1
#####参数介绍:
#####①key:消息队列的名字:由ftok生成
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
#####给定一个路径,给定一个整数,生成一个对应的key,拿到对应的key就可生成对应的msgid。也就是说,当两个进程拿到相同的pathname和proj_id时,那么他们所获取到的msgid也就相同,访问到的消息队列也就是同一个。
#####②msgflg:由九个权限标志组成,用法和创建文件使用的mode模式相同。
#####注: IPC_CREAT 如果消息队列不存在,则创建一个消息队列,否则打开操作。IPC_EXCL 只有在消息队列不存在的时候,新的消息队列才建立,否则就产生错误。所以在后面的示例中创建一个文件的时候只需要使用IPC_CREAT,而打开对应消息队列时需要或上IPC_EXCL
####二、msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//返回值:成功返回0;操作失败返回-1
#####参数介绍
#####①msqid:由于我们是在对某个消息队列进行操作,那么肯定要指定是哪一个消息队列,所以首先给定的就是msgid,让内核知道我们是在对谁进行操作;
#####②cmd:说明对由msgid指定的消息队列要执行的操作:主要包含以下三种:
IPC_STAT:取此队列的msgid_ds结构,并将它存放在buf指向的结构中;
#####IPC_SET:按由buf指向结构中的值,设置此队列中相关结构对应的字段;
#####IPC_RMID:从系统中删除该消息队列以及仍在该消息队列中的所有数据。
#####③struct msqid_ds* buf:给定的msqid结构体类型的指针,用于对应cmd操作中所要修改的值。
####三、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);
//返回值:成功返回0,失败返回-1
#####参数介绍
#####①msqid:由msgget函数返回的消息队列标识码;
#####②msgp:一个指向要准备发送消息的指针;
#####③msgsz:msgp指向的消息长度;
#####④msgflg:控制参数,具体自行man
#####注:man手册中写的很清楚,上面的msgp是一个指向如下结构体类型的结构体指针,mtype可以用于指定收发消息的来源(可用枚举或宏定义将不同的人定义为不同的值,但是注意must be > 0);mtext则为我们存放消息的数组,数组的大小是可以改的,这也就是msgsz的用处。
####四、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);
//返回值:成功返回拷贝到对应mtext数组中的字符个数,失败返回-1
#####参数介绍:
#####①msqid:msgget函数返回的消息队列标识码;
#####②msgp:一个指向上msgbuf结构体类型的结构体指针;
#####③msgsz:msgp执行的消息长度;
#####④msgtype:根据msgtype的取值,进行不同的操作;
#####⑤msgflag:控制参数。
#####注:从man手册中可看出:参数type使我们可以指定想要哪一种消息。
//msgtype的取值带来的影响
msgtype=0:返回队列第一条信息
msgtype>0:返回队列第一条类型等于msgtype的消息
msgtype<0:返回队列第一条类型小于等于msgtype的绝对值的消息,并且是满足条件的消息类型最小的消息
//msgflg的取值
msgflg = IPC_NOWAIT:队列没有可读消息不等待,返回ENOMSG错误;
msgflg = MSG_NOERROR:消息大小超过msgsz时被截断
msgtype>0 且 msgflg=MSG_EXCEPT:接收类型不等于msgtype的第一条消息。
####示例:使用消息队列再次完成sercer/client之间的通信,server端在收到client端发送的消息后,做一应答;完成三次对话之后删除销毁消息队列。
/**********comm.h************/
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define PATHNAME "."
#define PROJ_ID 0x666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct msgbuf{
long mytype;
char mtext[1024];
};
int creatMsgQueue();
int getMsgQueue();
int destroyMsgQueue(int msgid);
int sendMsg(int msgid, int who, char* msg);
int recvMSg(int msgid, int recvType, char out[]);
/***********comm.c***********/
#include "comm.h"
int CommMsgQueue(int flags)
{
key_t _key = ftok(PATHNAME,PROJ_ID);
//failed return -1,
if(_key < 0){
perror("ftok");
return -1;
}
//msgget(key,msg_flag)
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 destroyMsgQueue(int msgid)
{
// int msgctl(int msqid, int cmd, struct msqid_ds *buf);
if(msgctl(msgid,IPC_RMID,NULL) < 0){
perror("msgctl");
return -1;
}
return 0;
}
int sendMsg(int msgid, int who, char* msg)
{
//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
struct msgbuf buf;
buf.mytype = who;
strcpy(buf.mtext, msg);
if(msgsnd(msgid, (void*)&buf,sizeof(buf.mtext),0) < 0){
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msgid, int recvType, char out[])
{
//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
// int msgflg);
struct msgbuf buf;
if(msgrcv(msgid, (void*)&buf, sizeof(buf.mtext), recvType, 0) < 0){
perror("msgrcv");
return -1;
}
strcpy(out, buf.mtext);
return 0;
}
/**********client.c**********/
#include "comm.h"
int main()
{
int msgid = getMsgQueue();
char buf[1024] = {0};
while(1){
printf("please enter : ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if(s > 0){
buf[s] = 0;
if(sendMsg(msgid,CLIENT_TYPE,buf) == -1){
return -1;
}
printf("send done, wait recv...\n");
}else{
printf("client close\n");
break;
}
if(recvMsg(msgid, SERVER_TYPE, buf) == -1){
return -1;
}
printf("server : %s",buf);
}
return 0;
}
/************server.c*************/
#include "comm.h"
int main()
{
int msgid = creatMsgQueue();
char buf[1024] = {0};
int count = 3;
while(count--){
if(recvMsg(msgid, CLIENT_TYPE, buf) == -1){
perror("recvMsg");
break;
}
printf("client : %s",buf);
printf("please enter : ");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf)-1);
if( s > 0){
buf[s] = 0;
sendMsg(msgid, SERVER_TYPE, buf);
printf("send done, wait recv...\n");
}
}
destroyMsgQueue(msgid);
return 0;
}
/************Makefile**************/
.PHONY : all
all : server client
server : server.c comm.c
gcc $^ -o $@
client : client.c comm.c
gcc $^ -o $@
.PHONY : clean
clean:
rm server client
#####运行结果:当server端输入三次后,主动销毁了消息队列,导致client端在第四次发送时发送失败,报错终止。