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

《UNIX网络编程:卷2》P108-P110:图6-4、6-5、6-6、6-7

----------------------------------------------------------------------

msgcreate程序

创建一个消息队列。

把必须由用户作为命令行参数提供的路径名作为参数传递给ftok。导出的键由msgget转换成一个标识符。

/*
 * msgcreate.c
 * P108 图6-4 创建一个System V消息队列
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>

#define MSG_R   0400
#define MSG_W   0200
#define SVMSG_MODE  (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6)

int main(int argc, char *argv[])
{
    int c, oflag;
    key_t   key;

    oflag = SVMSG_MODE | IPC_CREAT;

    while ((c = getopt(argc, argv, "e")) != -1) {
        switch(c) {         // 使用-e选项指定IPC_EXCL标志
            case 'e':
                oflag |= IPC_EXCL;
                break;
        }
    }   
    if (optind != argc - 1) {
        fprintf(stderr, "usage: msgcreate [-e] <pathname>\n");
        exit(1);
    }   

    if ((key = ftok(argv[optind], 0)) < 0) {
        fprintf(stderr, "ftok error: %s\n", strerror(errno));
        exit(1);
    }   

    // 创建一个新的消息队列 
    if (msgget(key, oflag) < 0) {
        fprintf(stderr, "msgget error: %s\n", strerror(errno));
        exit(1);
    }   

    exit(0);
}

----------------------------------------------------------------------

msgsnd程序

该程序把一个指定了长度和类型的消息放置到某个队列中。

/*
 * msgsnd.c
 * P108 图6-5 往一个System V消息队列中加一个消息
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>

#define MSG_R	0400
#define MSG_W	0200
#define SVMSG_MODE	(MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6)

struct msgbuf {
	long	mtype;		// 消息类型
	char	mtext[1];	// 消息数据
};

int main(int argc, char *argv[])
{
	int				mqid;
	size_t			len;
	long			type;
	struct msgbuf	*ptr;
	key_t			key;

	if (argc != 4) {
		fprintf(stderr, "usage: msgsnd <pathname> <#bytes> <type>\n");
		exit(1);
	}
	len = atoi(argv[2]);
	type = atoi(argv[3]);

	if ((key = ftok(argv[1], 0)) < 0) {
		fprintf(stderr, "ftok error: %s\n", strerror(errno));
		exit(1);
	}

	// 打开一个消息队列 
	if ((mqid = msgget(key, MSG_W)) < 0) {
		fprintf(stderr, "msgget error: %s\n", strerror(errno));
		exit(1);
	}
	
	// 分配缓冲区
	if ((ptr = calloc(sizeof(long) + len, sizeof(char))) == NULL) {
		fprintf(stderr, "calloc error: %s\n", strerror(errno));
		exit(1);
	}
	ptr->mtype = type;

	// 放置一个消息到队列中
	if (msgsnd(mqid, ptr, len, 0) < 0) {
		fprintf(stderr, "msqid error: %s\n", strerror(errno));
		exit(1);
	}

	exit(0);
}

----------------------------------------------------------------------

msgrcv程序

该程序从队列中读出一个消息。-n命令行选项指定非阻塞,-t命令行选项指定msgrcv函数的type参数。

/*
 * msgrcv.c
 * P109 图6-6 从一个System V消息队列中读出一个消息
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>

#define MSG_R	0400
#define MSG_W	0200
#define SVMSG_MODE	(MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6)

#define MAXMSG (8192 + sizeof(long))

struct msgbuf {
	long	mtype;		// 消息类型
	char	mtext[1];	// 消息数据
};

int main(int argc, char *argv[])
{
	int				c, flag, mqid;
	long			type;
	ssize_t			n;
	struct msgbuf	*buff;
	key_t			key;

	type = flag  =0;
	while ((c = getopt(argc, argv, "nt:")) != -1) {
		switch(c) {
			case 'n':		// 非阻塞标志
				flag |= IPC_NOWAIT;
				break;
			case 't':		// 消息类型
				type = atol(optarg);
				break;
		}
	}

	if (optind != argc - 1) {
		fprintf(stderr, "usage: msgrcv [-n] [-t type] <pathname>\n");
		exit(1);
	}

	if ((key = ftok(argv[optind], 0)) < 0) {
		fprintf(stderr, "ftok error: %s\n", strerror(errno));
		exit(1);
	}

	// 打开一个消息队列 
	if ((mqid = msgget(key, MSG_R)) < 0) {
		fprintf(stderr, "msgget error: %s\n", strerror(errno));
		exit(1);
	}

	// 分配缓冲区
	if ((buff = malloc(MAXMSG)) == NULL) {
		fprintf(stderr, "malloc error: %s\n", strerror(errno));
		exit(1);
	}

	// 从队列中读出一个消息
	if ((n = msgrcv(mqid, buff, MAXMSG, type, flag)) < 0) {
		fprintf(stderr, "msgrcv error: %s\n", strerror(errno));
		exit(1);
	}

	printf("read %ld types, type = %ld\n", n, buff->mtype);

	exit(0);
}

----------------------------------------------------------------------

msgrmid程序

删除一个消息队列。

/*
 * msgrmid.c
 * P109 图6-7 删除一个System V消息队列
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>

int main(int argc, char *argv[])
{
    int             mqid;
    key_t           key;

    if (argc != 2) {
        fprintf(stderr, "usage: msgrmid <pathname>\n");
        exit(1);
    }   

    if ((key = ftok(argv[1], 0)) < 0) {
        fprintf(stderr, "ftok error: %s\n", strerror(errno));
        exit(1);
    }   

    // 打开一个消息队列 
    if ((mqid = msgget(key, 0)) < 0) {
        fprintf(stderr, "msgget error: %s\n", strerror(errno));
        exit(1);
    }   
    
    if (msgctl(mqid, IPC_RMID, NULL) < 0) {
        fprintf(stderr, "msgctl error: %s\n", strerror(errno));
        exit(1);
    }   

    exit(0);
}

----------------------------------------------------------------------

Makefile

msgcreate:
    gcc msgcreate.c -o msgcreate -Wall
msgsnd:
    gcc msgsnd.c -o msgsnd -Wall
msgrcv:
    gcc msgrcv.c -o msgrcv -Wall
msgrmid:
    gcc msgrmid.c -o msgrmid -Wall
all:
    make msgcreate msgsnd msgrcv msgrmid
clean:
    rm msgcreate msgsnd msgrcv msgrmid

----------------------------------------------------------------------

编译程序

$ make all
make msgcreate msgsnd msgrcv msgrmid
make[1]: 正在进入目录 `/home/user/workspace/unp2/chap06'
gcc msgcreate.c -o msgcreate -Wall
gcc msgsnd.c -o msgsnd -Wall
gcc msgrcv.c -o msgrcv -Wall
gcc msgrmid.c -o msgrmid -Wall
make[1]:正在离开目录 `/home/user/workspace/unp2/chap06'

----------------------------------------------------------------------

运行程序

现在使用刚刚给出的四个程序。首先创建一个消息队列并往其中写入三个消息。

$ ./msgcreate /tmp/no/such/file					使用不存在的路径创建消息队列
ftok error: No such file or directory
$ touch /tmp/test1								新建一个文件
$ ./msgcreate /tmp/test1 						使用已存在的文件创建一个消息队列
$ ./msgsnd /tmp/test1 1 100						写入消息
$ ./msgsnd /tmp/test1 2 200
$ ./msgsnd /tmp/test1 3 300
$ ipcs -q										查看消息队列信息

--------- 消息队列 -----------
键        msqid      拥有者  权限     已用字节数 消息      
0x0008fe95 65536      user      644        6            3


现在展示不以FIFO顺序读出消息时msgrcv的type参数的使用。

$ ./msgrcv -t 200 /tmp/test1 					请求读出类型为200的消息
read 2 types, type = 200
$ ./msgrcv -t -300 /tmp/test1 					读出类型小于或等于300且是最小的消息
read 1 types, type = 100
$ ./msgrcv /tmp/test1 							请求读出该队列中的第一个消息
read 3 types, type = 300			
$ ./msgrcv -n /tmp/test1 						无阻塞读取消息
msgrcv error: No message of desired type


测试读取不存在的消息

$ ./msgsnd /tmp/test1 1 100						放置一个长度为1、类型为100的消息
$ ./msgrcv -t 999 /tmp/test1 					读取不存在的消息类型
^C												Ctrl+C 终止程序
$ ./msgrcv -n -t 999 /tmp/test1 				使用无阻塞读取不存在的消息类型
msgrcv error: No message of desired type          
$ ./msgrmid /tmp/test1 							删除消息队列


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