flag是读写权限值的组合,还可以与IPC_CREAT或IPC_CREAT|IPC_EXCL按位或。
IPC_EXCL如果该队列已经存在,再加上该字段会报错,具体EXCL的意思类似于文件IO中的open函数中O_EXCL参数的含义。
给调用者返回对应所指定消息队列的当前msqid_ds结构。
写个客户-服务器例子,创建两个消息队列,一个队列用来从客户到服务器的消息,一个队列用于从服务器到客户的消息。主要功能是:客户向服务器发现一条消息,服务接收到并输出接收到的客户消息,然后服务器向客户发送一条消息,客户显示服务器发送的消息。
下面代码在测试中使用注释部分的代码会出问题,具体见代码中的注释。
头文件:
#ifndef SYSVMSG_H #define SYSVMSG_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <error.h> #include <unistd.h> #include <fcntl.h> #include <sys/msg.h> #include <sys/ipc.h> #include <sys/types.h> #define MSGQ_ID1 27 #define MSGQ_ID2 54 #define SENDMSG_TYPE 1 #define RECVMSG_TYPE 2 #define PATHNAME1 "/tmp/msg1" #define PATHNAME2 "/tmp/msg2" #define N 256 #define MSG_R 0400 #define MSG_W 0200 #define MODE IPC_CREAT | MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6 struct Data{ int a; int b; //char *buf;//这里也不要用指针 char buf[N]; }; struct msgbuf{ long mtype; struct Data mdata;//这里不要用指针 }; #endifServer.c
#include "sysvmsg.h" void server(int readfd,int writefd){ struct msgbuf *recv,*send; recv = (struct msgbuf*)malloc(sizeof(struct msgbuf)); send = (struct msgbuf*)malloc(sizeof(struct msgbuf)); ssize_t n; puts("Waiting for client..."); if((n = msgrcv(readfd,recv,sizeof(struct msgbuf),RECVMSG_TYPE,0)) == -1){ perror("msgrcv error"); exit(-1); } printf("Received message from client: Content = %s, a = %d, b = %d\n",recv->mdata.buf,recv->mdata.a,recv->mdata.b); //free(recv);/*这里如果不注释会出错,具体不知道,可能原因是在Client.c中free了,测试环境是centos6*/ puts("==========================================="); printf("Enter message content: "); scanf("%s",send->mdata.buf); printf("Enter two number: "); scanf("%d%d",&send->mdata.a,&send->mdata.b); send->mtype = SENDMSG_TYPE; puts("Sending message to client..."); if(msgsnd(writefd,send,sizeof(struct msgbuf),0) == -1){ perror("msgsnd error"); exit(-1); } //free(send);//同上 } int main(){ int readfd,writefd; //PATHNAME1: Client send message to Server, Server receive message from Client if((readfd = msgget(ftok(PATHNAME1,MSGQ_ID1),MODE)) == -1){ perror("msgget error"); exit(-1); } //PAHTNAME2: Server send message to Client, Client receive message from Server if((writefd = msgget(ftok(PATHNAME2,MSGQ_ID2),MODE)) == -1){ perror("msgget error"); exit(-1); } server(readfd,writefd); }client.c
#include "sysvmsg.h" void client(int readfd,int writefd){ struct msgbuf *recv,*send; recv = (struct msgbuf*)malloc(sizeof(struct msgbuf)); send = (struct msgbuf*)malloc(sizeof(struct msgbuf)); ssize_t n; printf("Enter message content: "); scanf("%s",send->mdata.buf); puts(send->mdata.buf); printf("Enter two number: "); scanf("%d %d",&send->mdata.a,&send->mdata.b); printf("%d\n",&send->mdata.a); send->mtype = RECVMSG_TYPE; puts("Sending message to Server"); if(msgsnd(writefd,send,sizeof(struct msgbuf),0) == -1){ perror("msgsnd error"); exit(-1); } free(send); puts("========================================"); if((n = msgrcv(readfd,recv,sizeof(struct msgbuf),SENDMSG_TYPE,0)) == -1){ perror("msgrcv error"); exit(-1); } printf("Received message from Server: Conten = %s, a = %d, b = %d\n",recv->mdata.buf,recv->mdata.a,recv->mdata.b); free(recv); } int main(){ int readfd,writefd; if((readfd = msgget(ftok(PATHNAME2,MSGQ_ID2),0)) == -1){ perror("msgget error"); exit(-1); } if((writefd = msgget(ftok(PATHNAME1,MSGQ_ID1),0)) == -1){ perror("msgget error"); exit(-1); } client(readfd,writefd); msgctl(readfd,IPC_RMID,NULL); msgctl(writefd,IPC_RMID,NULL); return 0; }
7、复用消息
消息队列中的消息结构可以由我们自由定义,具备较强的灵活性。通过消息结构可以共享一个队列,进行消息复用。
此种多个客户端和单个服务器使用同一个消息队列,死锁总是存在的,客户端可以填满消息队列后,妨碍服务器的发送应答,这些客户于是阻塞在msgsnd中,服务器同样如此。可检测这种死锁的方法之一是约定服务器对消息队列总是非阻塞写。
详细例子(客户端给服务器发送需要打开的文件名,服务器读取文件信息逐条发送给客户端):
公共头文件:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/ipc.h> #include <sys/msg.h> #include <errno.h> #include <sys/types.h> #include <sys/signal.h> #include <sys/wait.h> #define MSG_R 0400 #define MSG_W 0200 #define MSG_X 0100 #define MSG_MODE MSG_R | MSG_R >> 3 | MSG_W | MSG_R >> 6 | IPC_CREAT #define PATHNAME "/tmp/msg" #define ID 3456 #define N 1024 struct msgbuf{ int mlen;//发送消息的长度 long mtype; char mdata[N]; };Server.c
#include "msghead.h" const long client_type = 1; void server(int readfd,int writefd){ struct msgbuf msg; ssize_t n; char* ptr; long pid; FILE *fp; while(1){ if((n = msgrcv(readfd,&msg.mtype,sizeof(msg.mdata),client_type,0)) == 0){ puts("Pathname missing"); continue; } msg.mdata[n] = '\0'; printf("Received message from client: %s\n",msg.mdata); if((ptr = strchr(msg.mdata,'#')) == NULL){ printf("bad request: %s\n",msg.mdata); continue; } *ptr++ = 0; pid = atoi(msg.mdata); printf("pid = %d,filepath = %s,mdata = %s\n",pid,ptr,msg.mdata); msg.mtype = pid; if((fp = fopen(ptr,"r")) == NULL){ puts("Open file failed. Send msg to client"); int len = strlen(msg.mdata); snprintf(msg.mdata+len,sizeof(msg.mdata)-len,": can't open %s\n",strerror(errno)); memmove(msg.mdata+len+1,ptr,strlen(ptr)); printf("mdata = %s",msg.mdata); if(msgsnd(writefd,&msg.mtype,strlen(msg.mdata),0) == -1){ perror("msgsnd error"); exit(-1); } }else{ puts("Open file successfully."); setvbuf(fp,msg.mdata,_IOLBF,sizeof(msg.mdata)); while(fgets(msg.mdata,sizeof(msg.mdata),fp) != NULL){ fflush(fp); msgsnd(writefd,&msg.mtype,strlen(msg.mdata),0); } fclose(fp); } puts("send completed."); if(msgsnd(writefd,&msg,0,0) == -1){ perror("msgsnd error"); exit(-1); } } } int main(){ int msqid; if((msqid = msgget(ftok(PATHNAME,ID),MSG_MODE)) == -1){ perror("msgget error"); exit(-1); } server(msqid,msqid); exit(0); }第二种方法就是使用并发服务器,多个客户端通过同一个消息队列给服务器发送消息,并且客户端新建自己的消息队列,服务器使用子进程来处理每个客户端的请求,并且使用客户端新建的消息队列将信息发送到客户端。通信模型如图:
公共头文件和上面的一样。
客户端给服务器发送需要打开的文件名,服务器读取文件信息逐条发送给客户端。
Server.c
#include "msghead.h" void sig_child(int signo){ int stat; pid_t pid; while((pid = waitpid(-1,&stat,WNOHANG)) > 0); printf("Catch signal %d\n",signo); return; } void server(int readfd,int writefd){ FILE *fp; ssize_t n; char* ptr; struct msgbuf msg; signal(SIGCHLD,sig_child); while(1){ msg.mtype = 1; if((n = msgrcv(readfd,&msg.mtype,sizeof(msg.mdata),msg.mtype,0)) == 0){ puts("Pathname missing"); continue; } msg.mdata[n] = '\0'; printf("Received message from client = %s\n",msg.mdata); if((ptr = strchr(msg.mdata,'#')) == NULL){ puts("bad request"); continue; } *ptr++ = 0; writefd = atoi(msg.mdata); msg.mtype = writefd; printf("writefd = %d\n",writefd); if(fork() == 0){ if((fp = fopen(ptr,"r")) == NULL){ printf("Open file failed, send message to client\n"); int len = strlen(msg.mdata); snprintf(msg.mdata+len,sizeof(msg.mdata)-len,": can't open, %s\n",strerror(errno)); msg.mlen = strlen(msg.mdata); //memmove(msg.mdata,ptr,msg.mlen); printf("data = %s\n",msg.mdata); if(msgsnd(writefd,&msg.mtype,msg.mlen,0) == -1){ perror("msgsnd error"); exit(-1); } }else{ printf("open file successfully."); while(fgets(msg.mdata,sizeof(msg.mdata),fp) != NULL){ msg.mlen = strlen(msg.mdata); if(msgsnd(writefd,&msg.mtype,msg.mlen,0) == -1){ perror("msgsnd error"); exit(-1); } } fclose(fp); } puts("send completed"); msg.mlen = 0; if(msgsnd(writefd,&msg.mtype,msg.mlen,0) == -1){ perror("msgsnd error"); exit(-1); } } } } int main(){ int readfd,writefd; if((readfd = msgget(ftok(PATHNAME,ID),MSG_MODE)) == -1){ perror("msgget error"); exit(-1); } server(readfd,writefd); exit(0); }Client.c
#include "msghead.h" void client(int readfd,int writefd){ struct msgbuf msg; snprintf(msg.mdata,sizeof(msg.mdata),"%d",readfd);//将客户端消息队列的msqid发送给服务器 int len = strlen(msg.mdata); msg.mdata[len] = '#'; fgets(&msg.mdata[len]+1,sizeof(msg.mdata)-len,stdin);//获取标准输入,直接append到msg.mdata msg.mlen = strlen(msg.mdata); if(msg.mdata[msg.mlen - 1] == '\n') msg.mlen --; msg.mtype = 1; printf("data = %s\n",msg.mdata); if(msgsnd(writefd,&msg.mtype,msg.mlen,0) == -1){ perror("msgsnd error"); exit(-1); } ssize_t n; msg.mtype = readfd; while((n = msgrcv(readfd,&msg.mtype,sizeof(msg.mdata),msg.mtype,0)) > 0){ write(1,msg.mdata,n); } if(n == 0){ puts("Read file from server is completed"); } } int main(){ int readfd,writefd; if((writefd = msgget(ftok(PATHNAME,ID),0)) == -1){//打开服务器创建的消息队列 perror("msgget error"); exit(-1); } if((readfd = msgget(IPC_PRIVATE,MSG_MODE)) == -1){//新建客户端自己独立的消息队列,使用IPC_PRIVATE perror("msgget error"); exit(-1); } client(readfd,writefd); msgctl(readfd,IPC_RMID,NULL); exit(0); }