消息队列中的消息结构可以由我们*定义,具备较强的灵活性。通过消息结构可以共享一个队列,进行消息复用。通常定义一个类似如下的消息结构:
#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 }
程序测试结果如下: