《UNIX网络编程:卷2》P112-P113:图6-9、6-10、6-11、6-12、6-13
两个消息队列,一个队列用于从客户到服务器的消息,另一个队列用户从服务器到客户的消息。
--------------------------------------------
#ifndef SVMSG_H__ #define SVMSG_H__ /* * svmsg.h * P112 图6-9 使用消息队列的客户-服务器程序的头文件 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/msg.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> #include <limits.h> #define MSG_R 0400 #define MSG_W 0200 #define SVMSG_MODE (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6) #define MQ_KEY1 1234L #define MQ_KEY2 2345L /* P52 mesg.h */ #define MAXMESGDATA (PIPE_BUF - 2 * sizeof(long)) #define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATA) struct mymesg { long mesg_len; // 消息长度 long mesg_type; // 消息类型 char mesg_data[MAXMESGDATA]; }; void client(int readfd, int writefd); void server(int readfd, int writefd); ssize_t mesg_send(int fd, struct mymesg *mptr); ssize_t mesg_recv(int fd, struct mymesg *mptr); #endif
--------------------------------------------
创建两个方向的消息队列,随后调用server。
#include "svmsg.h" int main(int argc, char *argv[]) { int readid, writeid; // 创建第一个消息队列,用于读 if ((readid = msgget(MQ_KEY1, SVMSG_MODE | IPC_CREAT)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } // 创建第二个消息队列,用于写 if ((writeid = msgget(MQ_KEY2, SVMSG_MODE | IPC_CREAT)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } server(readid, writeid); exit(0); } /* * server.c * 图4-30 我们使用消息的server函数 */ void server(int readfd, int writefd) { FILE *fp; ssize_t n; struct mymesg mesg; mesg.mesg_type = 1; // 读取来自客户的路径名 if ((n = mesg_recv(readfd, &mesg)) == 0) { fprintf(stderr, "pathname messing\n"); exit(1); } mesg.mesg_data[n] = '\0'; // 以空字符结尾的字符串 // 打开客户指定文件 if ((fp = fopen(mesg.mesg_data, "r")) == NULL) { // 打开文件失败 // 构建出错消息 snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) - n, ": can't open, %s\n", strerror(errno)); mesg.mesg_len = strlen(mesg.mesg_data); mesg_send(writefd, &mesg); // 发送出错消息到客户 } else { // 打开文件成功,读出该文件并发送给客户 while (fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL) { mesg.mesg_len = strlen(mesg.mesg_data); mesg_send(writefd, &mesg); } fclose(fp); // 关闭打开的文件 } mesg.mesg_len = 0; mesg_send(writefd, &mesg); // 发送长度为0的消息表示已到达文件结尾 } /* * mesg_send.c * 图6-12 用于消息队列的mesg_send函数 */ ssize_t mesg_send(int id, struct mymesg *mptr) { return(msgsnd(id, &(mptr->mesg_type), mptr->mesg_len, 0)); } /* * mesg_recv.c * 图6-13 用于消息队列的mesg_recv函数 */ ssize_t mesg_recv(int id, struct mymesg *mptr) { ssize_t n; n = msgrcv(id, &(mptr->mesg_type), MAXMESGDATA, mptr->mesg_type, 0); mptr->mesg_len = n; return n; }
--------------------------------------------
打开两个方向上的消息队列,随后调用client。
#include "svmsg.h" int main(int argc, char *argv[]) { int readid, writeid; // 打开第一个消息队列,用于写 if ((writeid = msgget(MQ_KEY1, 0)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } // 打开第二个消息队列,用于读 if ((readid = msgget(MQ_KEY2, 0)) < 0) { fprintf(stderr, "msgget error: %s\n", strerror(errno)); exit(1); } client(readid, writeid); msgctl(readid, IPC_RMID, NULL); // 删除消息队列 msgctl(writeid, IPC_RMID, NULL); exit(0); } /* * client.c * 图4-29 我们的使用消息的client函数 */ void client(int readfd, int writefd) { size_t len; ssize_t n; struct mymesg mesg; // 从标准输入读路径名 fgets(mesg.mesg_data, MAXMESGDATA, stdin); len = strlen(mesg.mesg_data); if (mesg.mesg_data[len-1] == '\n') len--; // 删除换行符 mesg.mesg_len = len; // 消息长度 mesg.mesg_type = 1; // 消息类型 // 将消息发送给服务器 if (mesg_send(writefd, &mesg) < 0) { fprintf(stderr, "mesg_send error: %s\n", strerror(errno)); exit(1); } // 通过循环,读出服务器发送回的所有内容 while ((n = mesg_recv(readfd, &mesg)) > 0) { // 将消息内容发送 write(STDOUT_FILENO, mesg.mesg_data, n); } } /* * mesg_send.c * 图6-12 用于消息队列的mesg_send函数 */ ssize_t mesg_send(int id, struct mymesg *mptr) { return(msgsnd(id, &(mptr->mesg_type), mptr->mesg_len, 0)); } /* * mesg_recv.c * 图6-13 用于消息队列的mesg_recv函数 */ ssize_t mesg_recv(int id, struct mymesg *mptr) { ssize_t n; n = msgrcv(id, &(mptr->mesg_type), MAXMESGDATA, mptr->mesg_type, 0); mptr->mesg_len = n; return n; }
--------------------------------------------
运行程序:
在一个窗口启动服务器端:
$ ./server_main
在另一个窗口启动客户端:
$ ./client_main Makefile 指定打开Makefile文件 client_main: gcc client_main.c -o client_main -Wall server_main: gcc server_main.c -o server_main -Wall all: make client_main server_main clean: rm client_main server_main