1. 消息队列的基础知识:

(1).消息队列是基于消息的,而管道是基于字节流的,且消息队列的读取不一定是先入先出

(2).消息队列是用链表实现的。


2.代码如下:

//comm.h

  1 #ifndef _COMM_
  2 #define _COMM_
  3 
  4 #include<stdio.h>
  5 #include<stdlib.h>
  6 #include<sys/ipc.h>
  7 #include<sys/msg.h>
  8 #include<sys/types.h>
  9 #include<string.h>
 10 
 11 #define _MSG_SIZE_ 1024
 12 #define PATH "."
 13 #define PROJ_ID 0x53
 14 
 15 extern const int g_ser_send_type;
 16 extern const int g_cli_send_type;
 17 
 18 typedef struct _msginfo
 19 {
 20     long mtype;
 21     char mtext[_MSG_SIZE_];
 22 }msginfo;
 23 
 24 static int comm_msg(int flag);
 25 int creat_msg_queue();
 26 int get_msg_queue();
 27 int destroy_msg_queue(int msg_id);
 28 
 29 //void print_log(char*);
 30 
 31 #endif
 
//comm.c

  1 #include "comm.h"
  2 
  3 const int g_ser_send_type=1;
  4 const int g_cli_send_type=2;
  5 
  6 static int comm_msg(int flag)
  7 {
  8     key_t _key=ftok(PATH,PROJ_ID);
  9     if(_key<0)
 10     {
 11         perror("ftok");
 12         return -1;
 13     }
 14     int msg_id=msgget(_key,flag);
 15     if(msg_id<0)
 16     {
 17         perror("msgget");
 18         return -1;
 19     }
 20     else
 21         return msg_id;
 22 }
 23 
 24 int creat_msg_queue()
 25 {
 26     return comm_msg(IPC_CREAT|IPC_EXCL);
 27 }
 28 int get_msg_queue()
 29 {
 30     return comm_msg(IPC_CREAT);
 31 }
 32 int destroy(int msg_id)
 33 {
 34     return msgctl(msg_id,IPC_RMID,NULL);
 35 }
 
 //client.c
  1 #include "comm.h"
  2 int main()
  3 {
  4     int _msg_id=get_msg_queue();
  5     msginfo _cli_info;
  6     while(1)
  7     {
  8         printf("client:>");
  9         fgets(_cli_info.mtext,sizeof(_cli_info.mtext),stdin);
 10         if(strncasecmp(_cli_info.mtext,"quit",4)==0)
 11         {
 12             printf("client bye!\n");
 13             break;
 14         }
 15         _cli_info.mtype=g_cli_send_type;
 16 //      memset(_cli_info.mtext,'\0',sizeof(_cli_info.mtext));
 17 //      if(read(0,_cli_info.mtext,sizeof(_cli_info.mtext))>=0)
 18 //      {
 19         if(msgsnd(_msg_id,&_cli_info,sizeof(_cli_info.mtext),0)<0)
 20         {
 21             perror("msgsnd");
 22             return -1;
 23         }
 24 //      }
 25         memset(_cli_info.mtext,'\0',sizeof(_cli_info.mtext));
 26         if(msgrcv(_msg_id,&_cli_info,_MSG_SIZE_,g_ser_send_type,0)<0)
 27         {
 28             perror("msgrcv");
 29             return -1;
 30         }
 31         else
 32             printf("server:>%s\n",_cli_info.mtext);
 33     }
 34     if(destroy(_msg_id)!=0)
 35     {
 36         perror("destroy");
 37         return -1;
 38     }
 39     return 0;
 40 }
 
//server.c
  1 #include "comm.h"
  2 
  3 int main()
  4 {
  5     int _msg_id=creat_msg_queue();
  6     msginfo _ser_info;
  7     while(1)
  8     {
  9         _ser_info.mtype=g_cli_send_type;
 10         memset(_ser_info.mtext,'\0',sizeof(_ser_info.mtext));
 11         if(msgrcv(_msg_id,&_ser_info,sizeof(_ser_info.mtext),g_cli_send_type ,0)<0)
 12         {
 13             perror("msgrcv");
 14             return -1;
 15         }
 16         else
 17         {
 18             printf("client:>%s\n",_ser_info.mtext);
 19             printf("server:>");
 20         }
 21 
 22         _ser_info.mtype=g_ser_send_type;                                              23         memset(_ser_info.mtext,'\0',sizeof(_ser_info.mtext));
 24         read(0,_ser_info.mtext,sizeof(_ser_info.mtext));
 25     //  fgets(_ser_info.mtext,_MSG_SIZE_,stdin);
 26         if(strncasecmp(_ser_info.mtext,"quit",4)==0)
 27         {
 28             printf("server bye!\n");
 29             break;
 30         }
 31         if(msgsnd(_msg_id,&_ser_info,_MSG_SIZE_,0)<0)
 32         {
 33             perror("msgsnd");
 34             return -1;
 35         }
 36     }
 37     if(destroy(_msg_id)!=0)
 38     {
 39         perror("destroy");
 40         return -1;
 41     }
 42     return 0;
 43 }     
 
//makefile
  1 .PHONY:all
  2 all:client server
  3 client:client.c comm.c
  4     gcc -o $@ $^
  5 server:server.c comm.c
  6     gcc -o $@ $^
  7 .PHONY:clean
  8 clean:
  9     rm client server

输出结果:

wKiom1cQkrTQVVfEAACGb3X4BX8512.png

分析:1.client和server都能发送和接受信息,且能在显示器上显示。

    2.当聊天结束,即输入“quit”时,自动调用destroy()函数,kill掉创建的消息队列。

    3.缺陷:client和server得一人一句轮流,例如:不能client说了多句,server再说。


3.所涉及函数

(1).创建新消息队列或取得已存在消息队列


1).msgget

 原型:int msgget(key_t key, int msgflg);

 参数:key:可以认为是一个端口号,也可以由函数ftok生成。


2).ftok. 函数ftok把一个已存在的路径名和一个整数标识得转换成个key_t值,称为IPC键:

 头文件: # include <sys/types.h> 

       # include <sys/ipc.h>

 原型:  key_t ftok(const char *pathname, int proj_id);

 参数:  msgflg:IPC_CREAT 如果IPC不存在,则创建一个IPC资源,否则打开操作。

           IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。

    IPC_EXEL标志本身并没有太大的意义,但是和IPC_CREAT标志起使用可以用来保证所得的对象是新建的,而不是打开已有的对象。

   a.如果单独使用IPC_CREAT,XXXget()函数返回一个已经存在的共享内存的操作符或返回一个新建的共享内存的标识符。

   b.如果将IPC_CREAT和IPC_EXCL标志一起使用,XXXget()将返回一个新建的IPC标识符;或者如果该IPC资源已存在,返回-1。

 

(2).向队列读/写消息

 原型:

    1).从队列中取用消息msgrcv:

         ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

    2).将数据放到消息队列中msgsnd:

         int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

 参数:

   msqid:消息队列的标识码

   msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

struct msgstru

{

    long mtype; //大于0

    char mtext[用户指定大小];

};

   msgsz:消息的大小。

   msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。

   msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。


 (3).设置消息队列属性

 原型:int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

 参数:msgctl 系统调用对 msgqid 标识的消息队列执行 cmd 操作,系统定义了3 种cmd 操作IPC_STAT, IPC_SET , IPC_RMID

   IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 指定的地址空间。

   IPC_SET : 该命令用来设置消息队列的属性,要设置的属性存储在buf中。  

   IPC_RMID : 从内核中删除 msqid 标识的消息队列。

 

 (4)其他函数

ftok函数:一个已存在的路径名和一个整数标识得转换成一个key_t值,称为IPC键:

 头文件: # include <sys/types.h> 

       # include <sys/ipc.h>

  原形:  key_t ftok(const char *pathname, int proj_id);

  返回值:成功返回生成的值,失败返回-1。

查看函数可以在编译时底行模式输入!man 函数名,或在命令行输入man 函数名