进程间通信 - 消息队列

消息队列                                                                         
实际上是内核中的一个链表
  • 消息队列提供了从一个进程向另一个进程发送一块数据的方法。
  • 每一个数据都被认为有一个类型,接受的进程可以接受多种,单一次只能接受一个自己指定的类型。
  • 消息队列最大长度有上限(MSGMAX),每个消息队列的字节有上限(MSGMNB),消息队列的总是有上限(MSGMIN)。
可在Linux下的 /usr/include/linux/msg.h查看消息队列结构

消息队列在内核中的表示

msgget函数 - 创建访问消息队列

int msgget(key_t key,int msgflg);
参数:
    key:一个消息队列的名字
    msgflg:权限
返回值:
    成功返回消息队列的标识码,给后续的msgctl,msgsen,msgrcv使用
    失败返回 -1

msgflg:
    IPC_CREAT:不存在就创建,存在就打开
    IPC_EXCL : 加上这个选项,存在就打开失败

key值的产生:
    
key_t ftok(const char* pathname,int pro_id);
参数 : 
    pathname :是一个路径,只要确保是当前系统中一个存在的目录即可
    pro_id :表示同一项目中不同的消息队列
返回值:
    成功返回非负数
    失败返回小于0的值

msgctl 函数 - 控制消息队列

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

参数 : 
    msqid :由msgget所获得的标识码
    cmd : 相应操作

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

cmd有三值可取,我们最经常用的是 IPC_RMID,意义为删除消息队列,且不需要函数的第三个参数。


msgsnd函数 - 添加一条消息到消息队列中

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数 
    msgid: 由msgget函数返回的消息队列标识码 
    msgp:是⼀个指针,指针指向准备发送的消息
    msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型
    msgflg:控制着当前消息队列满或到达系统上限时将要发⽣的事情 msgflg=IPC_NOWAIT表⽰队列满不等待,返回EAGAIN错误。 
返回值:成功返回0;失败返回-1

消息结构体的形式如下
struct msgbuf
{
    long mtype; // 表示当前消息的类型
    char mtext[1]; //表示当前下次的内容,这里的1没有意义,大小需要根据需要来设定
}

msgrcv函数 - 从消息队列接收消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 
参数 
    msgid: 由msgget函数返回的消息队列标识码 
    msgp:是⼀个指针,指针指向准备接收的消息
    msgsz:是msgp指向的消息⻓度,这个⻓度不含保存消息类型的那个long int⻓整型 
    msgtype:它可以实现接收优先级的简单形式 
    msgflg:控制着队列中没有相应类型的消息可供接收时将要发⽣的事 
返回值:成功返回实际放到接收缓冲区⾥去的字符个数,失败返回-1

代码实现service和client的交互

//com.h 此次交互所涉及的动作
#pragma once
#include <sys/types.h>
#include <sys/ipc.h>

#define CLIENT_TYPE 1
#define SERVER_TYPE 2

typedef struct msgbuf
{
    long mtype;
    char mtest[128];
}msgbuf;


int CreateMsgQueue();

int OpenMsgQueue();

void DestoryMsgQueue();

void SendMsgQueue(int msgid,int type,char* msg);

void ReceiveMsgQueue(int msgid,int type,char out[]);
//com.c
#include "com.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <string.h>


int Commend(int flags)
{
    key_t key = ftok(".",1);
    if(key < 0)
    {
        perror("ftok");
        exit(1);
    }
    int msgid = msgget(key,flags);
    if(msgid < 0)
    {
        perror("magget");
        exit(1);
    }
    return msgid;
}

int CreateMsgQueue()
{
    return Commend(IPC_CREAT | IPC_EXCL | 0666);
}

int OpenMsgQueue()
{
    return Commend(IPC_EXCL);
}

void DestoryMsgQueue()
{
    int msgid = OpenMsgQueue();
    if((msgctl(msgid,IPC_RMID,NULL)) < 0)
    {
        perror("msgctl");
        exit(1);
    }
}


void SendMsgQueue(int msgid,int type,char* msg)
{
    msgbuf buf;
    buf.mtype = type;
    strcpy(buf.mtest,msg);

    if(msgsnd(msgid,&buf,sizeof(buf.mtest),0) < 0)
    {
        perror("msgsnd");
        exit(1);
    }
}

void ReceiveMsgQueue(int msgid,int type,char out[])
{
    msgbuf buf;
    if(msgrcv(msgid,&buf,sizeof(buf.mtest),type,0) < 0)
    {
        perror("msgrcv");
        exit(1);
    }
    strcpy(out,buf.mtest);
}
service接收客户端发来的消息,并对其进行相应
//service.c
#include "com.h"
#include <stdio.h>
#include <unistd.h>

int main()
{
    int msgid = CreateMsgQueue();
    char buf[1024] = {0};
    while(1)
    {
        ReceiveMsgQueue(msgid,CLIENT_TYPE,buf);
        printf("%s\n",buf);

        printf("server >");
        fflush(stdout);
        read(0,buf,sizeof(buf));
        SendMsgQueue(msgid,SERVER_TYPE,buf);
    }
    DestoryMsgQueue();
    return 0;
}

client想服务器发送消息,并相应服务器发回的消息

#include "com.h"
#include <stdio.h>
#include <unistd.h>

int main()
{
    int msgid = OpenMsgQueue();
    char buf[1024] = {0};
    while(1)
    {
        printf("client > ");
        fflush(stdout);
        read(0,buf,sizeof(buf));
        SendMsgQueue(msgid,CLIENT_TYPE,buf);

        ReceiveMsgQueue(msgid,SERVER_TYPE,buf);
        printf("%s\n",buf);
    }
    return 0;
}

客户端发送请求并等待服务器响应

服务器端接收客户端消息并相应客户端

消息队列总结                                                                     
  • 消息队列适用于任何两进程交互
  • 生命周期随内核,进程结束时不会自动销毁,可以使用函数(msgctl)删除,也可以使用命令 ipcrm -q [msqid] 删除。
  • 面向数据报,一次发送一个节点,一次也必须接收一个节点。
  • 消息队列内嵌同步互斥机制
  • 消息队列是全双工通信


猜你喜欢

转载自blog.csdn.net/J4Ya_/article/details/80558537