进程间通信之消息队列

什么是消息队列?

通俗来讲就是内核提供了一个链表,基于这个链表实现了一个有类型的队列。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。 每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

消息队列的特点

1.可作用与任意进程
2.自带同步互斥机制(面向数据块)
3.双向全双工(链表结构两端可读可写)
4.生命周期随内核(系统重启可删除)

消息队列与命名管道的差异

消息队列进行通信的进程可以是不相关的进程,同时它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用write,接收数据用read,则在消息队列中,发送数据用msgsnd,接收数据用msgrcv。而且它们对每个数据都有一个最大长度的限制。
与命名管道相比,消息队列的优势在于,1、消息队列也可以独立于发送和接收进程而存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难。2、同时通过发送消息还可以避免命名管道的同步和阻塞问题,不需要由进程自己来提供同步方法。3、接收程序可以通过消息类型有选择地接收数据,而不是像命名管道中那样,只能默认地接收

消息队列常用函数

这里写图片描述

1、msgget

   功能:创建消息队列

   头文件:#include <sys/types.h>

   #include <sys/msg.h>

   #include <sys/ipc.h>

   原型:int msgget(key_t key,int msgflg)

   说明:key为键值,可设置成常数IPC_PRIVATE,或由ftok获取;

 msgflg是标志位,设定的权限,标志位如下:IPC_CREAT,创建新的队列;

    IPC_EXCL,与IPC_CREAT一起使用,表示如果要创建的消息队列已经存在,则返
   回错误;
   IPC_NOWAIT,读写消息队列要求无法达到满足时,立即返回,不会出现堵塞

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

   返回值:成功返回消息队列描述字;

   失败返回-1.
2、ftok

   功能:将文件名转换成键值

   头文件:#include <sys/types.h>

   #include <sys/ipc.h>

   原型:key_t ftok(char *pathname,char proj)

   说明:pathname,路径名

 proj,项目名,不为0即可

   返回值:成功,返回与文件相对应的键值;

   失败,返回-1.
3.msgctl
功能:消息队列的控制函数
原型:
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
参数:
msqid:有msgget函数返回的消息队列标识码
cmd:是将要采取的动作(IPC_STAT,读取消息队列的数据结构msqid_ds,并将其存储在b u f指定的地址中。
     IPC_SET,设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
     IPC_RMID,从系统内核中移走消息队列。)
     返回值:
     成功返回0,失败返回-1
4.msgsnd
功能:把一条消息添加到消息队列中
原型:
int msgsnd(int msqid, const void *msgp,size_t msgsz,int msgflg);
参数:
msgid:由msgget函数返回的消息队列标识码
msgp:是一个指针,指向准备发送的消息
msgsz:是msgp指向的消息的长度
msgflg:控制着当前消息队列满或到达系统上限时将要发生的事
msgflg=IPC_NOWAIT表示消息队列满不等待,返回eagain
返回值:
成功返回0,失败返回-1
5.msgrcv
功能:是从一个消息队列接收消息
原型:
sszize_t msgrcv(int msqid, void *msgp,size_t msgsz,long msgtyp, int msgflg);
参数
msgid:由msgget函数返回的消息队列标识码
msgp:是一个指针,指向准备接收的消息
msgsz:是msgp指向的消息的长度,这个长度不含保存消息类型的那个long int长整形
msgtype:它可以实现接收有限中级的简单形式
msgfig:控制着队列中没有相应类型的消息可供接收时将要发生的事
返回值:
成功返回实际放到接收缓存区里去的字符个数,失败返回-1
说明
1.消息队列的结构在两方面受到制约
    (1)它必须是小于系统规定的上限值
    (2)它必须以一个long int 长整形数开始,接收者函数利用这个长整形确定消息的类型
2.消息队列的参考结构:
 struct msgbuf{
     long mtype;
     char mtext;
 }

使用消息队列完成server与client之间的通信

common.h
#pragma one

#define PATHNAME "."
#define PROJ_ID 0x666

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

//创建爱你一个消息队列
//如果已经存在说明调用失败
int Createmsg();

int Getmsg();//打开一个消息队列 如果不存返回失败;

int Destroymsg(int msgid);//销毁

int Sendmsg(int msgid,int who,char*msg);//向消息队列中发数据

int Recvmsg(int msgid,int recvtype,char out[]);//从消息队列中读
common.c
#include<stdlib.h>
#include<string.h>
#include"common.h"
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdio.h>

int Commonmsg(int flags)
{

    key_t key=ftok(PATHNAME,PROJ_ID);
    if(key==-1){
        perror("ftok");
        return -1;
    }
   int msgid= msgget(key,flags);
   if(msgid<0)
   {
       perror("msgget");
       return -1;
   }

   return msgid;
}
int Createmsg(){

    return Commonmsg(IPC_CREAT | IPC_EXCL | 0666);
}

int Getmsg()
{
   return Commonmsg(IPC_CREAT);
}

//IPC_STAT  把msqid_ds结构体中的数据设置为消息队列当前的关联值
//IPC_RMID 删除消息队列
//IPC_SET  进程有足够权限的条件下  把消息队列的关联值设置为msqid_ds数据结构中给出的值
int Destroymsg(int msgid)
{
   if(msgctl(msgid,IPC_RMID,NULL)<0){
       perror("msgctl");
       return -1;
   }
   return 0;
}


int Sendmsg(int msgid,int who,char* msg)//向消息队列中发数据
{    
    struct msgbuf buf;
    buf.mtype = 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[])//从消息队列中读
{
    struct msgbuf buf;
    if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvtype,0)<0){
        perror("msgrcv");
        return -1;

    }
    strcpy(out,buf.mtext);
    return 0;

}
server.c
#include<stdio.h>
#include<unistd.h>
#include"common.h"
int main()
{
    int msgid=Createmsg();
    char buf[1024];
    while(1){
        buf[0]=0;
        Recvmsg(msgid,2,buf);
        printf("client# %s\n",buf);

        printf("please Enter# ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf));
                if(s>0){
                    buf[s-1]=0;
                    Sendmsg(msgid,1,buf);
                    printf("send done, wait recv...\n");
                }
    }
    Destroymsg(msgid);

    return 0;
}
client.c
#include<stdio.h>
#include<unistd.h>
#include"common.h"

int main()
{

    int msgid=Getmsg();
    char buf[1024];
    while(1){
        buf[0]=0;
        printf("please Enter# ");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf));
                if(s>0){
                    buf[s-1]=0;
                    Sendmsg(msgid,1,buf);
                    printf("send done, wait recv...\n");
                }
                Recvmsg(msgid,1,buf);
                printf("server# ",buf);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值