消息队列的应用及自己封装的消息队列类

需要包含的头文件:
#include <sys/msg.h>
#include <sys/ipc.h>

消息队列用到的几个函数:
一、创建或者访问一个消息队列
int msgget(key_t key, int msgflg);
参数key:
    程序必须提供一个key值Key可以为IPC_PRIVATE创建一个私有队列,该队列理论上只可以为当前
    进程访问
参数msgflg:
    由9个权限标记组成,设置IPC_CREAT表示创建或者打开一个存在的消息队列

返回值:
    如果成功,msgget函数会返回一个正数作为队列标识符,如果失败则会返回-1。

二、添加消息到队列
int msgsnd(int msqid, const void *msg_ptr, size_t msg_sz, int msgflg);
消息结构由两种方式来限定。
第一,他必须小于系统限制
第二,必须以long int开始,这在接收函数中会用作一个消息类型
如下形式来定义消息结构:struct my_message {     long int message_type;     /* The data you wish to transfer */}

参数1 msgid:  是由msgget函数所返回的消息队列标识符。

参数2 msg_ptr:是一个指向要发送消息的指针,正如前面所描述的,这个消息必须以long int类型开始。

参数3 msg_sz: 是由msg_ptr所指向的消息的尺寸。这个尺寸必须不包含long int消息类型。

参数4 msgflg: 控制如果当前消息队列已满或是达到了队列消息的系统限制时如何处理。
           如果msgflg标记设置了IPC_NOWAIT,函数就会立即返回而不发送消息,并且返回值为-1。

           如果msgflg标记清除了IPC_NOWAIT标记,发送进程就会被挂起,等待队列中有可用的空间。

          如果msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式


返回值:如果成功,函数会返回0,如果失败,则会返回-1。如果调用成功,系统就会复制一份消息数据并将其放入消息队列中

三、从消息队列中接受消息
int msgrcv(int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg);

参数1 msqid:  是由msgget函数所返回的消息队列标记符。

参数2 msg_ptr:是一个指向将要接收消息的指针,正如在msgsnd函数中所描述的,这个消息必须以long int类型开始。

参数3 msg_sz: 是由msg_ptr所指向的消息的尺寸,并不包含long int消息类型。

参数4 msgtype:是一个long int类型,允许一个接收优先级形式的实现。

           如果msgtype的值为0,队列中第一个可用的消息就会被接收。
           如果其值大于0,具有相同消息类型的第一个消息就会被接收。如果其值小于0,第一个具有相同类型或是小于msgtype绝对值的消息就会被接收。
参数5 msgflg: 控制当没有合适类型的消息正在等待被接收时或消息队列为空时如何处理。
               如果在msgflg中设置了IPC_NOWAIT位,调用就会立即返回,而返回值为-1,并设定错误码为ENOMSG

               如果msgflg标记中消除了IPC_NOWAIT位,进程就会被挂起,等待一个合适类型的消息到来。

               如果msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式

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

如果成功,msgrcv会返回放入接收缓冲区中的字节数,消息会被拷贝到由msg_ptr所指向的用户分配缓冲区中,而数据就会由消息队列中删除。如果失败则会返回-1。

四、消息队列控制函数
int msgctl(int msqid, int command, struct msqid_ds *buf);

msqid_ds结构至少包含下列成员: struct msqid_ds {     uid_t msg_perm.uid;     uid_t msg_perm.gid     mode_t msg_perm.mode;}

参数1 msqid:   是由msgget函数所返回的标记符。

参数2 command: 是要执行的动作。

他可以取下面三个值:

命令                 描述

IPC_STAT    设置msqid_ds结构中的数据来反射与消息队列相关联的值。

IPC_SET     如果进程有权限这样做,这个命令会设置与msqid_ds数据结构中所提供的消息队列相关联的值。

IPC_RMID    删除消息队列。 返回值:如果成功则会返回0,如果失败则会返回-1。

当进程正在msgsnd或是msgrcv函数中等待时如果消息队列被删除,发送或接收函数就会失败。

  

消息队列发送:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

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

#define MAX_TEXT 512

struct my_msg_st
{
    long int my_msg_type;
    char some_text[MAX_TEXT];
};

int main()
{
    int running = 1;
    struct my_msg_st some_data;
    int msgid;
    char buffer[BUFSIZ];

    msgid = msgget((key_t)1234, 0666|IPC_CREAT);

    if(msgid==-1)
    {
        fprintf(stderr,"msgget failed with errno: %d\n", errno);
        exit(EXIT_FAILURE);
    }

    while(running)
    {
        printf("Enter some text: ");
        fgets(buffer, BUFSIZ, stdin);
        some_data.my_msg_type = 1;
        strcpy(some_data.some_text, buffer);

        if(msgsnd(msgid, (void *)&some_data, MAX_TEXT, IPC_NOWAIT)==-1)
        {
            fprintf(stderr, "msgsnd failed\n");
            perror("sendmsg");
            exit(EXIT_FAILURE);
        }

        if(strncmp(buffer, "end", 3) == 0)
        {
            running = 0;
        }
    }

    exit(EXIT_SUCCESS);
}



消息队列接受:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

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

struct my_msg_st
{
    long int my_msg_type;
    char some_text[BUFSIZ];
};

int main()
{
    int running = 1;
    int msgid;
    struct my_msg_st some_data;
    long int msg_to_receive = 0;


    msgid = msgget((key_t)1234,0666|IPC_CREAT);

    if(msgid == -1)
    {
        fprintf(stderr,"msgget failed with error: %d\n", errno);
        exit(EXIT_FAILURE);
    }


    while(running)
    {
        if(msgrcv(msgid, (void *)&some_data, BUFSIZ, msg_to_receive, 0) == -1)
        {
            fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
            exit(EXIT_FAILURE);
        }


        printf("You wrote: %s", some_data.some_text);
        if(strncmp(some_data.some_text, "end", 3)==0)
        {
            running = 0;
        }
    }

    if(msgctl(msgid, IPC_RMID, 0)==-1)
    {
        fprintf(stderr, "msgctl(IPC_RMID) failed\n");
        exit(EXIT_FAILURE);
    }

    exit(EXIT_SUCCESS);
}


//消息队列封装类

/**
  * @brief Linux消息队列封装
  * @filename Msgqueue.h
  * @author Lihailong
 */

#ifndef MSGQUEUE_H
#define MSGQUEUE_H
#include <stdio.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <string>

#define MAX_MSG_LEN (4096)

class MsgQueue
{
public:
    MsgQueue();
    virtual ~MsgQueue();

    /**
     * @brief OpenMsgQueue 打开消息队列
     * @param [in]nKey 消息ID
     * @param [in]nMode
     * @return
     */
    bool OpenMsgQueue(int nKey, int nMode = IPC_CREAT | 0660);

    /**
     * @brief OpenMsgQueue 打开消息队列
     * @param [in]strQueueName 创建消息队列文件名称:/tmp/strQueueName
     * @param [in]nMode
     * @return
     */
    bool OpenMsgQueue(std::string strQueueName, int nMode = IPC_CREAT | 0660);

    /**
     * @brief IsOpen 判断消息队列是否打开
     * @return
     */
    bool IsOpen();

    /**
     * @brief CloseMsgQueue 关闭消息队列
     * @return
     */
    bool CloseMsgQueue();

    /**
     * @brief SendMsg 发送消息
     * @param [in]nMsgType 消息类型
     * @param [in]pData    发送数据指针
     * @param [in]nMsgLen  发送数据长度
     * @param [in]nMode    发送模式:0:阻塞模式 1:不阻塞模式
     * @return 返回true成功
     */
    bool SendMsg(long nMsgType, void *pData, int nMsgLen, int nMode = 0);

    bool SendMsgTimeOut(long nMsgType, void *pData, int nMsgLen, int nTimeout = 6);

    /**
     * @brief RecvMsg 接收消息
     * @param [in]nMsgType 消息类型
     * @param [out]pData   接收到的数据
     * @param [int]nMsgLen 要接收的数据长度
     * @param [in]nMode    接收模式:0:阻塞模式 1:不阻塞模式
     * @return
     */
    bool RecvMsg(long nMsgType, void *pData, int nMsgLen, int nMode = 0 );

    bool RecvMsgTimeOut(long nMsgType, void *pData, int nMsgLen, int nTimeout = 6);
    /**
     * @brief GetCurByteOfMsgQueue 获取当前消息队列的字节数
     * @return
     */
    int GetCurByteOfMsgQueue();

    /**
     * @brief GetMaxByteOfMsgQueue 获取消息队列的最大占用字节数
     * @return
     */
    int GetMaxByteOfMsgQueue();

    /**
     * @brief GetCurMsgNumOfMsgQueue 获取当前消息队列消息数量
     * @return
     */
    int GetCurMsgNumOfMsgQueue();

    /**
     * @brief GetPidOfLastMsgSend    获取最后发送消息的进程ID
     * @return
     */
    int GetPidOfLastMsgSend();

    /**
     * @brief GetPidOfLastMsgRcv 获取最后接收消息的进程ID
     * @return
     */
    int GetPidOfLastMsgRcv();

    /**
     * @brief GetTimeOfLastMsgSnd 未实现
     * @return
     */
    int GetTimeOfLastMsgSnd();

    /**
     * @brief GetTimeOfLastMsgRcv 未实现
     * @return
     */
    int GetTimeOfLastMsgRcv();

    /**
     * @brief GetTimeOfLasetMsgChg 未实现
     * @return
     */
    int GetTimeOfLasetMsgChg();

    /**
     * @brief GetUid 获取用户组ID
     * @return
     */
    int GetUid();

    /**
     * @brief GetGid 获取组ID
     * @return
     */
    int GetGid();

    /**
     * @brief GetMsgId 获取消息队列标识符
     * @return
     */
    int GetMsgId();

private:

    /**
     * @brief GetMsgQueueSatus 获取消息队列信息
     */
    void GetMsgQueueSatus();

private:
    //消息队列打开标志
    bool m_bOpen;

    //消息队列ID
    int m_MsgId;

    //当前消息对列字节数
    int m_nCurByteSizeOfMsgQueue;

    //当前消息队列消息个数
    int m_nCurMsgCntOfMsgQueue;

    //消息队列最大字节数
    int m_nMaxByteSizeOfMsgQueue;

    //最近发送消息的进程ID
    int m_nPidOfLastMsgSnd;

    //最近接收消息的进程ID
    int m_nPidOfLastMsgRcv;

    //最近发送消息的时间
    int m_nTimeOfLastMsgSnd;

    //最近接收消息的时间
    int m_nTimeOfLastMsgRcv;

    //最近消息修改时间
    int m_nTimeOfLastMsgChange;

    int m_nUidOfMsg;
    int m_nGidOfMsg;

    //要发送的消息结构定义
    struct msg
    {
        long nType;
        char data[MAX_MSG_LEN];
    };

};

#endif // MSGQUEUE_H



/**
  * @brief Linux消息队列封装
  * @filename Msgqueue.h
  * @author Lihailong
 */

#include "msgqueue.h"
#include <stdio.h>
#include <stdlib.h>

MsgQueue::MsgQueue()
{
    m_MsgId = -1;
    m_bOpen = false;
    m_nCurByteSizeOfMsgQueue = 0;
    m_nCurMsgCntOfMsgQueue = 0;
    m_nMaxByteSizeOfMsgQueue = 0;
    m_nTimeOfLastMsgChange = 0;
    m_nTimeOfLastMsgRcv = 0;
    m_nTimeOfLastMsgSnd = 0;
    m_nPidOfLastMsgRcv = 0;
    m_nPidOfLastMsgSnd = 0;
    m_nUidOfMsg = 0;
    m_nGidOfMsg = 0;
}

MsgQueue::~MsgQueue()
{
    CloseMsgQueue();
}

bool MsgQueue::OpenMsgQueue(int nKey, int nMode)
{
    int nRet;
    nRet = msgget((key_t)nKey, nMode);
    if ( -1 == nRet)
    {
        perror("OpenMsgQueue_msgget");
        return false;
    }

    m_MsgId = nRet;
    m_bOpen = true;
    GetMsgQueueSatus();
    return true;
}

bool MsgQueue::OpenMsgQueue(std::string strQueueName, int nMode)
{
    std::string strFile = "/tmp/";
    strFile += strQueueName;
    std::string strCmd = "touch ";
    strCmd += strFile;
    system(strCmd.c_str());
    key_t mqkey = ftok(strFile.c_str(), 11);
    if(mqkey == -1)
    {
        perror("OpenMsgQueue_ftok");
        return false;
    }

    int nRet;
    nRet = msgget(mqkey, nMode);
    if ( -1 == nRet)
    {
        perror("OpenMsgQueue_msgget");
        return false;
    }

    m_MsgId = nRet;
    m_bOpen = true;
    GetMsgQueueSatus();
    return true;
}

bool MsgQueue::IsOpen()
{
    return m_bOpen;
}

bool MsgQueue::CloseMsgQueue()
{
    if (m_bOpen)
    {
        if ( msgctl(m_MsgId, IPC_RMID, NULL) == -1 )
        {
            return false;
        }
        else
        {
            m_bOpen = false;
            return true;
        }
    }
    return false;
}


bool MsgQueue::SendMsg(long nType, void *pData, int nMsgLen, int nMode)
{
    if (nType < 0 || NULL == pData || nMsgLen <= 0 || nMsgLen > MAX_MSG_LEN ) return false;

    int nFlags;
    if (nMode == 0) nFlags = 0;
    else if (nMode == 1) nFlags = IPC_NOWAIT;
    else nFlags = 0;

    int nRet;
    msg sndMsg;
    memset(&sndMsg, 0, sizeof(sndMsg));
    sndMsg.nType = nType;
    memcpy(sndMsg.data, pData, nMsgLen);
    nRet = msgsnd(m_MsgId, (void*)&sndMsg, nMsgLen, nFlags);
    if ( -1 == nRet)
    {
        perror("SendMsg");
        return false;
    }

    GetMsgQueueSatus();
    return true;
}

bool MsgQueue::SendMsgTimeOut(long nType, void *pData, int nMsgLen, int nTimeout)
{
    time_t startTime = time(NULL);
    if (nType < 0 || NULL == pData || nMsgLen <= 0 || nMsgLen > MAX_MSG_LEN ) return false;

    int nFlags = IPC_NOWAIT;
    msg sndMsg;
    memset(&sndMsg, 0, sizeof(sndMsg));
    sndMsg.nType = nType;
    memcpy(sndMsg.data, pData, nMsgLen);

    while(true)
    {
        int nRet = 0;
        nRet = msgsnd(m_MsgId, (void*)&sndMsg, nMsgLen, nFlags);
        if (0 == nRet)
        {
            GetMsgQueueSatus();
            return true;
        }
        else
        {
            if (errno == EAGAIN)
            {
                if (time(NULL) - startTime < nTimeout)
                {
                    usleep(30);
                    continue;
                }
            }

            perror("msgsnd");
            return false;
        }
    }
}

bool MsgQueue::RecvMsg(long nMsgType, void *pData, int nMsgLen, int nMode)
{
    if (NULL == pData || nMsgLen <= 0 || nMsgLen > MAX_MSG_LEN) return false;
    int nFlags;
    if ( 0 == nMode) nFlags = 0;
    else if (1 == nMode) nFlags = IPC_NOWAIT;
    else nFlags = 0;
    int nRet;
    msg rcvMsg;
    memset(&rcvMsg, 0, sizeof(rcvMsg));
    nRet = msgrcv(m_MsgId, (void*)&rcvMsg, nMsgLen, nMsgType, nFlags);
    if (-1 == nRet)
    {
        perror("RecvMsg");
        return false;
    }

    memcpy(pData, rcvMsg.data, nMsgLen);
    GetMsgQueueSatus();
    return true;
}

bool MsgQueue::RecvMsgTimeOut(long nMsgType, void *pData, int nMsgLen, int nTimeout)
{
    time_t startTime = time(NULL);
    if (NULL == pData || nMsgLen <= 0 || nMsgLen > MAX_MSG_LEN) return false;
    int nFlags = IPC_NOWAIT;
    msg rcvMsg;
    memset(&rcvMsg, 0, sizeof(rcvMsg));
    while(true)
    {
        int nRet;
        nRet = msgrcv(m_MsgId, (void*)&rcvMsg, nMsgLen, nMsgType, nFlags);
        if (0 < nRet)
        {
            memcpy(pData, rcvMsg.data, nMsgLen);
            GetMsgQueueSatus();
            return true;
        }
        else
        {
            if (errno == ENOMSG || 0 == nRet)
            {
                if (time(NULL) - startTime < nTimeout)
                {
                    usleep(30);
                    continue;
                }
            }

            perror("msgrcv");
            return false;
        }
    }
}

int MsgQueue::GetCurByteOfMsgQueue()
{
    return m_nCurByteSizeOfMsgQueue;
}

int MsgQueue::GetMaxByteOfMsgQueue()
{
    return m_nMaxByteSizeOfMsgQueue;
}

int MsgQueue::GetCurMsgNumOfMsgQueue()
{
    return m_nCurMsgCntOfMsgQueue;
}

int MsgQueue::GetPidOfLastMsgSend()
{
    return m_nPidOfLastMsgSnd;
}

int MsgQueue::GetPidOfLastMsgRcv()
{
    return m_nPidOfLastMsgRcv;
}

int MsgQueue::GetTimeOfLastMsgSnd()
{
    return 0;
}

int MsgQueue::GetTimeOfLastMsgRcv()
{
    return 0;
}

int MsgQueue::GetTimeOfLasetMsgChg()
{
    return 0;
}

int MsgQueue::GetUid()
{
    return m_nUidOfMsg;
}

int MsgQueue::GetGid()
{
    return m_nGidOfMsg;
}

int MsgQueue::GetMsgId()
{
    return m_MsgId;
}

void MsgQueue::GetMsgQueueSatus()
{
    if (!m_bOpen) return;
    struct msqid_ds msgInfo;
    int nRet;
    nRet = msgctl(m_MsgId, IPC_STAT, &msgInfo);
    if ( -1 == nRet)
    {
        perror("GetMsgQueueSatus");
        return;
    }

    m_nCurByteSizeOfMsgQueue = msgInfo.msg_cbytes;
    m_nCurMsgCntOfMsgQueue   = msgInfo.msg_qnum;
    m_nMaxByteSizeOfMsgQueue = msgInfo.msg_qbytes;
    m_nPidOfLastMsgSnd       = msgInfo.msg_lspid;
    m_nPidOfLastMsgRcv       = msgInfo.msg_lrpid;
    m_nUidOfMsg              = msgInfo.msg_perm.uid;
    m_nGidOfMsg              = msgInfo.msg_perm.gid;
}

///调用实例

int main()
{
    msbuf d,a;

    d.time = 3333;
    d.data = 4444;
    d.s = 4560.232;
    MsgQueue msg;
    msg.OpenMsgQueue("Test");
    msg.Send(10, &d, sizeof(d));
    printf("idss:%d\n",msg.GetMsgId());
    memset(&a, 0, sizeof(a));
    msg.RecvMsg(10, &a, sizeof(a));



    printf("time:%d\n", a.time);
    printf("data:%d\n", a.data);
        printf("double:%lf\n", a.s);

  //  msg.CloseMsgQueue();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值