Message Queue C on Unix/Linux

2011-02-13 / C/C++, MAC, Programming, Server, Unix/Linux / 51 Comments

วันนี้อยู่บ้านวันอาทิตย์ว่าง ๆ ไม่ได้ทำอะไร ก็เลยมานั่งหาอะไรอ่าน เอาความรู้ใส่สมอง ซักหน่อย
ก็เลยมาอ่านเกี่ยวกับเรื่อง Message Queue และก็ลองเขียนเล่นๆ ดู ก็ผมจะสรุปตามความเข้าใจของผม และย่อตัวอย่าง Code ที่ใช้งานดูนะครับ

Message Q คืออะไร และใช้ทำอะไร ?

ก่อนที่จะมาทำความรู้จักกับ Message Queue เรามาทำความรู้จักกับ IPC กันก่อน IPC หรือ Interprocess Communication มีไว้เพื่อการติดต่อสื่อสาร จะใช้ในการติดต่อสื่อสารระหว่าง Thread ภายใน Process เดียวกันก็ได้ หรือ จะใช้ในการติดต่อสารระหว่าง Process ก็ได้
โดยการสื่อสารของ IPC มีหลายชนิด และแต่ละชนิดมีข้อดีข้อเสียแตกต่างกันไปครับ

  • Pipes
  • Signal
  • Message Queue
  • Shared Memory
  • Socket
  • Semaphores

Page จะพูดเรื่อง Message Queue ก่อนนะครับ แล้วผมจะค่อย ๆ เพิ่มในส่วนอื่นให้ภายหลัง Message Queue สามารถติดต่อสื่อสารกันภายใน OS เดียวกันได้เท่านั้น คือ ตัว Message Queue ไม่สามารถสื่อสารข้ามเครื่องได้ ต้องอยู่ภายใต้เครื่องเดียวกันเท่านั้น

โดยการทำงานคร่าวๆ ของ Message Queue นั้นแน่นอนว่าจะมีในส่วนของการส่งและการรับ และที่เพิ่มเข้ามาจะเป็นในส่วนของ Passing Module ซึ่งทำหน้าที่เป็นตัวกลางในการจัดการ รับ-ส่ง Message ให้กับทั้ง 2 Process และ ใน Process ทั้งสองจะต้องกำหนดค่า Key ขึ้น ให้ตรงกัน เช่น Process ที่ส่ง ส่งด้วย Key 1234 ใน Process ในการรับก็ต้องรับ ด้วย Key 1234 ด้วย

ในใช้งาน Function หรือ type ต่าง ๆ ของ Message Queue จะต้อง include header file 3 file ดังนี้

  • #include <sys/types.h>
  • #include <sys/ipc.h>
  • #include <sys/msg.h>

function หลักๆ ของ Message ที่ใช้มีดังนี้ msgget(), msgsnd(), msgrcv()

  • int msgget(key_t key, int msgflg)
  • int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  • int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

1. msgget ใช้ในการสร้างช่องทางในการรับ-ส่ง

  • return ค่าของ Messsage Queue ID มาให้หาก function นี้ทำงานสำเร็จ และ return -1 หาก error
  • key คือเลขคือ ซื่งตัวแปล key จะมีขนาด เทียบเท่า unsigned long
  • msgflg คือ parameter ที่ใช้ตั้งค่า permission และ control flag ของ message queue

2. msgsnd ใช้ในการส่ง

  • return หาก function นี้ทำงานไม่สำเร็จจะ return -1
  • msqid คือ message queue id ที่ได้จากการ call function msgget
  • msgp คือ structure ของ message ที่ จะส่งออกไป
  • msgsz คือ ขนาดของ structure ของ message ที่จะส่งออก
  • msgflg คือ flag เพื่อบอกว่า call function นี้แล้วให้ทำอะไร ส่วนใหญ่ที่ผมใช้คือ IPC_NOWAIT

3.msgrcv ใช้ในการรับ

  • return หาก function นี้ทำงานสำเร็จจะ return ขนาดของ Message ที่รับเข้ามา แต่หากไม่สำเร็จจะ return -1
  • msqid คือ Qessage Queue id ที่ได้จากการ call function msgget
  • msgp คือ structure ของ message ที่ จะรับเข้ามา
  • msgsz คือ ขนาดของ structure ของ message ที่จะรับเข้ามา
  • msgtyp คือ จะรับ Message type อะไร
  • msgflg คือ flag เพื่อบอกว่า call function นี้แล้วให้ทำอะไร

Code Example

โดยตัวอย่างผมจะแบ่งออกเป็น 2 Process (2 โปรแกรม)

  1. โปรแกรมส่ง Send Process
  2. โปรแกรมรับ Receive Process

1.msgq_send.c

/*************************
*@author Ultra MCU
*@file-name msgq_send.c
*@date 2011 02 13
*compile apple-darwin10-gcc-4.2.1 on OSX 10.6.4
*@e-mail ultra_mcu[AT]msn>com
*@website http://www.im-ai.com
*************************/

#include
#include
#include
#include

#define _TRUE_ 0
#define _FALSE_ -1

typedef struct struct_message_text
{
long message_type;
char message_text[100];

}st_msg_txt;

int main (int argc, const char * argv[]) {

printf(“Hello, I’m send process.\r\n”);
send_process();
return 0;
}

int send_process()
{
st_msg_txt my_send_msg;
key_t msg_key;
int msg_flag;
int msg_q_id;
int msg_number;

msg_number = 0;
msg_key = 822; /* Message Queue Key 822 */
msg_flag = IPC_CREAT | 0666; /* IPC Create + Permission rw_rw_rw_ */
my_send_msg.message_type = 1; /* Message type 1 */
msg_q_id = msgget(msg_key, msg_flag);

if(msg_q_id > -1)
{
while(1)
{
sprintf(my_send_msg.message_text,”Message msg_number = %d”,msg_number);

if(msgsnd(msg_q_id, &my_send_msg, sizeof(st_msg_txt), IPC_NOWAIT) == _TRUE_)
{
printf(“SEND PROCESS : %s\r\n”,my_send_msg.message_text);
}
else
{
perror(“msgsnd”);
printf(“send message failed\r\n”);
return _FALSE_;
}

msg_number++;
sleep(1);
}
}
else
{
printf(“get message queue id failed\r\n”);
return _FALSE_;
}

return _TRUE_;

}

2.msgq_recv.c

/*************************
*@author Ultra MCU
*@file-name msgq_recv.c
*@date 2011 02 13
*compile apple-darwin10-gcc-4.2.1 on OSX 10.6.4
*@e-mail ultra_mcu[AT]msn>com
*@website http://www.im-ai.com
*************************/

#include
#include
#include
#include

#define _TRUE_ 0
#define _FALSE_ -1

typedef struct struct_message_text
{
long message_type;
char message_text[100];

}st_msg_txt;

int main (int argc, const char * argv[]) {

printf(“Hello, I’m recv process.\r\n”);
recv_process();
return 0;
}
int recv_process()
{
st_msg_txt my_recv_msg;
key_t msg_key;
int msg_flag;
int msg_q_id;
int recv_msg_type;

msg_key = 822; /* Message Queue Key 822 */
msg_flag = IPC_CREAT | 0666; /* IPC Create + Permission rw_rw_rw_ */
recv_msg_type = 1; /* Receive Message type 1 */

msg_q_id = msgget(msg_key, msg_flag);

if(msg_q_id > -1)
{
while(1)
{
if(msgrcv(msg_q_id, &my_recv_msg, sizeof(st_msg_txt),recv_msg_type , 0) < 0)
{
perror(“msgrcv”);
printf(“recv message failed\r\n”);
return _FALSE_;
}
else
{
printf(“RECV PROCESS : %s\r\n”,my_recv_msg.message_text);
}
}
}
else
{
printf(“get message queue id failed\r\n”);
return _FALSE_;
}

return _TRUE_;

}

ดังไฟล์ source code ตัวอย่างเป็นการรับส่ง message queue ด้วย key 822 และใช้ message type เป็น 1 โดย ทุกๆ 1 วินาที Process ที่ส่งจะส่งข้อความ “Message msg_number =XXX” ไปหา Process รับโดยเลข XXX จะนับเพิ่มขึ้นเรื่อย ๆ

Message Queue

จากตัวอย่าง หน้าจอสีแดงคือ Process รับ ส่วนหน้าจอสีขาวคือ Process ส่ง

นี่ก็เป็นตัวอย่างง่าย ๆ ของการสร้างการสื่อสารภายใน Application หรือระหว่าง Application ได้ง่ายและรวดเร็ว เพียงแค่เขียน code เพิ่มไม่กี่บรรทัด ซึ่งข้อดีของการใช้ Message Queue คือพัฒนาได้เร็ว รับส่งเร็ว แต่ข้อเสียคือขนาดของการส่งมีจำกัด ดูได้จากคำสั่ง ulimit -q แต่สามารถเปลี่ยนแปลงได้ ตามคำสั่งของ Unix นั้นๆ ครับ

ปล.หากต้องการนำไปเผยแพร่ ขอความกรุณาใส่เครดิต ให้ด้วยครับ ขอบคุณครับ

Ref : http://www.cs.cf.ac.uk/Dave/C/node25.html
Ref : http://linux.die.net/man/3/msgget

Read More

วิธีง่าย ๆ ในการส่ง Integer ไปทาง TCP

2009-10-03 / C/C++ / 476 Comments

วันนี้ผมจะมาเขียนเรื่องวิธีง่าย ๆ ที่ผมใช้ส่ง Integer ไปทาง TCP
ซึ่งการส่งข้อมูลจะต้องถูกตัดออกเป็นไบต์ ๆ  ในการส่ง
ซึ่งตัวแปล Integer (ของ CPU 32 bit) จะมีขนาด 4 ไบต์ (8 x 4 = 32)
ซึ่งวิธีการที่ผมใช้การ Shift Bit เข้ามาช่วง ซึ่งทำให้การเขียนโปแรกมนั้นง่าย CPU ทำงานน้อย (น้อยกว่าวิธีการ ลบ และ mod)

สมุติเรามีข้อมูลแบบนี้
(3)00110011 (2)00111100 (1)10000001 (0)11110000  (ค่าเท่าไหร่ไม่ต้องไปสนใจ โดยเราจะใช้ชื่อแทนว่า Data)
เวลาเราส่งก็ต้องแยกออกเป็นไบต์ ๆ ได้แบบนี้

(0)11110000
(1)10000001
(2)00111100
(3)00110011

โดยเรียงจากไบต์ต่ำมาหาไบต์สูง

ซึ่งวิธีการได้ไบต์ต่างๆ  มาดังนี้
(0)11110000 = (Data & 00000000 00000000 00000000 11111111)
(1)10000001 = (Data & 00000000 00000000 11111111 00000000) >> 8
(2)00111100 = (Data & 00000000 11111111 00000000 00000000) >> 16
(3)00110011 = (Data & 11111111 00000000 00000000 00000000) >> 24

จากตัวอย่างจะเป็นได้ว่า เรานำ Data มา & กับค่าตัวเลขชุดนึงเพื่อกำจัดค่าในไบต์อื่นที่เราไม่ต้องการออกไป แล้ว Shift Bit เพื่อให้ได้ไบต์ที่เราต้องการมาอยู่ในไบต์ที่ 0
โดยจาก วิธีการนี้สามารถนำมาเขียนเป็น c source code ได้ดังนี้ครับ

unsigned char splitInt2Byte(int data,unsigned char byteNumber)
{

return (data >> (8 * byteNumber)) & 0xFF;  
//return ((( 255 << (8*byteNumber)) & data) >>  (8*byteNumber));   //ค่า เท่ากัน
}

จบแล้วครับ หวังว่าจะเป็นประโยชน์ต่อใครหลายๆ คนครับ

ปล.ส่วนตัวแปลที่เป็น Double ก็ใช้วิธีการคล้าย ๆ นี้ครับ แต่จะแยกได้ทั้งหมด 8 ไบต์ครับ (CPU 32 bit)

Read More