System V 消息队列 - 复用消息

  消息队列中的消息结构可以由我们自由定义,具备较强的灵活性。通过消息结构可以共享一个队列,进行消息复用。通常定义一个类似如下的消息结构:

复制代码
#define MSGMAXDAT     1024
struct mymsg
{
    long msg_len;   //消息长度
    long msg_type; //消息类型
    long msg_data[MSGMAXDATA]; //消息内容
};
复制代码

 消息结构相关联的类型字段(msg_type)提供了两个特性:

(1)标识消息,使得多个进程在单个队列上复用消息。

(2)用作优先级字段,允许接收者以不同于先进先出的某个顺序读出各个消息。

例子1:每个应用一个队列,可以在多个客户和单个服务器之间复用消息。使用一个消息队列进行通信,由消息类型标识消息是从客户到服务器,还是服务器到客户。通信模型如下:

按照通信模型编写程序如下:

公共头文件svmsg.h

 1 #ifndef  SVMSG_H
 2 #define  SVMSG_H
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <string.h>
 6 #include <unistd.h>
 7 #include <sys/types.h>
 8 #include <sys/ipc.h>
 9 #include <sys/msg.h>
10 #include <errno.h>
11 
12 #define MSG_R 0400 /* read permission */
13 #define MSG_W 0200 /* write permission */
14 #define SVMSG_MODE (MSG_R | MSG_W | MSG_R >>3 | MSG_R >>6)
15 #define MQ_KEY  1234L
16 #define MSGMAX  1024
17 //消息结构
18 struct mymesg
19 {
20     long mesg_len;
21     long mesg_type;
22     char mesg_data[MSGMAX];
23 };
24 #endif

客户端程序sysv_client.c

 1 #include "svmsg.h"
 2 void client(int ,int);
 3 
 4 int main(int argc,char *argv[])
 5 {
 6     int     msqid;
 7     if((msqid = msgget(MQ_KEY,0)) == -1)
 8     {
 9         perror("megget()");
10         exit(-1);
11     }
12     client(msqid,msqid);
13     exit(0);
14 }
15 
16 void client(int readfd,int writefd)
17 {
18     size_t len;
19     ssize_t n;
20     char *ptr;
21     struct mymesg mesg;
22     printf("Send request to server.\n");
23     //set pid to message
24     snprintf(mesg.mesg_data,MSGMAX,"%ld",(long)getpid());
25     len = strlen(mesg.mesg_data);
26     mesg.mesg_data[len] = ' '; //blank
27     ptr = mesg.mesg_data+len+1;
28     printf("Enter filename: ");
29     fgets(ptr,MSGMAX-len,stdin);
30     len = strlen(mesg.mesg_data);
31     if(mesg.mesg_data[len-1] == '\n')
32         len--;
33     mesg.mesg_len = len;
34     mesg.mesg_type = 1;
35     printf("mesg_data is :%s len=%ld\n",mesg.mesg_data, mesg.mesg_len);
36     if(msgsnd(writefd,&(mesg.mesg_type),mesg.mesg_len,0) == -1)
37     {
38         perror("msgsnd() error");
39         exit(-1);
40     }
41     //read from IPC,write to standard output
42     mesg.mesg_type = getpid();
43     while( (n = msgrcv(readfd,&(mesg.mesg_type),MSGMAX,mesg.mesg_type,0))>0)
44     {
45         write(STDOUT_FILENO,mesg.mesg_data,n);
46         putchar('\n');
47     }
48     if(n == 0 )
49     {
50         printf("Read file from server is completed.\n");
51     }
52     if(n == -1)
53     {
54         perror("msgrcv() error");
55         exit(-1);
56     }
57 }

服务器程序sysv_server.c

 1 #include "svmsg.h"
 2 void server(int ,int);
 3 int main(int argc,char *argv[])
 4 {
 5     int     msqid;
 6     if((msqid = msgget(MQ_KEY,SVMSG_MODE | IPC_CREAT)) == -1)
 7     {
 8         perror("megget()");
 9         exit(-1);
10     }
11     server(msqid,msqid);
12     exit(0);
13 }
14 
15 void server(int readfd,int writefd)
16 {
17     FILE            *fp;
18     char            *ptr;
19     pid_t           pid;
20     ssize_t         n;
21     ssize_t         len;
22     struct mymesg   mesg;
23     printf("Waiting for client......\n");
24     for(; ;)
25     {
26         mesg.mesg_type = 1;
27         if((n = msgrcv(readfd,&(mesg.mesg_type),MSGMAX,mesg.mesg_type,0)) == 0)
28         {
29             printf("pathname missing.\n");
30             continue;
31         }
32         mesg.mesg_data[n] = '\0';
33         printf("Received message from client is: %s\n",mesg.mesg_data);
34         if ((ptr = strchr(mesg.mesg_data,' ')) == NULL)
35         {
36             printf("bogus request: %s\n",mesg.mesg_data);
37             continue;
38         }
39         *ptr++ = 0;
40         pid = atoi(mesg.mesg_data);
41         mesg.mesg_type = pid;
42         //open fiel and read data
43         if((fp = fopen(ptr,"r")) == NULL)
44         {
45             printf("open file failed.sent msg to client\n");
46             snprintf(mesg.mesg_data+n,sizeof(mesg.mesg_data)-n,": can't open,%s\n",strerror(errno));
47             mesg.mesg_len = strlen(ptr);
48             memmove(mesg.mesg_data,ptr,mesg.mesg_len);
49             if(msgsnd(writefd,&(mesg.mesg_type),mesg.mesg_len,0) == -1)
50             {
51                 perror("msgsnd() error");
52                 exit(-1);
53             }
54         }
55         else
56         {
57             printf("open file successed.sent file to client\n");
58             while(fgets(mesg.mesg_data,MSGMAX,fp) != NULL)
59             {
60                 mesg.mesg_len = strlen(mesg.mesg_data);
61                 if(msgsnd(writefd,&(mesg.mesg_type),mesg.mesg_len,0) == -1)
62                 {
63                     perror("msgsnd() error");
64                     exit(-1);
65                 }
66             }
67             fclose(fp);
68         }
69         printf("send compelted.\n");
70         mesg.mesg_len = 0;
71         if(msgsnd(writefd,&(mesg.mesg_type),mesg.mesg_len,0) == -1)
72         {
73             perror("msgsnd() error");
74             exit(-1);
75         }
76     }
77 }

程序测试结果如下所示:

 例子2:每个客户一个队列,将例子1改成所有用户用一个共同的消息队列向服务器发送消息,给每个客户分配一个消息队列,使得服务器对每个客户进行应答。通信模型如下:

以并发服务器模型编写这个程序,服务器给每个客户fork一个子进程进行处理。程序如下:

公共头文件svmsg.h和svmsg.c:

 1 //svmsg.h file
 2 #ifndef  SVMSG_H
 3 #define  SVMSG_H
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 #include <string.h>
 7 #include <unistd.h>
 8 #include <signal.h>
 9 #include <sys/types.h>
10 #include <sys/ipc.h>
11 #include <sys/msg.h>
12 #include <errno.h>
13 
14 #define MSG_R 0400 /* read permission */
15 #define MSG_W 0200 /* write permission */
16 #define SVMSG_MODE (MSG_R | MSG_W | MSG_R >>3 | MSG_R >>6)
17 #define MQ_KEY  1234L
18 #define MSGMAX  1024
19 //message structure
20 struct mymesg
21 {
22     long mesg_len;
23     long mesg_type;
24     char mesg_data[MSGMAX];
25 };
26 
27 ssize_t mesg_send(int id,struct mymesg *mptr);
28 ssize_t mesg_recv(int id,struct mymesg *mptr);
29 
30 void    Mesg_send(int id,struct mymesg *mptr);
31 ssize_t Mesg_recv(int id,struct mymesg *mptr);
32 #endif
 1 //svmsg.c file
 2 #include "svmsg.h"
 3 
 4 ssize_t mesg_send(int id,struct mymesg *mptr)
 5 {
 6     return (msgsnd(id,&(mptr->mesg_type),mptr->mesg_len,0));
 7 }
 8 
 9 ssize_t mesg_recv(int id,struct mymesg *mptr)
10 {
11     ssize_t n;
12     n = msgrcv(id,&(mptr->mesg_type),MSGMAX,mptr->mesg_type,0);
13     mptr->mesg_len = n;
14     return n;
15 }
16 
17 void Mesg_send(int id,struct mymesg *mptr)
18 {
19     if(mesg_send(id,mptr) == -1)
20     {
21         perror("mesg_send() error");
22         exit(-1);
23     }
24 }
25 ssize_t Mesg_recv(int id,struct mymesg *mptr)
26 {
27     ssize_t n;
28     do
29     {
30         n = mesg_recv(id,mptr);
31     }while(n==-1 && errno == EINTR);
32     if(n == -1)
33     {
34         perror("mesg_recv() error");
35         exit(-1);
36     }
37     return n;
38 }

客户端程序如下:

 1 #include "svmsg.h"
 2 
 3 void client(int ,int);
 4 
 5 int main(int argc,char *argv[])
 6 {
 7     int    readid,writeid;
 8     if((writeid = msgget(MQ_KEY,0)) == -1)
 9     {
10         perror("megget()");
11         exit(-1);
12     }
13     if((readid = msgget(IPC_PRIVATE,SVMSG_MODE | IPC_CREAT)) == -1)
14     {
15         perror("megget()");
16         exit(-1);
17     }
18     client(readid,writeid);
19     msgctl(readid,IPC_RMID,NULL);
20     exit(0);
21 }
22 
23 void client(int readid,int writeid)
24 {
25     size_t len;
26     ssize_t n;
27     char *ptr;
28     struct mymesg mesg;
29     printf("Send request to server.\n");
30     //set pid to message
31     snprintf(mesg.mesg_data,MSGMAX,"%d",readid);
32     len = strlen(mesg.mesg_data);
33     mesg.mesg_data[len] = ' '; //blank
34     ptr = mesg.mesg_data+len+1;
35     printf("Enter filename: ");
36     fgets(ptr,MSGMAX-len,stdin);
37     len = strlen(mesg.mesg_data);
38     if(mesg.mesg_data[len-1] == '\n')
39         len--;
40     mesg.mesg_len = len;
41     mesg.mesg_type = 1;
42     printf("mesg_data is :%s\n",mesg.mesg_data);
43     Mesg_send(writeid,&mesg);
44     printf("Send messge to server successed.\n");
45     //read from IPC,write to standard output
46     while( (n = Mesg_recv(readid,&mesg))>0)
47     {
48         write(STDOUT_FILENO,mesg.mesg_data,n);
49         putchar('\n');
50     }
51     if(n == 0 )
52     {
53         printf("Read file from server is completed.\n");
54     }
55 }

服务器程序如下:

 1 #include "svmsg.h"
 2 
 3 void server(int ,int);
 4 void sig_child(int signo);
 5 
 6 int main(int argc,char *argv[])
 7 {
 8     int     msqid;
 9     if((msqid = msgget(MQ_KEY,SVMSG_MODE | IPC_CREAT)) == -1)
10     {
11         perror("megget()");
12         exit(-1);
13     }
14     server(msqid,msqid);
15     exit(0);
16 }
17 
18 void server(int readid,int writeid)
19 {
20     FILE            *fp;
21     char            *ptr;
22     pid_t           pid;
23     ssize_t         n;
24     ssize_t         len;
25     struct mymesg   mesg;
26     signal(SIGCHLD,sig_child);
27     printf("Waiting for client......\n");
28     for(; ;)
29     {
30         mesg.mesg_type = 1;
31         if((n = Mesg_recv(readid,&mesg)) == 0)
32         {
33             printf("pathname missing.\n");
34             continue;
35         }
36         mesg.mesg_data[n] = '\0';
37         printf("Received message from client is: %s\n",mesg.mesg_data);
38         if ((ptr = strchr(mesg.mesg_data,' ')) == NULL)
39         {
40             printf("bogus request: %s\n",mesg.mesg_data);
41             continue;
42         }
43         *ptr++ = 0;
44         writeid = atoi(mesg.mesg_data);
45         if(fork() == 0)
46         {
47             //open fiel and read data
48             if((fp = fopen(ptr,"r")) == NULL)
49             {
50                 printf("open file failed.sent msg to client\n");
51                 snprintf(mesg.mesg_data+n,sizeof(mesg.mesg_data)-n,": can't open,%s\n",strerror(errno));
52                 mesg.mesg_len = strlen(ptr);
53                 memmove(mesg.mesg_data,ptr,mesg.mesg_len);
54                 Mesg_send(writeid,&mesg);
55             }
56             else
57             {
58                 printf("open file successed.sent file to client\n");
59                 while(fgets(mesg.mesg_data,MSGMAX,fp) != NULL)
60                 {
61                     mesg.mesg_len = strlen(mesg.mesg_data);
62                     Mesg_send(writeid,&mesg);
63                 }
64                 fclose(fp);
65             }
66             printf("send compelted.\n");
67             mesg.mesg_len = 0;
68             Mesg_send(writeid,&mesg);
69         }
70     }
71 }
72 
73 void sig_child(int signo)
74 {
75     pid_t   pid;
76     int     stat;
77     while ((pid = waitpid(-1,&stat,WNOHANG)) > 0);
78     return ;
79 }

程序测试结果如下:

posted on 2018-02-24 16:03  AlanTu  阅读(203)  评论(0编辑  收藏  举报

导航