为什么会有消息队列?
首先消息队列也是两个进程间进行数据传输的一个方式,除了这个作用外,还有一点就是当不同进程间传送消息时,由于进程进耦合度较高(一个进程的改变可能会影响到另一个进程)为了隔离两个进程,在两个进程之间抽出一块空间(消息队列),两个进程间传递的所有消息都必须通过消息队列。
理解消息队列
1、基本概念
消息队列就是一个存放消息的链表,对消息队列有写权限的进程就可以向消息队列中添加消息,对消息队列有读权限的进程就可以读走消息,写到了上限就不能写了,除非有人读走一些消息,读完了就没法再读,除非有人往里面写消息。
IPC对象数据结构:
消息队列结构:
这就是一整个消息队列:
2、消息队列的优缺点
1、可以实现异步通信
2、消息队列有大小限制。
3、实现简单,可移植性好。
用消息队列模拟实现客户端-服务器通信
服务器:
1、 服务器一定要先启动,在server.c中先将消息队列创建好,实时的接受客户端发来的消息(接受请求)
2、对客户端发送来的消息进行处理(此处是简易版,省略此步骤)
3、将结果返回给客户端,只有客户端退出服务器端才退出。(请求响应)
客户端:
1、从标准输入中获取数据
2、将数据发送到服务器端(发送请求)
3、接收服务器的响应。
写代码之前要介绍几个函数:
1、 msgget函数:用来创建和访问一个消息队列。
函数成功返回一个非负整数 即消息队列的标识码,失败返回-1;>
int msgget(key_t key,int msgflag);
//key是消息队列的名字,msgflag是由九个权限标志构成,
//用来指定按什么行为创建消息队列,指定消息队列的权限。
2、msgctl函数:消息队列的控制函数
成功返回0,失败返回-1
int msgctl(int msgid,int cmd,struct msgid_ds* buf);
//msgid是有msgget函数返回来的消息队列标识码。
//cmd是将要采取的动作(三个可能值)分别为:
//IPC_STAT:把msgid_ds结构体中的数据设置为消息队列的当前关联值
//IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msgid_ds数据结构中给出的值。
//IPC_RMID:删除消息队列。
msgsnd函数:把一条消息添加到消息队列中
成功返回0,失败返回-1;
int msgsnd(int msgid,const void* msgp,size_t msgsz,int msgflg);
//参数:msgid:消息队列标识码
//msgp:是一个指针,指向准备发送的消息。
//msgsz:msgp指向的消息长度(计算的长度不包含long int的保存消息类型的成员)
//msgflg:控制当前队列满或者到达系统上限即将发生的事情。为IPC_NOWAIT表示队列满不等待,返回EAGAIN错误。
comm.h文件:
2 #include<stdlib.h>
3 #include<sys/msg.h>
4 #include<sys/ipc.h>
5 #include<sys/types.h>
6
7 #define PATHNAME "."//当前路径
8 #define PROJ_ID 0x6666//用于创建消息队列,构造ipc中的key
9 #define request 1
10 #define respond 2//用于说明发送消息队列和接收消息队列时是以请求还是响应的状态。
11
12 struct msgbuf{
13 long mtype;
14 char mtext[1024];
15 };//消息结构
16
17 int createMsgQueue();
18 int openMsgQueue();
19 int destroyMsgQueue(int msgid);
20 int sendMsgQueue(int msgid,int sendtype,char* msg,size_t size);
21 int recvMsgQueue(int msgid,int recvtype,char output[]);
comm.c文件:
2 #include<stdio.h>
3 #include<sys/ipc.h>
4 #include<sys/msg.h>
5 #include<sys/types.h>
6 #include<string.h>
7 static int commMsgQueue(int flags){
8 key_t key=ftok(PATHNAME,PROJ_ID);
9 if(key<0){
10 perror("ftok\n");
11 return -1;
12 }
13 int msgid=msgget(key,IPC_CREAT|IPC_EXCL);
14 if(msgid<0){
15 perror("msgget\n");
16 return -1;
17 }
18 return msgid;
19 }
20 int createMsgQueue(){
21 return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
22 }
23 int openMsgQueue(){
24 return commMsgQueue(IPC_CREAT);
25 }
26 int destroyMsgQueue(int msgid){
27 if(msgctl(msgid,IPC_RMID,NULL)<0){
28 perror("msgctl");
29 return -1;
30 }
31 return 0;
32 }
33 int sendMsgQueue(int msgid,int sendtype,char* msg,size_t size){
34 struct msgbuf buf;
35 buf.mtype=sendtype;
36 if(size>=sizeof(buf.mtext)/sizeof(buf.mtext[0])){
37 printf("msg is too long\n");
38 return -1;
39 }
40 strcpy(buf.mtext,msg);
41 if(msgsnd(msgid,&buf,sizeof(buf.mtext)/sizeof(buf.mtext[0]),0)<0){
42 perror("msgsnd\n");
43 }
44 return 0;
45 }
46 int recvMsgQueue(int msgid,int recvtype,char output[]){
47 struct msgbuf buf;
48 buf.mtype=recvtype;
49 if(msgrcv(msgid,&buf,sizeof(buf.mtext),recvtype,0)<0){
50 perror("msgrcv\n");
51 return -1;
52 }
53 strcpy(output,buf.mtext);
54 return 0;
55 }
此处着重讲一下发送消息队列函数和接收消息队列函数
client.c文件:
1 #include"comm.h"
2 #include<string.h>
3 int main(){
4 int msgid=openMsgQueue();
5 char buf[1024]={0};
6 while(1){
7 printf("please enter:");
8 fflush(stdout);
9 ssize_t read_size= read(0,buf,sizeof(buf)-1);
10 if(read_size<0){
11 perror("read\n");
12 return -1;
13 }else if(read_size>0){
14 sendMsgQueue(msgid,request,buf,strlen(buf));
15 printf("wait receive...\n");
16 }
17 else{
18 printf("exit\n");
19 }
20 recvMsgQueue(msgid,respond,buf);
21 printf("server say:%s\n",buf);
22 }
23 return 0;
24 }
server.c文件:
1 #include"comm.h"
2 int main(){
3 int msgid=createMsgQueue;
4 char output[1024]={0};
5 while(1){
6 recvMsgQueue(msgid,request,output);
7 printf("client say:%s\n",output);
8 printf("================\n");
9 fflush(stdout);
10 ssize_t s=read(0,output,sizeof(output));
11 if(s>0){
12 sendMsgQueue(msgid,respond,output,strlen(output));
13 printf("response has been made\n");
14 }
15 }
16 destroyMsgQueue(msgid);
17 return 0;
18 }