Linux-进程间通信(二、消息队列)

####LINUX进程间通信(一、命名管道与匿名管道)

####消息队列概述

#####消息队列是消息的链接表,存放在内核中并由消息队列标识符表示。主要涉及到msgget(),msgsnd(),msgrcv(),msgctl()四个函数,在具体使用时可将四个函数再次封装进行使用。


####消息队列特点:

#####1、由于是一个链接表,所以消息队列是一个全双工的通信方式,两端都可读可写;
#####2、对于进程无关系要求(pipe要求必须有亲缘关系,FIFO无要求);
#####3、内嵌同步互斥机制;
#####4、生命周期随内核,也就是说当进程结束,所创建的消息队列还存在于内核中,可通过ipcs -q命令查看此时存在的消息队列信息;所以在程序结束时记得删除消息队列,否则要不重启之后消失,要不就在外侧通过命令删除,但是拿命令删除万一误删就又出麻烦了。。


####关于消息队列的命令:

#####1、ipcs -q 查看当前所创建的消息队列信息;
#####2、ipcrm -q +msgid 删除指定msgid的消息队列
#####3、ipcrm -a 删除当前所有的消息队列


####消息队列结构
这里写图片描述


####消息队列函数:

msgget用于创建一个新队列或打开一个现存的队列(取决于参数flag);
msgsnd将新消息添加到队列尾端,每个消息包含一个正长整型类型字段,一个非负长度以及实际数据字节,所有这些都在将消息添加到队列时,传送给msgsnd。
msgrcv用于从队列中取消息。
msgctl用于对队列执行多种操作。


####一、msgget

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgget(key_t key, int msgflg);
      
   //返回值:成功返回msgid(消息队列标识符),失败返回-1    

#####参数介绍:
#####①key:消息队列的名字:由ftok生成

       #include <sys/types.h>
       #include <sys/ipc.h>

       key_t ftok(const char *pathname, int proj_id);

#####给定一个路径,给定一个整数,生成一个对应的key,拿到对应的key就可生成对应的msgid。也就是说,当两个进程拿到相同的pathname和proj_id时,那么他们所获取到的msgid也就相同,访问到的消息队列也就是同一个。


#####②msgflg:由九个权限标志组成,用法和创建文件使用的mode模式相同。
#####注: IPC_CREAT 如果消息队列不存在,则创建一个消息队列,否则打开操作。IPC_EXCL 只有在消息队列不存在的时候,新的消息队列才建立,否则就产生错误。所以在后面的示例中创建一个文件的时候只需要使用IPC_CREAT,而打开对应消息队列时需要或上IPC_EXCL


####二、msgctl

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgctl(int msqid, int cmd, struct msqid_ds *buf);

//返回值:成功返回0;操作失败返回-1

#####参数介绍
#####①msqid:由于我们是在对某个消息队列进行操作,那么肯定要指定是哪一个消息队列,所以首先给定的就是msgid,让内核知道我们是在对谁进行操作;
#####②cmd:说明对由msgid指定的消息队列要执行的操作:主要包含以下三种:

IPC_STAT:取此队列的msgid_ds结构,并将它存放在buf指向的结构中;

#####IPC_SET:按由buf指向结构中的值,设置此队列中相关结构对应的字段;
#####IPC_RMID:从系统中删除该消息队列以及仍在该消息队列中的所有数据。
#####③struct msqid_ds* buf:给定的msqid结构体类型的指针,用于对应cmd操作中所要修改的值。


####三、msgsnd

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

//返回值:成功返回0,失败返回-1

#####参数介绍
#####①msqid:由msgget函数返回的消息队列标识码;
#####②msgp:一个指向要准备发送消息的指针;
#####③msgsz:msgp指向的消息长度;
#####④msgflg:控制参数,具体自行man


#####注:man手册中写的很清楚,上面的msgp是一个指向如下结构体类型的结构体指针,mtype可以用于指定收发消息的来源(可用枚举或宏定义将不同的人定义为不同的值,但是注意must be > 0);mtext则为我们存放消息的数组,数组的大小是可以改的,这也就是msgsz的用处。

这里写图片描述


####四、msgrcv

       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
       
     ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);
//返回值:成功返回拷贝到对应mtext数组中的字符个数,失败返回-1

#####参数介绍:
#####①msqid:msgget函数返回的消息队列标识码;
#####②msgp:一个指向上msgbuf结构体类型的结构体指针;
#####③msgsz:msgp执行的消息长度;
#####④msgtype:根据msgtype的取值,进行不同的操作;
#####⑤msgflag:控制参数。


#####注:从man手册中可看出:参数type使我们可以指定想要哪一种消息。

//msgtype的取值带来的影响
msgtype=0:返回队列第一条信息
msgtype>0:返回队列第一条类型等于msgtype的消息
msgtype<0:返回队列第一条类型小于等于msgtype的绝对值的消息,并且是满足条件的消息类型最小的消息

//msgflg的取值
msgflg = IPC_NOWAIT:队列没有可读消息不等待,返回ENOMSG错误;
msgflg = MSG_NOERROR:消息大小超过msgsz时被截断
msgtype>0 且 msgflg=MSG_EXCEPT:接收类型不等于msgtype的第一条消息。


####示例:使用消息队列再次完成sercer/client之间的通信,server端在收到client端发送的消息后,做一应答;完成三次对话之后删除销毁消息队列。

/**********comm.h************/
#pragma once

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#define PATHNAME "."
#define PROJ_ID 0x666

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbuf{
	long mytype;
	char mtext[1024];
};



int creatMsgQueue();

int getMsgQueue();

int destroyMsgQueue(int msgid);

int sendMsg(int msgid, int who, char* msg);
				
int recvMSg(int msgid, int recvType, char out[]);



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


int CommMsgQueue(int flags)
{
	key_t _key = ftok(PATHNAME,PROJ_ID);
	//failed return -1,
	if(_key < 0){
		perror("ftok");
		return -1;
	}

	//msgget(key,msg_flag)
	int msgid = msgget(_key,flags);
	if(msgid < 0){
		perror("msgget");
	}
	
	return msgid;
}

int creatMsgQueue()
{
	return CommMsgQueue(IPC_CREAT | IPC_EXCL | 0666);
}

int getMsgQueue()
{
	return CommMsgQueue(IPC_CREAT);
}

int destroyMsgQueue(int msgid)
{
	// int msgctl(int msqid, int cmd, struct msqid_ds *buf);
	if(msgctl(msgid,IPC_RMID,NULL) < 0){
		perror("msgctl");
		return -1;
	}

	return 0;
}

int sendMsg(int msgid, int who, char* msg)
{
	//int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
	struct msgbuf buf;
	buf.mytype = who;
	strcpy(buf.mtext, msg);

	if(msgsnd(msgid, (void*)&buf,sizeof(buf.mtext),0) < 0){
		perror("msgsnd");
		return -1;
	}
	return 0;
}

int recvMsg(int msgid, int recvType, char out[])
{
	//ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
	//                      int msgflg);
	struct msgbuf buf;
	if(msgrcv(msgid, (void*)&buf, sizeof(buf.mtext), recvType, 0) < 0){
		perror("msgrcv");
		return -1;
	}
	strcpy(out, buf.mtext);
	return 0;
}


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

int main()
{
	int msgid = getMsgQueue();

	char buf[1024] = {0};
	while(1){
		printf("please enter : ");
		fflush(stdout);
		ssize_t s = read(0, buf, sizeof(buf)-1);
		if(s > 0){
			buf[s] = 0;
			if(sendMsg(msgid,CLIENT_TYPE,buf) == -1){
				return -1;
			}
			printf("send done, wait recv...\n");
		}else{
			printf("client close\n");
			break;
		}
		
		if(recvMsg(msgid, SERVER_TYPE, buf) == -1){
			return -1;
		}
		printf("server : %s",buf);
	}
	return 0;
}


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

int main()
{
	int msgid = creatMsgQueue();

	char buf[1024] = {0};
	int count = 3;
	while(count--){
		if(recvMsg(msgid, CLIENT_TYPE, buf) == -1){
			perror("recvMsg");
			break;
		}
		printf("client : %s",buf);
	
		printf("please enter : ");
		fflush(stdout);
		ssize_t s = read(0,buf,sizeof(buf)-1);
		if( s > 0){
			buf[s] = 0;
			sendMsg(msgid, SERVER_TYPE, buf);
			printf("send done, wait recv...\n");
		}
	}

	destroyMsgQueue(msgid);
	return 0;
}

/************Makefile**************/
.PHONY : all
all : server client

server : server.c comm.c
	gcc $^ -o $@

client : client.c comm.c
	gcc $^ -o $@

.PHONY : clean
clean:
	rm server client
	


#####运行结果:当server端输入三次后,主动销毁了消息队列,导致client端在第四次发送时发送失败,报错终止。
这里写图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值