IPC之消息隊列

一、什麼是消息隊列?

消息隊列提供了一種從一個進程向另一個進程發送一個數據塊的方法。  每個數據塊都被認爲含有一個類型,接收進程可以獨立地接收含有不同類型的數據結構。我們可以通過發送消息來避免命名管道的同步和阻塞問題。但是消息隊列與命名管道一樣,每個數據塊都有一個最大長度的限制。

Linux用宏MSGMAX和MSGMNB來限制一條消息的最大長度和一個隊列的最大長度。

二、在Linux中使用消息隊列

Linux提供了一系列消息隊列的函數接口來讓我們方便地使用它來實現進程間的通信。它的用法與其他兩個System V PIC機制,即信號量和共享內存相似。

1、msgget函數

該函數用來創建和訪問一個消息隊列。它的原型爲:
int msgget(key_t, key, int msgflg);
與其他的IPC機制一樣,程序必須提供一個鍵來命名某個特定的消息隊列。msgflg是一個權限標誌,表示消息隊列的訪問權限,它與文件的訪問權限一樣。msgflg可以與IPC_CREAT做或操作,表示當key所命名的消息隊列不存在時創建一個消息隊列,如果key所命名的消息隊列存在時,IPC_CREAT標誌會被忽略,而只返回一個標識符。

它返回一個以key命名的消息隊列的標識符(非零整數),失敗時返回-1.

2.msgsnd函數


該函數用來把消息添加到消息隊列中。它的原型爲:
int msgsnd(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
msgid是由msgget函數返回的消息隊列標識符。

msg_ptr是一個指向準備發送消息的指針,但是消息的數據結構卻有一定的要求,指針msg_ptr所指向的消息結構一定要是以一個長整型成員變量開始的結構體,接收函數將用這個成員來確定消息的類型。所以消息結構要定義成這樣:
struct my_message{
    long int message_type;
    /* The data you wish to transfer*/
};
msg_sz是msg_ptr指向的消息的長度,注意是消息的長度,而不是整個結構體的長度,也就是說msg_sz是不包括長整型消息類型成員變量的長度。

msgflg用於控制當前消息隊列滿或隊列消息到達系統範圍的限制時將要發生的事情。

如果調用成功,消息數據的一分副本將被放到消息隊列中,並返回0,失敗時返回-1.
3.msgrcv函數
該函數用來從一個消息隊列獲取消息,它的原型爲
int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);
msgid, msg_ptr, msg_st的作用也函數msgsnd函數的一樣。

msgtype可以實現一種簡單的接收優先級。如果msgtype爲0,就獲取隊列中的第一個消息。如果它的值大於零,將獲取具有相同消息類型的第一個信息。如果它小於零,就獲取類型等於或小於msgtype的絕對值的第一個消息。

msgflg用於控制當隊列中沒有相應類型的消息可以接收時將發生的事情。

調用成功時,該函數返回放到接收緩存區中的字節數,消息被複制到由msg_ptr指向的用戶分配的緩存區中,然後刪除消息隊列中的對應消息。失敗時返回-1.

4.msgctl函數

該函數用來控制消息隊列,它與共享內存的shmctl函數相似,它的原型爲:
int msgctl(int msgid, int command, struct msgid_ds *buf);
command是將要採取的動作,它可以取3個值,
    IPC_STAT:把msgid_ds結構中的數據設置爲消息隊列的當前關聯值,即用消息隊列的當前關聯值覆蓋msgid_ds的值。
    IPC_SET:如果進程有足夠的權限,就把消息列隊的當前關聯值設置爲msgid_ds結構中給出的值
    IPC_RMID:刪除消息隊列

buf是指向msgid_ds結構的指針,它指向消息隊列模式和訪問權限的結構。msgid_ds結構至少包括以下成員:
struct msgid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};
成功時返回0,失敗時返回-1.


三、代碼實現

公共部分,端口封裝:

comm.c

/*************************************************************************
	> File Name: comm.c
	> Author: ZX
	> Mail: [email protected] 
	> Created Time: Sun 14 May 2017 07:20:00 AM PDT
 ************************************************************************/

#include <stdio.h>
#include "comm.h"
int commMsgQueue(int flag)
{
	key_t _key = ftok(PROCPATH, PROCID);
	if(_key < 0)
	{
		perror("ftok");
		return -1;
	}
	int msgid = msgget(_key, flag);
	if(msgid < 0)
	{
		perror("msgget");
		return -2;
	}

	return msgid;
}

int creatMsgQueue()
{
	return commMsgQueue(IPC_CREAT | IPC_EXCL | 0666);
}

int getMsgQueue()
{
	return commMsgQueue(IPC_CREAT);
}

int destoryQueue(int msgid)
{
	if(msgctl(msgid, IPC_RMID, NULL) < 0)
	{
		perror("msgctl");
		return -1;
	}
	return 0;
}

int sendMsg(int msgid, int type,const char* msg)
{
	struct msgbuf buf;
	buf.mtype = type;
	strcpy(buf.mtext, msg);

	int s = msgsnd(msgid, &buf, sizeof(buf.mtext), 0);
	if(s < 0)
	{
		perror("msgsnd");
		return -1;
	}
	return 0;
}

int recvMsg(int msgid, int type, char out[])
{
	struct msgbuf buf;
	printf("begin recvMsg!\n");
	if(msgrcv(msgid, (void*)&buf, sizeof(buf.mtext), type, 0) < 0)
	{
		perror("msgrcv");
		return -1;
	}
	strcpy(out, buf.mtext);
	printf("out: %s\n", buf.mtext);
	return 0;
}

server端:

/*************************************************************************
	> File Name: server.c
	> Author: ZX
	> Mail: [email protected] 
	> Created Time: Sun 14 May 2017 07:18:59 AM PDT
 ************************************************************************/

#include <stdio.h>
#include "comm.h"

int main()
{
	int msgid  = creatMsgQueue();
	printf("createMsgQueue:%d\n", msgid);
	if(msgid < 0)
	{
		return 1;
	}
	char buf[1024];
	while(1)
	{
		buf[0] = 0;
		recvMsg(msgid, SERVER_TYPE, buf);
		printf("recv from client#:%s\n", buf);

		printf("Please Enter:\n");
		fflush(stdout);
		int s = read(0, buf, sizeof(buf)-1);
		if(s > 0)
		{
			buf[s-1] = 0;
			printf("read from stdout buf:%s\n", buf);
			if(sendMsg(msgid, CLIENT_TYPE, buf) < 0)
			{
				return -1;
			}
			printf("send done,wait recv...\n");
		}
	}
	//sleep(6);
	destoryQueue(msgid);

	return 0;
}


client端:

/*************************************************************************
	> File Name: client.c
	> Author: ZX
	> Mail: [email protected] 
	> Created Time: Sun 14 May 2017 07:19:07 AM PDT
 ************************************************************************/

#include<stdio.h>
#include "comm.h"
int main()
{
	int msgid = getMsgQueue();
	//printf("getMsgQueu:%d\n", msgid);
	if(msgid < 0)
	{
		return 1;
	}
	char buf[1024];
	while(1)
	{
		buf[0] = 0;
		printf("Please Enter#:\n");
		fflush(stdout);
		ssize_t s = read(0, buf, sizeof(buf));
		if(s > 0)
		{
			buf[s-1] = '\0';
			sendMsg(msgid, SERVER_TYPE, buf);
			printf("send done,wait recv...\n");
		}
		recvMsg(msgid, SERVER_TYPE, buf);
		printf("recv from server# %s\n", buf);
	}
	
	return 0;
}


四、運行結果




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章