消息队列是消息的连接表,存储在内核中。本实例主要实现消息队列方式进行进程间通信,接收端收到消息之后,立马转发给发送端;发送端发出消息之后,立马监听接收端回馈的消息,实现一个双向通信示例。
一、示例
发送端client.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <unistd.h>
#define MSG_FILE "server.c"
#define BUFFER 255
#define PERM S_IRUSR|S_IWUSR
struct msgtype {
long mtype;
char buffer[BUFFER];
};
int main(int argc,char **argv)
{
struct msgtype msg;
key_t key;
int msgid;
struct msqid_ds msqid;
if((key = ftok(MSG_FILE, 'a')) == -1) {
fprintf(stderr,"Creat Key Error:%s\a\n",strerror(errno));
exit(-1);
}
if((msgid = msgget(key, PERM | IPC_CREAT))==-1)
{
fprintf(stderr,"Creat Message Error:%s\a\n",strerror(errno));
exit(-1);
}
printf("PID=%d\n", getpid());
while(1) {
printf("Please in put the sending msg:");
if((fgets(msg.buffer, BUFFER, stdin)) == NULL){
printf("No message, terminal msg sending\n");
exit(1);
}
msg.mtype=1;
// client send.
msgsnd(msgid, &msg, sizeof(struct msgtype), 0);
memset(&msg,'\0',sizeof(struct msgtype));
sleep(1);
// client recive.
msgrcv(msgid, &msg, sizeof(struct msgtype), 2, 0);
fprintf(stderr,"Client receive:[ID=%ld] msg=%s\n", msg.mtype, msg.buffer);
msgctl(msgid, IPC_STAT, &msqid);
printf("last-msgsnd pid=%d\nlast-msgrcv pid=%d\n", msqid.msg_lspid, msqid.msg_lrpid);
}
// remove msg in system and delete all data in buffer.
msgctl(msgid, IPC_RMID, NULL);
exit(0);
}
接收端Server.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <sys/msg.h>
#define MSG_FILE "server.c"
#define BUFFSIZE 255
#define PERM S_IRUSR|S_IWUSR
struct msgtype {
long mtype;
char buffer[BUFFSIZE];
};
int main(void) {
struct msgtype msg;
key_t key;
int msgid;
if((key = ftok(MSG_FILE, 'a')) == -1) {
fprintf(stderr,"Creat Key Error %s\a\n",strerror(errno));
exit(-1);
}
if((msgid = msgget(key, PERM|IPC_CREAT)) == -1) {
fprintf(stderr,"Creat Message Error:%s\a\n",strerror(errno));
exit(-1);
}
printf("PID=%d\n", getpid());
while(1)
{
// server receive.
msgrcv(msgid, &msg, sizeof(struct msgtype), 1, 0);
fprintf(stderr,"Server Receive:[ID=%ld] %s\n", msg.mtype, msg.buffer);
msg.mtype = 2;
// server send.
printf("send:%s\n", msg.buffer);
msgsnd(msgid, &msg, sizeof(struct msgtype), 0);
}
exit(0);
}
运行结果:
接收端
PID=141798
Server Receive:[ID=1] hellosend:hello
Server Receive:[ID=1] hello
send:hello
Server Receive:[ID=1] world
send:world
发送端
PID=141800
Please in put the sending msg:hello
Client receive:[ID=2] msg=hellolast-msgsnd pid=141798
last-msgrcv pid=141800
Please in put the sending msg:world
Client receive:[ID=2] msg=worldlast-msgsnd pid=141798
last-msgrcv pid=141800
二、接口函数分析
key_t ftok(const char *path, int id);
内核中IPC结构(消息队列、信号量、共享内存)都有一个唯一的非负整数标识符,都是通过ftok函数产生。Path,必须引用一个现有的文件,当产生key时,只使用id参数的低8位。
int msgget (key_t key, int msgflg);
用于创建一个新队列或者打开一个现有队列,比如指定key为IPC_PRIVATE表示引用一个现有队列;而用ftok产生的key,表示创建一个新队列。msgflg有三个:
IPC_CREAT(如果key不存在就创建);
IPC_EXCL(如果key存在,返回失败);
IPC_NOWAIT(返回错误不等待)
int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid表示msget返回的id值;msgp指针指向发送的自定义的数据buffer;msgsz,指定发送数据buffer的长度;msgflag可以指定为IPC_NOWAIT,表示如果队列已满,立即返回EAGAIN,如果没有指定IPC_NOWAIT,则会一直阻塞到:有空间可以容纳要发送的消息,或者从系统中删除了此队列(返回EIDRM错误),或者捕捉到一个信号并从信号处理程序返回(返回EINTR错误)。
ssize_t msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msqid、msgp、msgsz、msgflag与上面msgsnd接口描述的一样,msgtyp指定了在msgflg指定的队列溢出或者下溢之后返回的消息值。msgtyp分类:
msgtyp==0,返回队列中第一个消息;
msgtyp>0,返回队列中消息类型为msgtyp的消息;
msgtyp<0,返回队列中消息类型小于msgtyp绝对值的消息,如果消息有多个,则取类型值最小的那个。
int msgctl (int msqid, int cmd, struct msqid_ds *buf);
类似于ioctl函数,第一个参数msqid是msgget的返回值,第二个cmd有如下:
IPC_SATA,获取队列的msqid_ds结构体信息,并存入buf中;
IPC_SET,讲buf中相关msqid_ds相关数据设定到队列的msqid_ds中;
IPC_RMID,从系统中删除该消息队列以及仍然在队列中的数据,删除立即生效,如果正在使用这个队列的进程当它再一次对队列进行操作时会返回EIDRM错误;