上一次我们看的管道是单向通信,如果你想用管道进行双向通信,可以创建两个管道。下面我们再来看一个可以双向通信的——消息队列(msgqueue)
消息队列提供了一种从一个进程向另一个进程发送一个有类型数据块的方法。
创建消息队列用的函数是:
int msgget(key_t key, int msgflg);
其中,key(消息队列标识符)可以看成端口号,管道用的是文件描述符来判断确定父子进程使用的是同一个管道,这里的key也是相同的功能,key可以用ftok()函数生成,那么我们在来看一下ftok()函数
key_t ftok(const char* pathname, int proj_id)
//成功返回消息队列标识符,失败返回-1
第二个参数:msgflag一般为:IPC_CREAT、IPC_EXCL
前一个是如果不存在,则创建,存在,则打开;后一个单独使用没有什么意义;两个一起使用的意思是:
能创建就能删除,删除用的函数是:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
这个不仅仅可以用来删除,还可以进行其他的操作,是控制消息队列的函数。其中第一个参数就是要删除的消息队列的ID,第二个参数是IPC_RMID,第三个参数不需要关心,直接设置为空就可。删除成功返回0,失败返回-1.
下面是实现消息队列的代码:
//comm.h
1 #ifndef _COMM_H_
2 #define _COMM_H_
3
4 #include<stdio.h>
5 #include<sys/types.h>
6 #include<sys/stat.h>
7 #include<sys/msg.h>
8 #include<sys/ipc.h>
9 #include<stdlib.h>
10 #include<string.h>
11
12 #define PATHNAME "."
13 #define ID 0x6666
14
15 #define SERVER_TYPE 1
16 #define CLIENT_TYPE 2
17
18 struct msgbuf
19 {
20 long mtype;
21 char mtext[1024];
22 };
23
24 int creatMsgQueue();
25 int getMsgQueue();
26 int destroyMsgQueue(int msgid);
27 int sendMsg(int msgid,int type,const char* msg);
28 int recvMsg(int msgid,int type,char* out);
29
30
31 #endif
//comm.c
1 #include"comm.h"
2
3 static int commMsgQueue(int flags)
4 {
5 key_t _key=ftok(PATHNAME,ID);//get key
6 if(_key<0)
7 {
8 perror("ftok");
9 return -1;
10 }
11 int msgid=msgget(_key,flags);
12 if(msgid<0)
13 {
14 perror("msgget");
15 return -2;
16 }
17 return msgid;
18 }
19
20 int creatMsgQueue()
21 {
22 return commMsgQueue(IPC_CREAT | IPC_EXCL | 0666);
23 }
24
25 int getMsgQueue()
26 {
27 return commMsgQueue(IPC_CREAT);
28 }
29
30 int destroyMSgQueue(int msgid)
31 {
32 if(msgctl(msgid,IPC_RMID,NULL)<0)
33 {
34 perror("msgctl");
35 return -3;
36 }
37 return 0;
38 }
39
40 int sendMsg(int msgid,int type,const char* msg)
41 {
42 struct msgbuf _mb;
43 _mb.mtype=type;
44 strcpy(_mb.mtext,msg);
45 if(msgsnd(msgid,&_mb,sizeof(_mb.mtext),0)<0)
46 {
47 perror("msgsnd");
48 return -4;
49 }
50 return 0;
51 }
52
53 int recvMsg(int msgid,int type,char *out)
54 {
55 struct msgbuf _mb;
56 if(msgrcv(msgid,&_mb,sizeof(_mb.mtext),type,0)<0)
57 {
58 perror("msgrcv");
59 return -5;
60 }
61 strcpy(out,_mb.mtext);
62 return 0;
63 }
64
1 #include"comm.h"
2
3 int main()
4 {
5 int msgid = creatMsgQueue();
6 char buf[1024];
7 while(1)
8 {
9 buf[0]=0;
10 recvMsg(msgid,CLIENT_TYPE,buf);
11 printf("client say: %s\n",buf);
12 printf("Please Enter:");
13 fflush(stdout);
14 ssize_t s=read(0,buf,sizeof(buf)-1);
15 if(s>0)
16 {
17 buf[s-1]=0;//no '\n'
18 sendMsg(msgid,SERVER_TYPE,buf);
19 }
20 }
21 // detoryMsgQueue(msgid);
22
23 return 0;
24 }
1 #include"comm.h"
2
3 int main()
4 {
5 int msgid=getMsgQueue();
6 char buf[1024];
7 while(1)
8 {
9 buf[0]=0;
10 printf("Please Enter: ");
11 fflush(stdout);
12 ssize_t s=read(0,buf,sizeof(buf)-1);
13 if(s>0)
14 {
15 buf[s-1]=0;
16 sendMsg(msgid,CLIENT_TYPE,buf);
17 }
18 recvMsg(msgid,SERVER_TYPE,buf);
19 printf("server say: %s\n",buf);
20 }
21 return 0;
22 }
消息队列的特性,除了上面我们说的,它是双向的,还有一个比较重要的特性,就是,它随内核:如果进程退出,不用指令或系统调用删除消息队列,除非关机或重启,那么消息队列一直存在。
对了,如果你在运行过程中出现问题,那么重新调试过后,会发现还是有问题,这时候,你需要将你原来的消息队列删除,再创建一个新的来。查看和删除消息队列的命令就很重要了:
iccs -q //查看消息队列
ipcrm -q //删除消息队列