linux进程间通讯之消息队列

文件名到键值

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char*pathname, char proj);

它返回与路径pathname相对应的一个键值。该函数不直接对消息队列操作,但在调用msgget()来获得消息队列描述字前,往往要调用该函数.

消息队列API

  系统消息队列API共有四个,使用时需要包括几个头文件:

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

1)int msgget(key_t key, int msgflg)

参数key是一个键值,由ftok获得;msgflg参数是一些标志位。该调用返回与健值key相对应的消息队列描述字。

在以下两种情况下,该调用将创建一个新的消息队列:

  • 如果没有消息队列与健值key相对应,并且msgflg中包含了IPC_CREAT标志位;
  • key参数为IPC_PRIVATE;

参数msgflg可以为以下:IPC_CREAT、IPC_EXCL、IPC_NOWAIT或三者的或结果。

调用返回:成功返回消息队列描述字,否则返回-1。

注:参数key设置成常数IPC_PRIVATE并不意味着其他进程不能访问该消息队列,只意味着即将创建新的消息队列。

2)int msgrcv(int msgid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);

该系统调用从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的msgbuf结构中。

msqid为消息队列描述字;消息返回后存储在msgp指向的地址,msgsz指定msgbuf的mtext成员的长度(即消息内容的长度),msgtyp为请求读取的消息类型;读消息标志msgflg可以为以下几个常值的或:

  • IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时,errno=ENOMSG
  • IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为msgtyp的消息
  • IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的msgsz字节,则把该消息截断,截断部分将丢失。

msgrcv手册中详细给出了消息类型取不同值时(>0; <0; =0),调用将返回消息队列中的哪个消息。

msgrcv()解除阻塞的条件有三个:

  1. 消息队列中有了满足条件的消息;
  2. msqid代表的消息队列被删除;
  3. 调用msgrcv()的进程被信号中断;

调用返回:成功返回读出消息的实际字节数,否则返回-1。

3)int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);

向msgid代表的消息队列发送一个消息,即将发送的消息存储在msgp指向的msgbuf结构中,消息的大小由msgze指定。

对发送消息来说,有意义的msgflg标志为IPC_NOWAIT,指明在消息队列没有足够空间容纳要发送的消息时,msgsnd是否等待。造成msgsnd()等待的条件有两种:

  • 当前消息的大小与当前消息队列中的字节数之和超过了消息队列的总容量;
  • 当前消息队列的消息数(单位"个")不小于消息队列的总容量(单位"字节数"),此时,虽然消息队列中的消息数目很多,但基本上都只有一个字节。

msgsnd()解除阻塞的条件有三个:

  1. 不满足上述两个条件,即消息队列中有容纳该消息的空间;
  2. msqid代表的消息队列被删除;
  3. 调用msgsnd()的进程被信号中断;

调用返回:成功返回0,否则返回-1。

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

该系统调用对由msqid标识的消息队列执行cmd操作,共有三种cmd操作:IPC_STAT、IPC_SET 、IPC_RMID。

  1. IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在buf指向的msqid结构中;
  2. IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储在buf指向的msqid结构中;可设置属性包括:msg_perm.uid、msg_perm.gid、msg_perm.mode以及msg_qbytes,同时,也影响msg_ctime成员。
  3. IPC_RMID:删除msqid标识的消息队列;

调用返回:成功返回0,否则返回-1。

例子:

TestQueueClient.cpp

#include "msgqueue.h"
#include <stdio.h>
#include <pthread.h>
int msgid = 0;
IpcMsgQueue msgQueue;
void* Receive(void* arg){
	char buff[1024] = {0};
	fflush(stdout);
	msgQueue.ReceiveMessage(msgid,buff,_SERVER_);
	printf("Server#%s\n",buff);
	return 0;
}

void* Send(void* arg){
	char buff[1024] = {0};
	printf("Client#");
	fflush(stdout);
	ssize_t s = read(1,buff,sizeof(buff));
	buff[s-1] = 0;
	msgQueue.SendMessage(msgid,buff,_CLIENT_);
	return 0;
}

int main(){
	key_t key = ftok(PATHNAME,PROJNAME);
	if(key < 0){
		perror("ftok");
		return 1;
	}
	msgid = msgQueue.GetMessage(key);
	pthread_t c1,c2;
	for(int i=0; i<10; ++i){
		pthread_create(&c1,NULL,Send,NULL);
		pthread_create(&c2,NULL,Receive,NULL);
		pthread_join(c1,NULL);
		pthread_join(c2,NULL);
	}
	return 0;
}

TestQueueServer.cpp

#include "msgqueue.h"
#include <stdio.h>
#include <pthread.h>
int msgid = 0;
IpcMsgQueue msgQueue;
void* Receive(void* arg){
	char buff[1024] = {0};
	msgQueue.ReceiveMessage(msgid,buff,_CLIENT_);
	printf("Client#%s\n",buff);
	return 0;
}

void* Send(void* arg){
	char buff[1024] = {0};
	printf("Server#");
	fflush(stdout);
	ssize_t s = read(1,buff,sizeof(buff));
	buff[s-1] = 0;
	msgQueue.SendMessage(msgid,buff,_SERVER_);
	return 0;
}

int main(){
	key_t key = ftok(PATHNAME,PROJNAME);
	if(key < 0){
		perror("ftok");
		return 1;
	}
	msgid = msgQueue.CreateMessage(key);
	pthread_t c1,c2;
	for(int i=0; i<10; ++i){
		pthread_create(&c1,NULL,Send,NULL);
		pthread_create(&c2,NULL,Receive,NULL);
		pthread_join(c1,NULL);
		pthread_join(c2,NULL);
	}
	msgQueue.DestoryMessage(msgid);
	return 0;
}

msgqueue.h

#ifndef __PUB_MSGQUEUE_H__
#define __PUB_MSGQUEUE_H__
/*
	desc:进程间通讯--消息队列
	writer:Lake
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include "../Log.h"

#define _SERVER_ 1
#define _CLIENT_ 2
#define PATHNAME "/"
#define PROJNAME 0

//消息结构体
struct IpcMsgbuf
{
        long mtype;      //消息类型,由用户自定义
        char mtext[1024];//发送的消息(长度、类型可以自行指定)
};
//进程间消息队列通讯类
class IpcMsgQueue
{
	public:
		IpcMsgQueue()
		{
			
		}
		virtual ~IpcMsgQueue()
		{
			
		}
	public:
		//创建与打开消息队列公共函数
		int MessageCommon(key_t key,int flag);
		//创建全新的消息队列(服务端)
		int CreateMessage(key_t qid);
		//打开已有的消息队列(客户端)
		int GetMessage(key_t qid);
		//发送消息
		void SendMessage(int msgid,const char* msg,int who);
		//接收消息
		void ReceiveMessage(int msgid,char* msg,int who);
		//消息队列的删除
		void DestoryMessage(int msgid);
};

#endif

msgqueue.cpp

#include "msgqueue.h"

//创建与打开消息队列公共函数
int IpcMsgQueue::MessageCommon(key_t key,int flag)
{
    int ret = 0;
    if((ret=msgget(key,flag))==-1){
        //LOG (LOG_ERROR, __FILE__, __LINE__, "MessageCommon--error[%s]",strerror(errno));
    }
    return ret;
}
//创建全新的消息队列(服务端)
int IpcMsgQueue::CreateMessage(key_t qid)
{
    //消息队列也是具有权限结构的,因此在创建时给666权限
    return MessageCommon(qid,IPC_CREAT|IPC_EXCL|0666);
}
//打开已有的消息队列(客户端)
int IpcMsgQueue::GetMessage(key_t qid)
{
    return MessageCommon(qid,IPC_CREAT);
}
//发送消息
void IpcMsgQueue::SendMessage(int msgid,const char* msg,int who)
{
    IpcMsgbuf buf;
    buf.mtype = who;                //消息类型
    strcpy(buf.mtext,msg);        //消息内容
    if(msgsnd(msgid,&buf,sizeof(buf.mtext),0) == -1){
    	//LOG (LOG_ERROR, __FILE__, __LINE__, "SendMessage--error[%s]",strerror(errno));
        DestoryMessage(msgid);
    }
}
//接收消息
void IpcMsgQueue::ReceiveMessage(int msgid,char* msg,int who)
{
    IpcMsgbuf buf;
    if(msgrcv(msgid,&buf,sizeof(buf.mtext),who,0)==-1){
    	//LOG (LOG_ERROR, __FILE__, __LINE__, "SendMessage--error[%s]",strerror(errno));
        DestoryMessage(msgid);
    }
    strcpy(msg,buf.mtext);
}
//消息队列的删除
void IpcMsgQueue::DestoryMessage(int msgid)
{
    if(msgctl(msgid,IPC_RMID,NULL) == -1){
        //LOG (LOG_ERROR, __FILE__, __LINE__, "DestoryMessage--error[%s]",strerror(errno));
    }
}

Makefile

INCLUDE = -I ../ -I ../../
LIB = -lpthread
CLIENTOBJS = TestQueueClient.cpp ../msgqueue.o
SERVEROBJS = TestQueueServer.cpp ../msgqueue.o
G = g++
CFLAGS = -Wall -O -g

all:Client Server

Client:$(CLIENTOBJS)
	$(G) -o Client $(CLIENTOBJS) $(INCLUDE) $(LIB)
Server:$(SERVEROBJS)
	$(G) -o Server $(SERVEROBJS) $(INCLUDE) $(LIB)
msgqueue.o:
	$(G) $(CFLAGS) -c  $(INCLUDE) -o msgqueue.o msgqueue.cpp
clean:
	rm -f Client Server
	rm -f ../*.o
makefile,TestQueueClient.cpp,TestQueueServer.cpp文件,放在同msgqueue.h,msgqueue.cpp目录test文件夹下,即可完成编译测试.

猜你喜欢

转载自blog.csdn.net/lake1314/article/details/80050378