Cygwin メッセージキュー

Cygwin上でのメッセージキューの使用は色々制限がありそうで敬遠していた。しかし社内の勉強会で、非同期プログラムを扱いたくて調べてみた。本当はIPでやる方がつぶしが利くのはわかっているが、どうしてもネットワーク周りの知識が必要になる。メッセージキューはソケットに比べれば、呪文は少ないし、データ構造のキューの実践例としてはよいかも、と思ったのだ。それに、Windowsも裏ではイベントメッセージキューとメッセージポンプで動作している。それが少しでも実感できれば、と思うわけだ。

以下プリミティブなコード例

#ifndef MQ_COMMON_H__
#define MQ_COMMON_H__

#include <stdio.h>

/* perror再定義 */
#define TRAP_ERR	do { \
	char strBuf[255]; \
	sprintf(strBuf, "[%-10s][ERROR][%-10s](%05d)" \
		, __FILE__, __func__, __LINE__); \
	perror(strBuf); \
} while(0)

#define KEYFILE_PATH   "msgQueue"
#define PROJ_CHAR	   (char)'z'
#define BUFLEN		   (128)

typedef enum {
	ESTRING = 1
}EMsgId;

typedef union {
	char aText[128];
} TMsgBody;

/* メッセージ構造体 */
typedef struct msgbuf {
	long mtype;		  /* message type, must be > 0 */
	TMsgBody data;	  /* message data */
}TMsgBuf;

#endif
/* mqServer.c */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include "mqCommon.h"

int main(int argc, char *argv[])
{
	key_t  key;
	int	 idMsg;
	TMsgBuf msg = {0};
	ssize_t rcvSize;

	/* キー生成 */
	key = ftok(KEYFILE_PATH,PROJ_CHAR);
	if (key == -1) {
		TRAP_ERR;
		return 1;
	}

	/* メッセージキュー生成 */
	idMsg = msgget(key, IPC_CREAT | IPC_EXCL | 0666 ) ;
	if (idMsg == -1) {
		idMsg = msgget(key, IPC_EXCL | 0666 ) ;
		if (idMsg == -1) {
			TRAP_ERR;
			return 1;
		}
	}

	/* メッセージループ */
	for (;;) {
		/* バッファクリア */
		memset(&msg, 0, sizeof(msg));
		/* 受信 */
		rcvSize = msgrcv(idMsg, &msg, sizeof(msg.data), 0, 0);
		if (rcvSize == -1) {
			TRAP_ERR;
		} else {
			printf("get message: %s\n", msg.data.aText);
		}
		
	}

	/* close */
	msgctl(idMsg, IPC_RMID, NULL);

	return 0;
}
/* mqClient.c */
#include <string.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/msg.h>
#include "mqCommon.h"

int main(int argc, char *argv[])
{
	key_t key;
	int idMsg;
	TMsgBuf msg = {0};

	/* コマンドライン引数から、メッセージ設定 */
	msg.mtype = ESTRING;
	strncpy(msg.data.aText, argv[1], sizeof(msg.data.aText) - 1);

	/* キー生成 */
	key = ftok(KEYFILE_PATH,PROJ_CHAR);
	if (key == -1) {
		TRAP_ERR;
		return 1;
	}

	/* メッセージキュー取得 */
	idMsg = msgget(key, IPC_EXCL);
	if (idMsg == -1) {
		TRAP_ERR;
		return 1;
	}

	/* メッセージ送信 */
	if (msgsnd(idMsg, &msg, sizeof(msg.data.aText), ESTRING) == -1) {
		TRAP_ERR;
		return 1;
	}

	return 0;
}

コンパイル方法

$ gcc -Wall -o server.exe mqServer.c
$ gcc -Wall -o client.exe mqClient.c

実行方法

$ ./server &
PIDが表示される
$ ./client hello
$ get message: hello