linux---消息隊列

消息隊列

消息隊列是消息的鏈接表,存儲在內核中,,由消息隊列標識符標識。

在linux下查看當前所有的消息隊列

ipcs -q

刪除一個消息隊列

ipcrm -q 消息隊列的id

消息隊列的特點

  1. 面向數據報
  2. 全雙工
  3. 支持一個進程向另一個進程發送一塊數據的方法
  4. 每個數據塊都可以是不同的類型,接收者進程接收的數據塊可以有不同的類型值。
  5. 消息隊列中的每個消息的大小是有上限的,每個消息隊列的總的字節數是有上限的,系統上消息隊列可以創建的總數也是有上限的。
  6. 創建的消息隊列的生命週期是隨內核的。不會隨着進程的結束而銷燬,需要人爲顯式的終止。

內核爲每個IPC對象分配的一個結構體

struct ipc_perm                     
{                             
    __kernel_key_t  key;        
    __kernel_uid_t  uid;         
    __kernel_gid_t  gid;        
    __kernel_uid_t  cuid;       
    __kernel_gid_t  cgid;       
    __kernel_mode_t mode;       
    unsigned short  seq;                        
};

消息隊列的數據結構,每個消息隊列有一個結構體用來描述該消息隊列。

struct msqid_ds {
    struct ipc_perm msg_perm;
    struct msg *msg_first;      /* first message on queue,unused  */
    struct msg *msg_last;       /* last message in queue,unused */
    __kernel_time_t msg_stime;  /* last msgsnd time */
    __kernel_time_t msg_rtime;  /* last msgrcv time */
    __kernel_time_t msg_ctime;  /* last change time */
    unsigned long  msg_lcbytes; /* Reuse junk fields for 32 bit */
    unsigned long  msg_lqbytes; /* ditto */
    unsigned short msg_cbytes;  /* current number of bytes on queue */
    unsigned short msg_qnum;    /* number of messages in queue */
    unsigned short msg_qbytes;  /* max number of bytes on queue */
    __kernel_ipc_pid_t msg_lspid;   /* pid of last msgsnd */
    __kernel_ipc_pid_t msg_lrpid;   /* last receive pid */
};

消息隊列的幾個常用接口函數

  1. msgget函數—用於創建和訪問一個消息隊列
    原型: int msgget(key_t key, int msgflg);
    參數:
    key :消息隊列對應的唯一id,一般用ftok函數獲得
    msgflag : 常用的就是IPC_CREAT(不存在就創建,存在就打開) 、IPC_EXCL(若不存在就創建,否則就創建失敗)
  2. msgctl函數—消息隊列的控制函數
    原型:int msgctl(int msgid, int cmd, struct msqid_ds *buf);
    參數:
    msgid : 要控制的消息隊列的id,一般是msgget的返回值
    cmd : 對消息隊列要採取的操作(IPC_SET、IPC_STAT、IPC_RMID)
    buf :是一個輸出型參數,返回的是消息隊列的一些性質
    cmd 操作:
    1. IPC_STAT:把msqid_ds結構中的數據設置爲消息隊列的當前關聯值
      1. IPC_SET :在進程有足夠權限的前提下,把消息隊列的當前關聯值設置爲msqid_ds數據結構中給出的值
        1. IPC_RMID:刪除消息隊列
  3. msgsnd函數—把一條消息添加到消息隊列中
    原型:int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
    參數:
    msgid:要添加到的消息隊列的id
    msgp:是一個指向準備發送的的消息
    msgsz:準備發送數據的長度,即是msgp所指向的消息的長度
    msgflg:標誌位
    返回值:
    成功返回 0 ,失敗返回 -1 。
  4. msgrcv函數—從一個消息隊列接收消息
    原型: ssize_t msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    參數:
    msgid:需要接收的消息的消息隊列的id
    msgp:指向準備接收到的消息
    msgsz:要接受的消息的長度,不包括用於指定消息類型的long的長度
    msgtyp:接收消息的優先級
    msgflg:標誌位
    返回值:
    成功返回接收到的消息隊列的長度,失敗返回 -1。
    優先級說明:
    • msgtyp == 0返回隊列的第一條消息
    • > 0 返回隊列的第一條類型等於msgtype的消息
    • < 0返回隊列第一條類型小於等於msgtype絕對值的消息,並且是滿足條件的消息類型最小的消息
    • msgflg = IPC_NOWAIT,隊列沒有可讀消息不等待,返回ENOMSG錯誤
    • msgflg = MSG_NOERROR,消息大小超過msgsz時被截斷
    • msgtype>0且msgflg=MSG_EXCEPT,接收類型不等於msgtype的第一條消息

用消息隊列實現一個簡單的服務器/客戶端

客戶端是主動發送請求。服務器被動接收請求。

服務器先啓動,7 x 24小時工作。

客戶端發送的請求不同,服務器會根據不同的請求計算不同的結果,並將結果給客戶端。


我們首先需要將系統提供的函數進行封裝。得到更完整的接口函數,讓服務器端和客戶端的代碼寫起來比較簡便。

common.h

    #pragma once
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <string.h>

    #define PATHNAME "."
    #define PROJ_ID 0x6666

    #define SERVER_TYPE 1
    #define CLIENT_TYPE 2

    typedef struct Msgbuf{
        long mtype;        // 數據的類型 
        char mtext[1024];  // 存儲數據的字符串數組
    }Msgbuf;

    //創建一個消息隊列,如果已經存在,就調用失敗
    int CreateMsg();

    //打開一個已有的消息隊列,如果不存在,也返回失敗
    int GetMsg();

    //銷燬一個消息隊列
    int DestroyMsg(int msgid);

    int sendMsg(int msgid, long type, char *buf, size_t size);

    int recvMsg(int msgid, long type, char *buf, size_t max_size);

common.c

    #include "common.h"
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <string.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()
    {
        // 0666表示權限,所有用戶可讀可寫 
        //IPC_EXCL要搭配IPC_CREAT使用,意思是要是消息隊列不存在就創建, 存在就創建失敗。
        return CommonMsg(IPC_CREAT | IPC_EXCL | 0666);
    }

    int GetMsg()
    {
        return CommonMsg(IPC_CREAT);
    }

    int DestroyMsg(int msgid)
    {
        int ret = msgctl(msgid, IPC_RMID, NULL);
        if(ret < 0)
        {
            perror("msgctl");
            return -1;
        }
        return 0;
    }

    int sendMsg(int msgid, long type, char* buf, size_t size)
    {
       Msgbuf msgbuf;
       if(size >= sizeof(msgbuf.mtext))
       {
           printf("size is to large!\n");
           return -1;
       }
       msgbuf.mtype = type;
       strcpy(msgbuf.mtext, buf);
       int ret = msgsnd(msgid, &msgbuf, sizeof(msgbuf.mtext), 0);
       if(ret < 0)
       {
           perror("msgsnd");
           return -1;
       }
       return 0;
    }

    int recvMsg(int msgid, long type, char *buf, size_t max_size)
    {
        Msgbuf msgbuf;
        ssize_t ret = msgrcv(msgid, &msgbuf, sizeof(msgbuf.mtext), type, 0);
        if(ret < 0)
        {
            perror("msgrcv");
            return -1;
        }
        if(max_size < sizeof(msgbuf.mtext))
        {
            printf("buf is too small");
            return -1;
        }
        strcpy(buf, msgbuf.mtext);
        return 0;
    }

服務器端代碼

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

    int main()
    {
        int msgid = CreateMsg();
        printf("msgid = %d\n", msgid);
        while(1)
        {
            char buf[2048] = {0};
            int ret = recvMsg(msgid, CLIENT_TYPE, buf, sizeof(buf) - 1);
            if(ret < 0)
            {
                perror("recvMsg");
                return 1;
            }
            printf("client say: %s",buf);
            sendMsg(msgid, CLIENT_TYPE, buf, strlen(buf));
        }
        //DestroyMsg(msgid);
        return 0;
    }

客戶端

  #include <stdio.h>
    #include <unistd.h>
    #include "common.h"
    int main()
    {
        int msgid = CreateMsg();
        printf("msgid = %d\n", msgid);
        while(1)
        {
            char buf[2048] = {0};
            int ret = recvMsg(msgid, CLIENT_TYPE, buf, sizeof(buf) - 1);
            if(ret < 0)
            {
                perror("recvMsg");
                return 1;
            }
            printf("client say: %s",buf);
            sendMsg(msgid, CLIENT_TYPE, buf, strlen(buf));
        }
        //DestroyMsg(msgid);
        return 0;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章