一、什么是消息队列

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认

为是有一个类型,接收者进程接收的数据块可以有不同的类型值。我们可以通过发送消息

来避免命名管道的同步和阻塞问题。消息队列与管道不同的是,消息队列是基于消息的,

而管道是基于字节流的,且消息队列的读取不一定是先入先出。消息队列与命名管道有一

样的不足,就是每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总的字节

数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)。

消息队列:操作系统提供缓冲区,提供了一种从一个进程向另一个进程发送一个数据块的方法。消息队列与管道不同的是,消息队列是基于消息的,而管道是基于字节流的。

查看系统消息队列命令:ipcs -q

删除消息队列命令:ipcrm -q 消息id号

相关函数:

原型: 产生消息队列:int msgget(key_t key, int msgflg);

发送消息:int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

接收消息:ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);

设置消息队列属性原型:int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

参数:系统定义了 种 cmd 操作: IPC_STAT , IPC_SET , IPC_RMID

IPC_STAT : 该命令用来获取消息队列对应的 msqid_ds 数据结构,并将其保存到 buf 定的地址空间。 
IPC_SET : 该命令来设置消息队列的属性,要设置的属性存储在buf中。
IPC_RMID : 从内核中删除 msqid 标识的消息队列。

二、IPC对象数据结构

wKiom1cN9seAX41hAADbD8lPf3w492.png


三、消息队列结构(/usr/include/linux/msg.h)

wKiom1cN9sfzv0b_AAJQZMChKDw195.png



实现1:

//comm.h
  1 #pragma once
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<string.h>
  5 #include<sys/types.h>
  6 #include<unistd.h>
  7 #include<sys/ipc.h>
  8 #include<sys/msg.h>
  9 #define _PATH_ "."
 10 #define _PROJ_ID_ 0x777
 11 #define _BLOCK_SIZE_ 1024
 12 #define _SERVER_MSG_TYPE_ 1
 13 #define _CLIENT_MSG_TYPE_ 2
 14 struct msgbuf
 15 {
 16     long mtype;
 17     char mtext[_BLOCK_SIZE_];
 18 };
 19 static int comm_msg_queue(int flag);
 20 int set_msg_queue();
 21 int get_msg_queue();
 22 int msg_queue_send(int msg_id,const char* msg,long type);
 23 int msg_queue_recv(int msg_id,char* msg,long type);
 24  int destory_msg_queue(int msgId);
 //comm.c
  1 #include"comm.h"
  2 static int comm_msg_queue(int flag)
  3 {
  4 
  5     key_t _key=ftok(_PATH_,_PROJ_ID_);
  6     if(_key<0)
  7     {
  8         perror("ftok");
  9         return -1;
 10     }   
 11     int msg_id=msgget(_key,flag);
 12     if(msg_id<0)
 13     {
 14         perror("msgget");
 15         return -1;
 16     }   
 17     return msg_id;
 18 }   
 19 int set_msg_queue()
 20 {
 21     umask(0);
 22     return comm_msg_queue(IPC_CREAT|IPC_EXCL|0666);
 23 }   
 24 int get_msg_queue()
 25 {
 26     return comm_msg_queue(IPC_CREAT);
 27 }   
 28 int msg_queue_send(int msg_id,const char* message,long type)
 29 {
 30     struct msgbuf msg;
 31     msg.mtype=type;
 32     strcpy(msg.mtext,message);
 33     if(msgsnd(msg_id,&msg,strlen(msg.mtext),0)<0)
 34     {
 35         perror("msgsnd");
 36         return -1;
 37     }   
 38     return 0;
 39  }
 40 int msg_queue_recv(int msg_id,char* msg,long type)
 41 {
 42     struct msgbuf ret;
 43     memset(ret.mtext,'\0',_BLOCK_SIZE_);
 44     if(msgrcv(msg_id,&ret,_BLOCK_SIZE_-1,type,0)<0)
 45     {
 46         perror("msgrcv");
 47         return -1;
 48     }
 49     strcpy(msg,ret.mtext);
 50     return 0;
 51 }
 52 int destory_msg_queue(int msg_id)
 53 {
 54     if(msgctl(msg_id,IPC_RMID,NULL)<0)
 55     {
 56         perror("msgctl");
 57         return -1;
 58     }
 59     else
 60     {
 61         printf("remove msg_queue\n");
 62         return 0;
 63     }
 64 }
 //server.c
  1 #include"comm.h"
  2 int main()
  3 {
  4     int msgid=set_msg_queue();
  5     if(msgid<0)
  6     {
  7         exit(1);
  8     }
  9     char buf[_BLOCK_SIZE_];
 10     printf("input quit endding..\n");
 11     while(1)
 12     {
 13         if(msg_queue_recv(msgid,buf,_CLIENT_MSG_TYPE_)<0)
 14         {
 15             printf("recv fail\n");
 16             exit(1);
 17         }
 18         else
 19         {
 20             if(strcmp("quit",buf)==0)
 21                 return 0;
 22             printf("client:%s\n",buf);
 23         }
 24         printf("input:");
 25         fflush(stdout);
 26         memset(buf,'\0',_BLOCK_SIZE_);
 27         gets(buf);
 28         if(msg_queue_send(msgid,buf,_SERVER_MSG_TYPE_)<0)
 29         {
 30             printf("send fail\n");
 31             exit(1);
 32         }
 33     }
 34     destroy(msgid);
 35     return 0;
 36 }
 //client.c
  1 #include"comm.h"
  2 int main()
  3 {
  4     int msgid=get_msg_queue();
  5     if(msgid<0)
  6     {
  7         exit(1);
  8     }
  9     char buf[_BLOCK_SIZE_];
 10     while(1)
 11     {
 12         fflush(stdout);
 13         printf("please input:");
 14         memset(buf,'\0',_BLOCK_SIZE_);
 15         gets(buf);
 16         if(msg_queue_send(msgid,buf,_CLIENT_MSG_TYPE_)<0)
 17         {
 18             printf("send fail\n");
 19             exit(1);
 20         }
 21         if(msg_queue_recv(msgid,buf,_SERVER_MSG_TYPE_)<0)
 22         {
 23             printf("recv fail\n");
 24             exit(1);
 25         }
 26         printf("server:%s\n",buf);
 27     }
 28     return 0;
 29 }
 //Makefile的编写
  1 .PHONY:all
  2 all:server client
  3 server:server.c comm.c
  4     gcc -o $@ $^
  5 client:client.c comm.c
  6     gcc -o $@ $^
  7 .PHONY:clean
  8 clean:
  9     rm -f server client

运行结果:

wKiom1cKC0-hcvITAAB4DDppdKs102.png

实现2:

//comm.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <string.h>
#include <time.h>

#define _MSG_SIZE_ 1024
#define _FILEPATH_ "./.msg"
#define ID 0x7777

extern const int g_ser_send_type;
extern const int g_cli_send_type;

typedef struct _msginfo
{
	long mytype;
	char mtext[_MSG_SIZE_];
}msginfo;

//comm.c
#include"comm.h"

const int g_ser_send_type = 1;
const int g_cli_send_type = 2;

void print_log(char *msg)
{
	printf("%s[%d]:%s\n",__FUNCTION__,__LINE__,msg);
}


//msg_server.c
#include"comm.h"

int _msg_id = -1;

int msg_server_end(int id);

int msg_server_start();
int msg_server_start()
{
	key_t _key = ftok(_FILEPATH_,ID);
	if(_key < 0)
	{
		print_log("get key id error");
	}

	msginfo _ser_info;
	_msg_id = msgget(_key,IPC_CREAT);
	if(_msg_id < 0)
	{
		print_log("msg_server got key id failed\n");
		return 1;
	}

	while(1)
	{
		sleep(15);
		if(msgrcv(_msg_id,&_ser_info,sizeof(_ser_info),g_cli_send_type,0) == -1)
		{
			print_log("msg rcv error");
			return 1;
		}

		printf("client:>%s\n",_ser_info.mtext);
		printf("server:>");
		memset(_ser_info.mtext,'\0',sizeof(_ser_info.mtext));
		fgets(_ser_info.mtext,_MSG_SIZE_,stdin);
		if(strncasecmp(_ser_info.mtext,"quit",4) == 0)
		{
			printf("server bye!\n");
			break;
		}
		_ser_info.mytype = g_ser_send_type;
		if(msgsnd(_msg_id,&_ser_info,_MSG_SIZE_,0) == -1)
		{
			printf("server send msg error\n");
			exit(0);
		}
	}
	return 0;
}


int msg_server_end(int id)
{
	if(msgctl(id,IPC_RMID,NULL) == -1)
	{
		printf("delete msg knernel info error\n");
		return 1;
	}
	return 0;
}

static void delete_msg()
{
	if(_msg_id != -1)
	{
		msg_server_end(_msg_id);
	}
	printf("delete msg queue end\n");
}

int main()
{
	atexit(delete_msg);
	if(msg_server_start() == 0)
	{
		print_log("msg_server start success\n");
	}
	else{
		print_log("msg_server start failed\n");
	}
	return 0;
}

//msg_client.c
#include"comm.h"

int _msg_id = -1;

int msg_client_end(int id);
int msg_client_start();
int msg_client_start()
{
	key_t _key = ftok(_FILEPATH_,ID);
	if(_key < 0)
	{
		print_log("client get key id error");
		return 1;
	}
	msginfo _cli_info;
	_msg_id = msgget(_key,0);
	if(_msg_id < 0)
	{
		print_log("msg_sever get key id failer\n");
		return 1;
	}
	while(1)
	{
		printf("client:>");
		fgets(_cli_info.mtext,sizeof(_cli_info.mtext),stdin);
		if(strncasecmp(_cli_info.mtext,"quit",4) == 0)
		{
			printf("client bye!\n");
			break;
		}
		_cli_info.mytype = g_cli_send_type;
		if(msgsnd(_msg_id,&_cli_info,sizeof(_cli_info),0) == -1)
		{
			printf("client send msg error\n");
			exit(0);
		} 
		memset(_cli_info.mtext,'\0',sizeof(_cli_info.mtext));
		if(msgrcv(_msg_id,&_cli_info,_MSG_SIZE_,g_ser_send_type,0) == -1)
		{	
			print_log("client recive msg error");
			return 1;
		}
	printf("server:>%s\n",_cli_info.mtext);
	}
	return 0;
}

int msg_client_end(int id)
{
	if(msgctl(id,IPC_RMID,NULL) == -1){
		return 1;
	}
	return 0; 
}

static void delete_msg()
{

	if(_msg_id != -1)
	{
		msg_client_end(_msg_id);
	}
	printf("delete msg queue end\n");
}

int main()
{
	atexit(delete_msg);
	if(msg_client_start() == 0)
	{
		print_log("msg_server start success\n");

	}
	else
	{
		print_log("msg_server start failed\n");
	}
	return 0;
}