メッセージキュー on cygwin

メッセージキューのサンプルコードを書いてみた。

////////////////////////////////////
// 計算メッセージヘッダ calc.h
////////////////////////////////////

#ifndef CALC_HEADER_FILE_INCLUDED__
#define CALC_HEADER_FILE_INCLUDED__

/* メッセージキュー生成用キー、マジックワード */
#define MSGQUE_PATH		"./calcQue"
#define MAGIC			'Z'

/* マルチクライアントの場合、クライアントごとに
   ユニークなメッセージタイプ送受信用を割り当てる */
#define MSG_SND_ID		((long)1)	// 送信用ID(サーバ側から見て)
#define MSG_RCV_ID		((long)2)	// 受信用ID(サーバ側から見て)

/* メッセージID */
typedef enum {
	  E_TERMINATE	// 停止
	, E_STRING		// 文字列
	, E_REQ_CALC	// 計算要求
	, E_RSP_ANSWER	// 計算結果応答
} E_ID_MSG;

/* 演算識別ID */
typedef enum {
	  E_ADD			// 加算
	, E_SUB			// 減算
	, E_MLT			// 乗算
	, E_DIV			// 除算
	, E_MOD			// 剰余算
	, E_LAST_OPE	// 最後の演算
} E_OPE;

/* 計算要求メッセージ構造体 */
typedef struct {
	E_OPE ope;		// 演算識別ID
	int op1;		// 被演算子
	int op2;		// 被演算子
} T_CALC;

/* メッセージ構造体 */
typedef struct {
	long mtype;					// メッセージタイプ
	struct {
		E_ID_MSG idMsg;			// メッセージID
		union {
								// 停止要求(メッセージ内容無し)
			char str[256];		// E_STRING
			T_CALC reqCalc;		// 計算要求
			int answer;			// 計算結果
		} u;					// 共用体
	} msg;						// メッセージ本体
} T_MSG;

#endif
// クライアント
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "calc.h"

int main(int argc, char **argv)
{
	key_t key;				// メッセージキュー生成キー
	int msgQueue;			// メッセージキューID
	T_MSG sndData = {0};	// 送信用バッファ
	T_MSG rcvData = {0};	// 受信用バッファ
	int rtn = 0;			// 戻り値

	/* キー作成 */
	key = ftok(MSGQUE_PATH, MAGIC);
	/* キュー作成 */
	msgQueue = msgget(key, IPC_CREAT | 0666);
	if (msgQueue == -1) {
		perror("msgget");
		return -1;
	}

	/* Stringメッセージ設定 */
	sndData.mtype = MSG_RCV_ID;
	sndData.msg.idMsg = E_STRING;
	strcpy(sndData.msg.u.str, "hello");
	/* Stringメッセージ送信 */
	if (msgsnd(msgQueue, &sndData, sizeof(sndData.msg), 0)) {
		perror("msgsnd");
		rtn =  -1;
		goto FINALLY;
	}
	
	/* 計算要求メッセージ設定 */
	memset(&sndData, 0, sizeof(sndData));
	sndData.mtype = MSG_RCV_ID;
	sndData.msg.idMsg = E_REQ_CALC;
	sndData.msg.u.reqCalc.ope = E_ADD;
	sndData.msg.u.reqCalc.op1 = 5;
	sndData.msg.u.reqCalc.op2 = 3;
	/* 計算要求メッセージ送信 */
	if (msgsnd(msgQueue, &sndData, sizeof(sndData.msg), 0)) {
		perror("msgsnd");
		rtn =  -1;
		goto FINALLY;
	}

	// 計算結果応答受信処理
	for (;;) {
		/* 受信メッセージバッファクリア */
		memset(&rcvData, 0, sizeof(rcvData));

		// メッセージ受信
		if (msgrcv(msgQueue, &rcvData, sizeof(rcvData.msg), MSG_SND_ID, 0) == -1) {
			perror("msgrcv: ");
			rtn =  -1;
			goto FINALLY;
		}

		// 計算結果に決め打ち
		printf("recieve answer: %d\n", rcvData.msg.u.answer);
		break;
	}
	
	/* 終了メッセージ設定 */
	memset(&sndData, 0, sizeof(sndData));
	sndData.mtype = MSG_RCV_ID;
	sndData.msg.idMsg = E_TERMINATE;
	/* 終了メッセージ送信 */
	if (msgsnd(msgQueue, &sndData, sizeof(sndData.msg), 0)) {
		perror("msgsnd");
		rtn =  -1;
		goto FINALLY;
	}

FINALLY:
	// メッセージキューはサーバ側で破壊する。

	return rtn;
}
// サーバ
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "calc.h"

// 加算関数
static int m_add(int op1, int op2)
{
	return op1 + op2;
}
// 減算関数
static int m_sub(int op1, int op2)
{
	return op1 - op2;
}
// 乗算関数
static int m_mlt(int op1, int op2)
{
	return op1 * op2;
}
// 除算関数
static int m_div(int op1, int op2)
{
	return op1 / op2;
}
// 剰余算関数
static int m_mod(int op1, int op2)
{
	return op1 % op2;
}

// 計算関数テーブル
static int (*m_aCalcTbl[])(int, int) = {
	m_add, m_sub, m_mlt, m_div, m_mod
};

// 計算要求受信処理
static void m_calcProc(int msgQueue, const T_CALC *pCalc)
{
	T_MSG sndData = {0};	// 送信用バッファ
	
	fprintf(stderr, "recieve calc req %d, %d, %d\n"
		, pCalc->ope, pCalc->op1, pCalc->op2);

	// オペレータ範囲チェック
	if ((0 <= pCalc->ope) && (pCalc->ope < E_LAST_OPE)) {
		// 送信メッセージ設定
		sndData.msg.u.answer = m_aCalcTbl[pCalc->ope](pCalc->op1, pCalc->op2);
		sndData.mtype = MSG_SND_ID;
		sndData.msg.idMsg = E_RSP_ANSWER;
		// メッセージ送信
		if (msgsnd(msgQueue, &sndData, sizeof(sndData.msg), 0)) {
			perror("msgsnd");
		}
		printf("send answer %d\n", sndData.msg.u.answer);
	}
}

// 受信処理
static int m_rcv(int msgQueue)
{
	T_MSG rcvData = {0};	// 受信用バッファ

	// メッセージ受信
	if (msgrcv(msgQueue, &rcvData, sizeof(rcvData.msg), 0, 0) == -1) {
		perror("msgrcv: ");
		// とりあえず処理継続
	}
	
	switch (rcvData.msg.idMsg) {
	case E_TERMINATE:		// 停止要求
		printf("recieve terminate request\n");
		return -1;
		break;
		
	case E_STRING:			// 文字列
		printf("recieve: %s\n", rcvData.msg.u.str);
		break;

	case E_REQ_CALC:		// 計算要求
		m_calcProc(msgQueue, &rcvData.msg.u.reqCalc);
		break;
		
	case E_RSP_ANSWER:		// 計算結果応答
		printf("recieve answer: %d\n", rcvData.msg.u.answer);
		break;
		
	default:				// 未定義メッセージ
		fprintf(stderr, "oops! undefined idMsg: %u\n", rcvData.msg.idMsg);
		break;
	}
	
	return 0;
}

int main(int argc, char **argv)
{
	key_t key;				// メッセージキュー生成キー
	int msgQueue;			// メッセージキューID
	int rtn = 0;			// 戻り値

	/* キー作成 */
	key = ftok(MSGQUE_PATH, MAGIC);
	// キュー生成
	msgQueue = msgget(key, IPC_CREAT | 0666);
	if (msgQueue == -1) {
		perror("msgget");
		return -1;
	}

	/* メッセージ受信ループ */
	for (;;) {
		if ((rtn = m_rcv(msgQueue)) != 0) {
			// エラー発生 停止処理要求
			break;
		}
	}

	/* メッセージキュー破壊 */
	msgctl(msgQueue, IPC_RMID, NULL);

	return rtn;
}

実行方法。サンプルコードではクライアントを実行すると、計算結果応答をもらったあと、サーバを停止するようになっている。

$ ./calcSrv &
[1] 7052 ←PIDがエコーされる。

$ ./calcUsr
recieve: hello
recieve calc req 0, 5, 3
send answer 8
recieve answer: 8

$ recieve terminate request

[1]+  Exit 255                ./calcSrv