所谓消息队列,字面意思就是传递消息的队列,事实上也正是如此。
消息队列提供了一种从一个进程向另一个进程发送一块具有类型的数据块的方法。那么什么是有类型的数据块呢?
先解释一下数据块,消息队列,首先它是队列,那么他可以使用顺序表或者链表两种常见的数据结构来实现。其次它是用于传递消息的队列,那么,肯定会频繁地往里边放消息,取消息,也就是说增、删操作比较频繁。很自然地,就应该想到使用链式结构来实现这个队列了。我们都知道,链式结构的每一个结点的数据结构都是一样的,那么,存放在这个队列里的东西就是一块一块的。
那么,什么是有类型的数据块呢?甲乙两个进程在通信,甲要在队列里边取乙放的消息,而乙恰恰相反,要取甲放的消息。但是他们的消息,在底层都是相同的结点,怎么区分它们呢?很简单,给每个结点做个标记不就行了。而这个标记,也就是上边提到的类型,所以此类型非彼类型,它用于标记不同进程发送的消息。
据此,可以将一个消息的结构设置成如下形式:
struct msgbuf
{
long mytype;
char mtext[1024];
};
消息队列是由操作系统替我们维护的,在同一时刻,操作系统可能会同时维护很多个消息队列,所以,为了辨别出每个消息队列,操作系统为每一个消息队列分配了一个唯一的标识id。
接下来看一看具体怎么去利用消息队列来实现进程间的通信。
创建一个消息队列---->需要往队列里边发消息----->往队列里边取消息----->通信结束后,要将消息队列销毁。
首先来介绍一下与以上环节相关的函数
msgget函数:⽤来创建和访问⼀个消息队列
原型:
int msgget(key_t key, int msgflg);
参数:key: 某个消息队列的名字
msgflg:由九个权限标志构成,它们的⽤法和创建⽂件时使⽤的mode模式标志是⼀样的
返回值:成功返回⼀个⾮负整数,即该消息队列的标识码;失败返回-1
特别说明一下第三个参数
msgsnd函数:把一条消息添加到消息队列中
原型:int msgsnd(int msgid, const void* msgp, size_t msgsz, int msgflg);
参数:msgid:使用msgget函数返回的消息队列标识码
msgp:是一个指针,指向准备发送的消息
msgsz:是msgp指向的消息的长度
msgflg = IPC_NOWAIT 表示队列满时不等待,返回EAGAIN错误
msgrcv函数
:是从⼀个消息队列接收消息
原型:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:msgid: 由msgget函数返回的消息队列标识码
msgp:是⼀个指针,指针指向准备接收的消息
msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型
msgtype:它可以实现接收优先级的简单形式
msgflg:
控制着队列中没有相应类型的消息可供接收时将要发⽣的事
返回值:成功返回实际放到接收缓冲区⾥去的字符个数,失败返回-1
msgctl函数:消息队列的控制函数
原型:int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:msqid: 由msgget函数返回的消息队列标识码
参数:msqid: 由msgget函数返回的消息队列标识码
cmd:是将要采取的动作,(有三个可取值)
IPC_STAT:把
msgid_ds结构(
消息队列的结构
)
中的数据设置为消息队列的当前关联值
IPC_SET:在进程有足够权限的前提下,把消息队列的当前关联值设置为msgid_ds数据结构中给出
的值
IPC_RMID:
删除消息队列。
返回值:成功返回0,失败返回-1
利用消息队列实现server和client之间的会话
用于包含头文件和声明相关函数的comm.h部分
#ifndef __COMM_H__ #define __COMM_H__ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> #define PATHNAME "/home/guaiguai" #define PROJ_ID 0x7777 #define SERVER_TYPE 1 #define CLIENT_TYPE 2 struct MyMsg { long mtype; char mtext[1024]; }; int CreateMsgQueue(); int GetMsgQueue(); int DestroyQueue(int msg_id); int SendMsg(int msg_id,int sendtype,char* massege); int RecvMsg(int msg_id,int recvtype,char out[]); #endif
具体函数实现comm.c部分
#include "comm.h" static int Creat(int flag) { key_t key = ftok(PATHNAME,PROJ_ID); if(key < 0) { perror("frok"); return -1; } int msg_id = msgget(key,flag); if(msg_id < 0) { perror("msgget"); return -2; } return msg_id; } int CreateMsgQueue() { return Creat(IPC_CREAT|IPC_EXCL|0777); } int GetMsgQueue() { return Creat(IPC_CREAT); } int DestroyQueue(int msg_id) { if(msgctl(msg_id,IPC_RMID,NULL) < 0) { perror("msgctl"); return -1; } return 0; } int SendMsg(int msg_id,int sendtype,char* massege) { struct MyMsg mymsg; mymsg.mtype = sendtype; strcpy(mymsg.mtext,massege); if(msgsnd(msg_id,(void*)&mymsg,sizeof(mymsg.mtext),0) < 0) { perror("msgsnd"); return -1; } return 0; } int RecvMsg(int msg_id,int recvtype,char out[]) { struct MyMsg mymsg; if(msgrcv(msg_id,(void*)&mymsg,sizeof(mymsg.mtext),recvtype,0) < 0) { perror("msgrcv"); return -1; } strcpy(out,mymsg.mtext); return 0; }
server.c部分
#include "comm.h" int main() { int msg_id = CreateMsgQueue(); char buf[1024] = {0}; while(1) { RecvMsg(msg_id,CLIENT_TYPE,buf); printf("##client said:## %s\n",buf); if(strcasecmp("quit",buf) == 0) break; printf("please enter: "); fflush(stdout); ssize_t s = read(0,buf,sizeof(buf)); if(s > 0) { buf[s-1] = 0; SendMsg(msg_id,SERVER_TYPE,buf); if(strcasecmp("quit",buf) == 0) break; printf("please waiting..\n"); } } DestroyQueue(msg_id); return 0; }
client.c部分
#include "comm.h" int main() { int msg_id = GetMsgQueue(); char buf[1024] = {0}; while(1) { printf("please enter: "); fflush(stdout); ssize_t s = read(0,buf,sizeof(buf)); if(s > 0) { buf[s-1] = 0; SendMsg(msg_id,CLIENT_TYPE,buf); if(strcasecmp("quit",buf) == 0) break; printf("please waiting..\n"); } RecvMsg(msg_id,SERVER_TYPE,buf); printf("##server said:## %s\n",buf); if(strcasecmp("quit",buf) == 0) break; } DestroyQueue(msg_id); return 0; }
以上代码思路都很简单,主要就是要对那几个跟消息队列相关函数的使用
由于要同时编译多个源文件,使用一个Makefile文件来管理
Makefile文件
.PHONY:all all:server client server:server.c comm.c gcc -o $@ $^ client:client.c comm.c gcc -o $@ $^ .PHONY:clean rm -rf server client
运行效果