07 Linux消息队列Demo

07 Linux消息队列Demo

作者 将狼才鲸
创建日期 2023-03-05

  • 运行效果:
jim@ubuntu:/mnt/hgfs/git_win10/many-repositories/23_Linux消息队列Demo$ make
gcc -o client comm.c client.c
gcc -o server comm.c server.c
jim@ubuntu:/mnt/hgfs/git_win10/many-repositories/23_Linux消息队列Demo$ make run
./server &
./client
msg_id is 294915
waitting for client message...
msg_id is 294915
send start...
server<==client[20]: Client hello world!
server<==client[11]: I'm client
server-->client[16]: server received
server-->client[16]: server received
server<==client[8]: e(exit)
message queue close success
jim@ubuntu:/mnt/hgfs/git_win10/many-repositories/23_Linux消息队列Demo$ 
  • 源码:
  1. Makefile:
default:
	gcc -o client comm.c client.c
	gcc -o server comm.c server.c
run:
	./server &
	./client
clean:
	rm server client
  1. comm.h:
/******************************************************************************
 * \brief	Linux消息队列Demo
 * \note	File format: UTF-8,中文编码:UTF-8
 * \remarks	[【Linux】进程间通信之消息队列](https://blog.csdn.net/wei_cheng18/article/details/79661495)
 * \author	将狼才鲸
 * \date	2023-03-05
 ******************************************************************************/


#ifndef _COMM_H_
#define _COMM_H_

/* 用于获取指定通道的消息 */
#define SERVER_ID 1
#define CLIENT_ID 2

#define MSG_QUEUE_MAXSIZE 128

extern int create_msg_queue(void);
extern int get_msg_queue(void);
extern int destroy_msg_queue(int msg_id);
extern int send_msg_queue(int msg_id, int channel_id, char *msg, int len);
extern int recv_msg_queue(int msg_id, int channel_id, char *recv_buf);

#endif /* _COMM_H_ */

  1. comm.c:
#include <sys/types.h>	/* key_t */
#include <sys/ipc.h>	/* ftok */
#include <sys/msg.h>	/* msgget msgctl msgsnd msgrcv */
#include <stdio.h>		/* perror */
#include <string.h>		/* memcpy */
#include "comm.h"

#define MSG_QUEUE_PATH "/tmp"
#define MSG_QUEUE_PROJECT_ID 1

/* 必须定义类似此结构的消息结构体,其中mtype有特定的操作含义;
   每条消息允许的最大长度还由操作系统限定,可在系统中用命令查看最大的允许值;
   用cat /proc/sys/kernel/msgmax命令查看单个消息队列的最大长度,
   我的电脑上是8k,用cat /proc/sys/kernel/msgmnb查看单个队列的最大总长度,
   我的电脑上是16k,这些最大值可以自行配置修改 */
typedef struct msgbuf {
    
    
    long mtype;
    char mtext[MSG_QUEUE_MAXSIZE];
} msgbuf_t;

static int set_msg_queue(int flags)
{
    
    
	/* 在指定目录下获取虚拟内存文件的id值,文件的权限为可读写;
	   也可以不用ftok函数获取key,可以直接给key赋值一个随机值就能运行 */
	key_t key = ftok(MSG_QUEUE_PATH, MSG_QUEUE_PROJECT_ID);
	if (key < 0) {
    
    
		perror("ftok");
		return -1;
	}

	//!!! 如果server msgget()报错文件已存在,可以每次手动给key赋新的不同的随机值再尝试
	//key_t key = 0x1111;

	/* 创建或者获取消息队列,给出创建参数 */
	int msg_id = msgget(key, flags);
	if (msg_id < 0) {
    
    
		perror("msgget");
		printf("please call destroy_msg_queue(), or change the key value and try again\n");
		return -1;
	}

	printf("msg_id is %d\n", msg_id);

	return msg_id;
}

int create_msg_queue(void)
{
    
    
	/* 创建消息队列,如果不存在则创建,如果存在则报错;
	   消息队列文件的权限为可读写 */
	return set_msg_queue(IPC_CREAT | IPC_EXCL | 0664);
}

int get_msg_queue(void)
{
    
    
	/* 获取消息队列,如果存在则返回,如果不存在则创建 */
	return set_msg_queue(IPC_CREAT);
}

/* server中必须要每次都正确执行到此函数,否则下次运行会报文件已存在错误 */
int destroy_msg_queue(int msg_id)
{
    
    
	/* 销毁消息队列 */
	int ret = msgctl(msg_id, IPC_RMID, NULL);
	if (ret < 0) {
    
    
		perror("msgctl");
		return -1;
	}
	
	return 0;
}

int send_msg_queue(int msg_id, int channel_id, char *msg, int len)
{
    
    
	struct msgbuf mbuf;
	
	if (!msg || len > sizeof(mbuf.mtext)) {
    
    
		printf("param %p %d error\n", msg, len);
		return -1;
	}

	mbuf.mtype = channel_id;
	memcpy(mbuf.mtext, msg, len);	/* 真正在项目中使用时可以直接将msg参数替换成mbuf参数,这样可以省一次拷贝的时间 */

	/* 发送消息 */
	int ret = msgsnd(msg_id, (void *)&mbuf, len, 0);
	if (ret < 0) {
    
    
		perror("msgsnd");
		return -1;
	}

	return 0;
}

int recv_msg_queue(int msg_id, int channel_id, char *recv_buf)
{
    
    
	struct msgbuf mbuf;
	
	if (!recv_buf) {
    
    
		printf("param %p error\n", recv_buf);
		return -1;
	}

	ssize_t len = msgrcv(msg_id, (void *)&mbuf, sizeof(mbuf.mtext), channel_id, 0);
	if (len < 0) {
    
    
		perror("msgrcv");
		return -1;
	}

	memcpy(recv_buf, mbuf.mtext, len);	/* 真正在项目中使用时可以直接将msg参数替换成mbuf参数,这样可以省一次拷贝的时间 */

	return len;	/* 返回真正读到mtext中的长度 */
}

  1. server.c:
#include <stdio.h>	/* printf */
#include "comm.h"

#define SERVER_STRING "server received"

static char rbuf[MSG_QUEUE_MAXSIZE];

int main()
{
    
    
	int msgid = create_msg_queue();
	if (msgid < 0)
		return -1;
	
	printf("waitting for client message...\n");
	
	int len;
	while (1) {
    
    
		len = recv_msg_queue(msgid, CLIENT_ID, rbuf);
		if (len > 0) {
    
    
			rbuf[len + 1] = '\0';
			printf("server<==client[%d]: %s\n", len, rbuf);

			send_msg_queue(msgid, SERVER_ID, SERVER_STRING, sizeof(SERVER_STRING));
						
			if (rbuf[0] == 'e') {
    
    	/* 要确保server成功退出并销毁 */
				goto exit;
			}
		}
	}

exit:
	printf("message queue close success\n");

	/* 不要在为接收到数据时就手动Ctrl + C关闭server,这样没有销毁消息队列,
	   下次重新运行server时会报错文件已存在;这时你可以自行添加程序关闭时的后处理,
	   或者手动对comm.c中key手动再赋一个新值进行测试;
	   也可以用ipcs -q命令查看消息队列,然后用ipcrm -Q key值 来销毁之前残留的
	   消息队列后再尝试*/
	destroy_msg_queue(msgid);
	
	return 0;
}
  1. client.c:
#include <stdio.h>	/* printf */
#include "comm.h"

#define CLIENT_STRING1 "Client hello world!"
#define CLIENT_STRING2 "I'm client"
#define CLIENT_STRING3 "e(exit)"	/* 通知server退出 */

static char rbuf[MSG_QUEUE_MAXSIZE];

int main()
{
    
    
	int msgid = get_msg_queue();
	if (msgid < 0)
		return -1;

	printf("send start...\n");

	send_msg_queue(msgid, CLIENT_ID, CLIENT_STRING1, sizeof(CLIENT_STRING1));
	send_msg_queue(msgid, CLIENT_ID, CLIENT_STRING2, sizeof(CLIENT_STRING2));

	int len;
	for (int i = 0; i < 2; i++) {
    
    
		len = recv_msg_queue(msgid, SERVER_ID, rbuf);
		if (len > 0) {
    
    
			rbuf[len + 1] = '\0';
			printf("server-->client[%d]: %s\n", len, rbuf);
		}
	}

	send_msg_queue(msgid, CLIENT_ID, CLIENT_STRING3, sizeof(CLIENT_STRING3));

	/* 客户端不要关闭消息队列 */

	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq582880551/article/details/129347316
07