zoukankan      html  css  js  c++  java
  • LINUX消息队列实战之一

        前言

        能说能抄能论皆不算,能写能打才是真功夫。

        唠叨

        反正我也是一个孤独的程序猿,多说一些奇奇怪怪的唠叨也无妨,第一次写消息队列,书本的东西和实战很不同,根据实战总结的一些注意事项会和大家分享,也敲打敲打自己,以后别总是想当然,要头顶蓝天,脚踩大地,做一个能文亦能武的敦厚男人。

        简介

        消息队列是linux提供的一种便利的IPC机制,不具有任何血缘关系的程序可以通过消息队列进行便利的通信:不同的程序通过同样的key访问同一个消息队列,支持不同优先级的消息队列,效率较高且使用便利。

        消息队列常用的几个API 

        ftok:根据指定文件和参数生成key

        msgget:创建或者附加一个消息队列

        msgctl:获取消息队列的信息或者设置消息队列的参数

        msgsnd:向消息队列中添加消息

        msgrcv:从消息队列获取一条消息

        发消息代码 

    #include <sys/types.h> 

    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <iostream>

    using namespace std;

    key_t key;
    #define MAX_TEXT 2000
    //定义消息收发结构体,第一个成员必定为long型,用以存储msgtype,第二个成员为char字符串,长度可以自己定义
    struct mymsg
    {
        long mtype;
        char mtext[MAX_TEXT];
    };


    int main()
    {
        //ftok的用法示例,在linux下使用ftok返回的key创建消息队列存在一些很奇怪的地方:不指定访问权限时msgget不返回错误,也没有创建消息队列
        
    //指定访问权限为0666时msgget返回错误并提示文件已存在,没有想明白为什么,网上也没有找到答案
        
    //这里仅示范下ftok的使用方法:返回值为key值,第一个参数为已经存在的文件,第二个参数为8位的数字0~255
        
    //if((key = ftok("./msg_server.cpp", 108)) == -1)
        
    //{
            
    //printf("fotk error, %s ", strerror(errno));
            
    //return -1;
        
    //}
        
    //printf("key %d ", key);
        
    //由于上面提到的原因,采用了指定一个随机key的方式进行创建,这次成功了
        
    //msgget用法:返回值为消息队列ID,第一个参数为key值,指定key未IPC_PRIVATE时将创建新的消息队列,无法创建时报错
        
    //第二个参数为flag,IPC_CREAT表示如不存在就创建新的消息队列,加上IPC_EXCL表示创建新的消息队列,无法创建时报错,通常要加上操作权限,比如0666
        int iMsgId = msgget((key_t)1828888, IPC_CREAT | 0666);
        //关于key的一点疑惑,在腾讯云上敲代码时部分key并不支持,用了一个较大的key去创建消息队列直接失败了

        if(iMsgId == -1)
        {
            cout << "iMsgId:" << iMsgId << endl;
            printf("create msgqueue failed, %s ", strerror(errno));
            return -1;
        }
        cout << "iMsgId:" << iMsgId << endl;

        int ret = 0;
        //获取消息队列的属性信息,msgctl传入IPC_STAT和msqid_ds对象指针
        msqid_ds info;
        memset(&info, 0sizeof(info));
        ret = msgctl(iMsgId, IPC_STAT, &info);
        if( ret == -1)
        {
            cout << "msgctl with IPC_STAT failed" << endl;
        }
        //下面是一个失败的尝试,企图将消息队列的大小扩大至6400000,但系统报错了,先记录下来
        info.msg_qbytes = 6400000;
        ret = msgctl(iMsgId, IPC_SET, &info);
        if(ret == -1)
        {
            cout << "msgctl with IPC_SET failed" << endl;
        }
        //linux下特有的支持,可以查看消息队列的系统属性,传入IPC_INFO和msginfo,相关信息将写入msginfo
        msginfo stinfo;
        ret = msgctl(iMsgId, IPC_INFO, (msqid_ds*)&stinfo);
        if(ret == -1)
        {
            cout << "msgctl with IPC_INFO failed" << endl;
        }
        else
        {
            cout << "msginfo.msgmax:" << stinfo.msgmax << endl;
            cout << "msginfo.msgmnb:" << stinfo.msgmnb << endl;
            cout << "msginfo.msgmni:" << stinfo.msgmni << endl;
        }

        mymsg msg;
        msg.mtype = 1;
        memset(msg.mtext, 11, MAX_TEXT-1);
        msg.mtext[1999] = '';
        int count = 0;
        while(count != 10000)
        {
            //向消息队列写入消息,如果成功,ipcs查看消息队列将会多一个消息
            
    //msgsnd详解:第一个参数为消息队列id,由msgget返回,第二个参数为要发送的消息指针,第三个参数为待发送消息的字符串变量长度,第三个参数为flag
            ret = msgsnd(iMsgId, (void*) &msg, MAX_TEXT, IPC_NOWAIT);
            if(ret == -1)
            {
                printf("msgsnd error, %s ", strerror(errno));
                if(errno == EACCES)
                    printf("errno == EACCES ");
                else if(errno == EAGAIN)
                    printf("errno == EAGAIN ");
                else if(errno == EFAULT)
                    printf("errno == EFAULT ");
                else if(errno == EIDRM)
                    printf("errno == EIDRM ");
                else if(errno == EINTR)
                    printf("errno == EINTR ");
                else if(errno == EINVAL)
                    printf("errno == EINVAL ");
                else if(errno == ENOMEM)
                    printf("errno == ENOMEM ");
                else
                    printf("errno == E2BIG ");
                break;
            }
            count++;
        }
        getchar();
    }

        收消息代码

    #include <sys/types.h> 

    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <iostream>
    #include <unistd.h>

    using namespace std;

    key_t key = 0x31a3852;
    #define MAX_TEXT 2000

    struct mymsg
    {
        long mtype;
        char mtext[MAX_TEXT];
    } msg;



    int main()
    {
        //同server相对,指定同样的key来获取同一个消息队列的访问权限,这里没有指定IPC_CREAT
        int msqid = msgget((key_t)1828888, IPC_NOWAIT | 0666);
        if(msqid == -1)
        {
            printf("msgget failed, %s ", strerror(errno));
            return -1;
        }
        cout << "msqid:" << msqid << endl;
        int count = 0;
        int ret = 0;
        while(1)
        {
            //第一参数指定消息队列ID,第二参数指定消息存储对象指针,第三参数指定消息存储字符成员的长度,第四参数指定要收取的消息类型即优先级,0表示所有优先级,第五参数为flag
            ret = msgrcv(msqid, &msg, MAX_TEXT, 0, IPC_NOWAIT);
            if(ret == -1)
            {
                printf("msgrcv failed, %s ", strerror(errno));
                usleep(100000);
            }
            else
            {
                count++;
                printf("recv a msg, %s ", msg.mtext);
            }
        }
    }

         小结

        消息队列使用比较简单,但存在一些限制,仅凭道听途说怕是不能真正掌握。 

  • 相关阅读:
    Java 实现 蓝桥杯 生兔子问题
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 LeetCode 33 搜索旋转排序数组
    Java实现 LeetCode 33 搜索旋转排序数组
    Java实现 LeetCode 33 搜索旋转排序数组
    深入探究VC —— 资源编译器rc.exe(3)
    深入探究VC —— 编译器cl.exe(2)
    深入探究VC —— 编译器cl.exe(1)
  • 原文地址:https://www.cnblogs.com/learn-my-life/p/3890449.html
Copyright © 2011-2022 走看看