IPC--消息队列
消息隊列的特點
消息隊列是生命周期是隨進程的,同時消息隊列可以實現的是消息的傳遞方向是雙向的。接受者和發送者時通過發送一個數據塊來進性消息傳遞的,每個消息的數據類型不一樣依次來進行區分我們該取哪個數據。消息隊列是基于消息的,并不是像管道一樣基于字節流,因此我可以一次放入或者取出一個數據塊,另外也不是先進先出的。但是消息隊列也有一個最大的存儲上限(MSGMAX)。
IPC對象 數據結構
我們可以理解,每個消息隊列也是一個數據存儲空間,系統 為了維護這個存儲空間,定義了一個數據結構用于維護它的各種信息
struct ipc_perm {
key_t __key; /* Key supplied to xxxget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
簡單的解釋其中的幾個數據
__key:類似于內存描述符pc一樣,__key也是一個標志某一個消息隊列的唯一標識符,唯一的身份ID。
cuid:創建用戶id
cgid:創建用戶組id
mode:消息隊列相關權限
消息隊列結構(/usr/include/linux/msg.h)
struct msqid_ds
{struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of last change */unsigned long __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t msg_qnum; /* Current number of messagesin queue */msglen_t msg_qbytes; /* Maximum number of bytesallowed in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
我們可以看到消息隊列結構中的第一個內容就是IPC數據的對象結構體,可以這樣簡單的理解,我們的所有的消息隊列用一個隊列進行維護,這個隊列中就放置了很多關于消息隊列的各個信息。
消息隊列的創建
#include<sys/types.h>
#include<msg.h>
#include<ipc.h>
int msgget(key_t key, int msgflg);
其中的key可以理解為一個端口號,我們可以這樣理解消息隊列也是一個存儲空間,把它比作是一個房間,而中國key就是房間的門,就是一個端口。接下來會介紹 一個函數ftok,由他來生成key。msgflg是操作消息隊列的方式,有兩個參數IPC_CREAT和IPC_EXCL,當我們單獨的使用IPC_CREAT如果IPC不存在創建一個新的IPC,如果已經存在則打開它;通常IPC_EXCL不單獨使用,我們是結合著IPC_CREAT一起使用,這樣做的結果是保證所得的對象是新建的,而不是已經打開的。
程序詳解
下面的這個程序,寫了一個客戶端一個服務端,然后利用消息隊列進行進程間通信
封裝函數
#include"comm.h"int commMsgQueue(int flag) //這里封裝了一個關于消息隊列的一個函數,通過傳入參數即IPC_CREAT和IPC_EXCL來完成創建消息隊列和得到消息隊列的操作,這里加上static只是想讓這個函數在本文件中有效
{key_t key = ftok(".",0); //第一個參數是路徑名,第二個參數是隨意給的一個,這里我如果使用的是"./queue"就錯誤//這里一開始寫0x6666,然后錯了if(key < 0){printf("ftok error\n");return -1;}int queue = msgget(key,flag); //key是生成的一個端口,后面的兩個宏時固有的參數,0666是訪問 的權限if(queue == -1){printf("msgget error\n");return -2;}return queue; //這里我開始返回值是0,,這個是錯誤的,應該返回的是消息隊列id,因為這個id以后會用到
}int creatMsgQueue()
{int ret = commMsgQueue(IPC_CREAT|IPC_EXCL|0666);if(ret < 0){printf("creatMsgQueue error\n");return -1;}return ret;
}int getMsgQueue()
{int ret = commMsgQueue(IPC_CREAT);if(ret < 0){printf("getMsgQueue error\n");return -1;}return ret;
}int desMsgQueue(int id)
{int flag = msgctl(id,IPC_RMID,NULL);if(flag < 0){printf("delete error\n");return -1;}return 0;
}int rcvMsg(int id,int who,char* recbuf) //我們需要得到某一個隊列中的消息,所以這里傳入隊列號,另外我們還需要傳入一個類型,這個類型是確認某一個類型呢的
{msginfo buf; int ret = msgrcv(id,&buf,sizeof(buf.mtext),who,0);strcpy(recbuf,buf.mtext);if(ret < 0){printf("rcvMsg error\n");return -1;}return 0;
}int sndMsg(int id,int who,char* sndbuf) //標記要往哪個隊列里面發,標記發送或者是接受的數據類型,who就是相當于標記數據類型
{int ret;msginfo buf;buf.mtype = who; //who是用來標記數據類型 的strcpy(buf.mtext,sndbuf); //這個時候我們應該是從外面傳入一個數據//int msqid = getMsgQueue();//這里不能這樣使用,因為我們要往一個消息隊列中發送數據的時候,應該在外面把這個隊列的id傳進來//ret = msgsnd(id,(void*)&buf,sizeof(buf.mtext),0); //雖然在這個函數中沒有使用到who,但是who傳進來是為了標記//上面的那個buf.mtype的 發數據的時候只需要標記自己的類型就好了,所以這個函數中并沒有who選項用來標記數據的//類型,但是在上面的取數據的時候卻需要who這個選項 用來識別我應該取什么樣子的數據類型if(ret < 0){printf("sndMsg error\n");return -1;}return 0;
}
server
#include"comm.h"
int main()
{int id = creatMsgQueue();char sndbuf[1024];char recbuf[1024];int i = 0; printf("%d\n",id);
// printf("creat queue success\n");while(i++ < 10){printf("請輸入:");scanf("%s",sndbuf);// sleep(1);// rcvMsg(id,CLIENT,recbuf);// printf("%s",recbuf);//sleep(1); sndMsg(id,SERVER,sndbuf);rcvMsg(id,CLIENT,recbuf);printf("client say # %s\n",recbuf);}desMsgQueue(id);return 0;
}
client
#include"comm.h"
int main()
{
// int id = creatMsgQueue();char sndbuf[1024];char recbuf[1024];int i = 0;int id = getMsgQueue();
// printf("creat queue success\n");while(i++ < 10){//printf("請輸入:");//scanf("%s",sndbuf);// sleep(1);// rcvMsg(id,CLIENT,recbuf);// printf("%s",recbuf);//sleep(1); //sndMsg(id,CLIENT,sndbuf);rcvMsg(id,SERVER,recbuf);printf("server say # %s\n",recbuf);printf("請輸入 #");scanf("%s",sndbuf);//sleep(1);sndMsg(id,CLIENT,sndbuf);}//desMsgQueue(id);return 0;
}
comm.h
#ifndef __COMM_H__
#define __COMM_H__#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>
#include<string.h>typedef struct _msginfo{long mtype;char mtext[1024];
}msginfo;#define CLIENT 1
#define SERVER 2int getMsgQueue();
int creatMsgQueue();
int commMsgQueue(int flag);
int desMsgQueue(int id);
int commMsg();
int sndMsg(int id,int who,char* sndbuf);
int rcvMsg(int id,int who,char* recbuf);#endif
總結
- 上一篇: 求一个适合小学生的个性签名!
- 下一篇: IPC--信号量