一.简介 目前主要有两种类型的消息队列:POSIX消息队列以及系统V消息队列,系统V消息队列目前被大量使用。考虑到程序的可移植性,新开发的应用程序应尽量使用POSIX消息队列。 消息队列是内核创建的一个数据结构,是有标识的! 对于有读写权限的进程来说哦,所以可以通过对共享的消息进程的读写实现不同进程之间的通信! 关于消息队列的数据结构: #include<sys/msg.h> structmsqid_ds //!> msg queue id dscription ( 消息队列状态描述符 ) { structipc_perm msg_perm; //!> 读写perms structmsg * msg_first; //!> 队列中的第一个msg, 对于内存的存储而言的,对用户使用没有意义 structmsg * msg_last; //!> 队列中的最后一个msg ,对于内存的存储而言的,对用户使用没有意义 msglen_t msg_cbytes; //!> 当前的队列中的bytes msgunum_t msg_qnum; //!> 当前的队列中的message msglen_t msg_qbytes; //!> 队列中最大允许的bytes pid_t msg_lspid; //!> 最后一个发送msg的pid pid_t msg_lrpid; //!> 最后一个接收msg的pid time_t msg_stime; //!> 最后一个msg发送时间 time_t msg_rtime; //!> 最后一个msg收到时间 time_t msg_ctime; //!> 最后一个msg控制时间( time of last magctl()) }; 消息队列的创建:函数---> msgget():可以创建或者访问一个已经存在的queue #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> int msgget(key_t key, int flag); //!> 返回值就是操作句柄 //!> key 可以是ftok()函数的返回值或者IPC_PRIVATE //!> flag也是读写权限(一般是:IPC_CREAT或者IPC_CREAT |IPC_EXCL) 注意创建的一个队列后的msqid_ds的初始化参数是: msg_perm的uid和cuid是当前进程的有效用户id;gid和cgid为当前进程的有效组id msg_ctime是当前时间 msg_qbytes:为系统限制值 其余的值都是0 关于队列的操作: >>>>> int msgsnd(int msqid, const void * ptr, size_t mbytes, int flag ); //!> 参数:msqid:msgget()的返回值;ptr:是一个结构体的指针structmsgbuf { long mtype; char mtext[1] }; //!> flag:可以指定为:IPC_NOWAIT,。。。 >>>>> int msgrcv(int msqid, void * ptr, size_t nbytes, long type, int flag ); //!> 参数:ptr;接收到的消息的储存的位置 //!> type:希望从队列获取什么样内容的消息=0:返回第一个消息;>0 : 返回类型为type的第一个消息;<0:返回类型值小于或等于type参数的绝对值的消息中类型值中最小的第一个消息 >>>>> int msgctl(int msqid, int cmd, struct msqid_ds * buff ); 可以完成的操作: IPC_RMID: 删除指定队列的所有数据,只能由两种进程执行,第一个: 用户有效ID为 msg_perm.cuid或者msg_perm.uid 第二个:超级用户权限进程 IPC_SET: 按照buff值设置msg_perm.uid, msg_perm.gid, msg_perm.mode, msg_qbytes四个字段,执行进程同上面 IPC_STAT: 取队列中msqid_ds结构值放到buff中 二.ftok()函数简介:系统建立IPC通讯(如消息队列、共享内存时)必须指定一个ID值。通常情况下,该id值通过ftok函数得到。ftok原型如下:key_t ftok( char * name, int id )fname就时你指定的文件名,id是子序号。在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。 查询文件索引节点号的方法是: ls -i当删除重建文件后,索引节点号由操作系统根据当时文件系统的使用情况分配,因此与原来不同,所以得到的索引节点号也不同。如果要确保key_t值不变,要目确保ftok的文件不被删除,要么不用ftok,指定一个固定的key_t值。 同一段程序,用于保证两个不同用户下的两组相同程序获得互不干扰的IPC键值。由于etc/config.ini(假定)为应用系统的关键配置文件,因此不存在被轻易删除的问题——即使被删,也会很快被发现并重建(此时应用系统也将被重起)。ftok()的设计目的也在于此. 三. #include <stdlib.h>#include<string.h> #include <stdio.h>#include <errno.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <sys/stat.h> #define MSG_FILE "server.c" //!> 仅仅是创建的path而已#define BUFFER 255#define PERM S_IRUSR | S_IWUSR typedef struct dataType{ long mID; char buffer[BUFFER + 1];}dataType; struct msgtype{ long mtype; dataType mdata;}; int main(){ structmsgtype msg; key_t key; //!> 每个queue都有一个自己的KEY作为标志 int msgid; //!> if( ( key =ftok( MSG_FILE, 'a' ) ) == -1) //!> when fail { //!>注意'a'相当于是一个标志码而已 fprintf( stderr, "Create Key error...: %s /n", strerror( errno )); exit( EXIT_FAILURE ); } if( ( msgid= msgget( key, PERM | IPC_CREAT | IPC_EXCL ) ) == -1)//!> 创建 msg queue { fprintf( stderr, "Create Msg error...: %s /n", strerror( errno )); exit( EXIT_FAILURE ); } printf("/nMsg id = %d /n", msgid); //!>注意此处的flag都是简单处理为0 while( 1) //!> 接收 { msgrcv( msgid, &msg, sizeof( struct msgtype ), 1, 0); //!> 我们可以知道type=1,所以可以知道在client中的msg标志就是1 //!> ( 必须的,我们等一下可以使用进程ID的处理 ) fprintf( stderr, "Server receive: %s /n", msg.mdata.buffer); //!> msg.mtype = msg.mdata.mID; //!> 注意此处的ID必须要换成data中客户端进程的ID( 不然客户端无法识别!!!!!!!) sprintf( msg.mdata.buffer, " %d 客户端接收的回馈!",(int)msg.mtype ); //!> 注意server的回馈msg已经改变 msgsnd( msgid, &msg, sizeof( struct msgtype ), 0); //!> 回发送给client } exit( 0); } #include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>#include <sys/stat.h>#include <stdio.h> #define MSG_FILE "server.c"#define BUFFER 255#define PERM S_IRUSR | S_IWUSR typedef struct dataType{ long mID; char buffer[BUFFER + 1];}dataType; struct msgtype{ long mtype; dataType mdata;}; int main( int argc, char ** argv) { structmsgtype msg; key_t key; int msgid; if( argc !=2) //!> 需要输入一个字符串作为参数 { fprintf( stderr, "Usage: %s string ./n", argv[0] ); exit( EXIT_FAILURE ); } if( ( key =ftok( MSG_FILE, 'a' ) ) == -1) //!> 获得这个Key { fprintf( stderr, "Create Key error...: %s /n", strerror( errno )); exit( EXIT_FAILURE ); } if( ( msgid= msgget( key, PERM ) ) == -1) //!>获得queue的ID { fprintf( stderr, "Create Msg error...:%s /n", strerror( errno )); exit( EXIT_FAILURE ); } msg.mtype =1; //!>注意此处的type需要与server中匹配 msg.mdata.mID =getpid(); //!> my ID strncpy(msg.mdata.buffer, argv[1], BUFFER); //!> 获得命令行参数而已 msgsnd(msgid, &msg, sizeof( struct msgtype ), 0); //!> 发送给server memset(&msg, '/0', sizeof( struct msgtype )); msgrcv(msgid, &msg, sizeof( struct msgtype ), getpid(), 0); //!>获得server的反馈( 注意type是自己的ID,所以就是server的发送msg中带的 ) fprintf(stderr, "Client receive: %s /n", msg.mdata.buffer); //!> 注意只接受自己ID的msg!!! exit(EXIT_SUCCESS );} 2.运行./s& //!> server 后台./cILOVEYOU //!> 参数是字符串 if是多客户操作:for i in 1 2 3 4 5; do ./c sss &done //!> 注意 sss 是参数而已 3.注意:<1>: client对于server的发送的标志是一样的才是可以的! 因为server不可以识别多个未知的client,但是对于server的反馈而言可以根据client的ID 也就是client的接受可以根据自己的进程ID来进行不同的接受( 在网络环境下可以根据Socket套接字接受) <2>: 注意对于msg queue 而言:必须有一个这样的数据结构:struct msgtype{ long mtype; dataType mdata; //!> 注意此处的不一定只是char*,可以是自己定义的}; //!> 但是整体的格式必须的,主要是long这个标志是必需的!!! typedef struct dataType{ long mID; char buffer[BUFFER + 1];}dataType; 注意:对于cs模式而言,server和client必须是打开同一个queue才是OK的,所以需要有相同的操作就是: if( ( key = ftok( MSG_FILE, 'a' ) ) == -1) //!> 获得相同的Key ... 获得了相同的KEY后,对于msgget的操作(也就是打开文件,自己随便...) 摘自shanshanpt的专栏