C++消息隊列(定義,結構,如何創建,發送與接收)

一、定義

1、消息隊列是一種先進先出的隊列型數據結構,實際上是系統內核中的一個內部鏈表。消息被順序插入隊列中,其中發送進程將消息添加到隊列末尾,接受進程從隊列頭讀取消息。
2、多個進程可同時向一個消息隊列發送消息,也可以同時從一個消息隊列中接收消息。發送進程把消息發送到隊列尾部,接受進程從消息隊列頭部讀取消息,消息一旦被讀出就從隊列中刪除。

二、結構

1、消息隊列中消息本身由消息類型和消息數據組成,通常使用如下結構:

struct msgbuf
{
	long 	mtype;
	char	mtext[1];
}

1)mtype指定瞭消息類型,為正整數。

引入消息類型之後,消息隊列在邏輯上由一個消息鏈表轉化為多個消息鏈表。發送進程仍然無條件把消息寫入隊列的尾部,但接收進程卻可以有選擇地讀取某個特定類型的消息中最接近隊列頭的一個,即使該消息不在隊列頭。相應消息一旦被讀取,就從隊列中刪除,其它消息維持不變。

2)成員mtext指定瞭消息的數據。我們可以定義任意的數據類型甚至包括結構來描述消息數據。

例1 :定義消息結構,它的消息數據是一個整型數據。

struct msgbuf
{
	long 	mtype;
	int 	ntext;
};

例2:定義消息結構,它的消息數據是一個字符數組和一個整型數據。

struct msgbuf
{
	long 	mtype;
	char	ctext[100];
	int 	ntext;
};

例3:定義消息結構,它的消息數據是一個結構,該結構由一個字符數組和一個整型數據組成。

struct msgtext
{
	char	ctext[200];	
    int 	ntext;
}
struct msgbuf
{
   long 	mtype;	
   struct  msgtext stext;
};

三、消息隊列的創建

1、在UNIX中,采用函數msgget創建消息隊列,原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);

函數創建一個新的消息隊列,或訪問一個已經存在的消息隊列。

1)參數key是消息隊列的關鍵字。

註:當參數key取值IPC_PRIVATE時,函數創建關鍵字為0的消息隊列。在UNIX內核中雖然要求消息隊列關鍵字唯一,但也可以創建多個關鍵字為0的消息隊列。

2)參數msgflg的低9位指定隊列的屬主、屬組和其他用戶的訪問權限,其它位指定消息隊列的創建方式。

創建方式參數:

  • IPC_CREAT:創建,如存在則打開;
  • IPC_EXCL:與IPC_CREAT使用,單獨使用無意義。創建時,如存在則失敗。

例1:創建關鍵字為0x1234,訪問權限為0666的消息隊列,如隊列已存在返回其標識號。

int msgid;
msgid = msgget(0x1234, 0666|IPC_CREAT);

例2:創建關鍵字為0x1234,訪問權限為0666的消息隊列,如隊列已存在則報錯。

int msgid;
msgid = msgget(0x1234, 0666|IPC_CREAT|IPC_EXCL);

四、消息隊列的發送與接收

類似於底層文件編程的函數read和write,函數msgsnd應用於消息隊列的發送,函數msgrcv用於消息隊列的接收。

1、在UNIX中函數msgsnd向消息隊列發送消息原型:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, void *msgp, int msgsz, int msgflg);

1)函數msgsnd向隊列消息msgid發送消息,相關參數的含義:

  • msgid:指定發送消息隊列的標識號;
  • msgp:指向存儲待發送消息內容的內存地址,用戶可設計自己的消息結構;
  • msgsz:指定長度,僅記載數據的長度,不包括消息類型部分,且必須大於0;
  • msgflg:控制消息發送的方式,有阻塞和非阻塞(IPC_NOWAIT)兩種方式。

2)導致msgsnd函數阻塞的原因:

  • 消息隊列滿:阻塞條件為msg_cbytes + msgsz > msg_qbytes;
  • (msg_cbytes:消息隊列中已使用字節數;
  • msg_qbytes:消息隊列中可以容納的最大字節數;)
  • 消息總數滿:系統中所有消息隊列記載的消息總數已達到系統上限值。

3)以阻塞方式向阻塞隊列(關鍵字為KEY)中寫入字符串“Helo UNIX!”,消息類型為TYPE。

全部過程分為5步:

第一步:定義消息結構

struct msgbuf{	
	long mtype;		
	char ctext[100];
}	

第二步:打開消息隊列

int msgid;
msgid = msgget(KEY, 0666|IPC_CREAT);
if(msgid < 0)	//打開或創建消息失敗;

第三步:組裝消息,設置消息類型和拷貝消息數據

struct msgbuf buf;
buf.mtype = 100;
strcpy(buf.ctext, “HELLO UNIX!”);

第四步:發送消息

int ret;
ret = msgsnd(msgid, &buf, strlen(buf.ctext), 0);

第五步:發送判斷

if(ret == -1)
{
	if(errno == EINTR)	//信號中斷,重新發送;
	else //系統錯誤
}

進程在發送消息過程中如果接收到信號,將中止消息發送並返回EINTR錯誤,此時重新發送即可。

2、實例:循環讀取鍵盤輸入,並將輸入的字符串寫入到消息隊列(關鍵字為0x1234)。

#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/errno.h>
#include<string.h>

extern int errno;
struct mymsgbuf{
		long mtype;
		char ctext[100];
};
int main(){
		struct mymsgbuf buf;	
		int msgid;
		if((msgid = msgget(0x1234, 0666|IPC_CREAT)) < 0)	
		{
			fprintf(stderr, "open msg %x failed.\n", 0x1234);
			return;
		}
		while(strncmp(buf.ctext, "exit", 4))	
		{
			memset(&buf, 0, sizeof(buf));
			fgets(buf.ctext, sizeof(buf.ctext), stdin);
			buf.mtype = getpid();

			while((msgsnd(msgid, &buf, strlen(buf.ctext),0)) < 0)
			{
				if(errno == EINTR)
					continue;
				return;		
			}

		}
		return 0;
}

3、在UNIX中函數msgrcv從消息隊列中接收消息原型:

#include <sys/types>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, void *msgp, int msgsz, long msgtyp, int msgflg);

1)函數msgrcv從消息隊列msgid中讀取一條消息,參數含義:

  • msgid:消息隊列標識號;
  • msgp:指向接收消息的內存緩沖區;
  • msgsz:指定該緩沖區的最大容量,不包括消息類型占用的部分;
  • msgtyp:指定讀取消息的類型;

( 0:讀取消息隊列中第一個消息;

  • 正整數:讀取消息隊列中第一個類型為msgtyp的消息;
  • 負整數:讀取消息隊列中第一個類型小於或等於msgtyp的絕對值的消息。)
  • msgflg:指定瞭消息的接收方式
  • (IPC_NOWAIT:非阻塞方式讀取信息;
  • MSG_NOERROR:截斷讀取消息。)

2)以阻塞方式從消息隊列(關鍵字為KEY)接收消息,接收消息類型為TYPE。

第一步:定義消息結構
一般要求與發送消息程序中定義結構一致
第二步:打開(創建)消息隊列

int msgid;
msgid = msgget(KEY, 0666|IPC_CREAT);

第三步:準備接收消息緩沖區

struct msgbuf buf;
memset(buf, 0, sizeof(buf));

第四步:接收消息

int ret;
ret = msgrcv(msgid, &buf, sizeof(buf.ctext), TYPE, 0);

第五步:接收判斷

if(ret == -1)
{
	if(errno == EINTR)	 //信號中斷,重新接收;
	else                 //系統錯誤
}

4、實例:以阻塞方式不斷從消息隊列(關鍵字為0x1234)中讀取消息,並打印接收到的消息類型、長度和數據等,當接收到內容為“exit”的消息時程序結束。

#include <sys/msg.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <sys/errno.h>
extern int errno;
struct mymsgbuf{
	long mtype;
	char ctext[100];
};
int main(){
	struct mymsgbuf buf;
	int msgid;	
	int ret;
	if((msgid = msgget(0x1234, 0666|IPC_CREAT)) < 0)	{
		fprintf(stderr, "open msg %X failed.\n", 0x1234);
		return;
	}
	while(strncmp(buf.ctext, "exit", 4))
	{
		memset(&buf, 0, sizeof(buf));
		while((ret = msgrcv(msgid, &buf, sizeof(buf.ctext), buf.mtype, 0)) < 0)
		{
			if(errno == EINTR)
				continue;
			return;
		}
		fprintf(stderr,"Msg:Type=%d,Len=%d,Text:%s",buf.mtype,ret, buf.ctext);
	}
	return 0;
}

綜合以上兩個實例:

五、小結

  • 1、采用消息隊列通信比采用管道通信具有更多的靈活性,通信的進程不但沒有血緣上的要求,也不需要進行同步處理。
  • 2、消息隊列是一種先進先出的隊列型數據結構;
  • 3、消息隊列將輸出的信息進行瞭打包處理,可以保證以消息為單位進行接收;
  • 4、消息隊列對信息進行分類服務,根據消息的類別進行分別處理。
  • 5、提供消息數據自動拆分功能,同時不能接受兩次發送的消息。
  • 6、消息隊列提供瞭不完全隨機讀取的服務。
  • 7、消息隊列提供瞭完全異步的讀寫服務。

到此這篇關於C++消息隊列(定義,結構,如何創建,發送與接收)的文章就介紹到這瞭,更多相關C++消息隊列內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: