メッセージキュー 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