首頁 > 軟體

C++訊息佇列(定義,結構,如何建立,傳送與接收)

2022-08-30 18:01:33

一、定義

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++訊息佇列內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com