读书笔记:第6章 System V消息队列 (4)

《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

你可能感兴趣的:(读书笔记,《UNIX网络编程》)