溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

IPC之消息隊(duì)列·即時(shí)通訊小程序(一)

發(fā)布時(shí)間:2020-08-06 05:52:22 來(lái)源:網(wǎng)絡(luò) 閱讀:14222 作者:SherryX 欄目:系統(tǒng)運(yùn)維

消息隊(duì)列

上次說(shuō)到了進(jìn)程間通信的管道,不過(guò)匿名管道有個(gè)缺點(diǎn)就是,只能做到有親緣關(guān)系的進(jìn)程間通信,所以今天學(xué)習(xí)一個(gè)新的進(jìn)程間通信方式——消息隊(duì)列。

  • 消息隊(duì)列提供了一個(gè)從一個(gè)進(jìn)程向另外一個(gè)進(jìn)程發(fā)送一塊數(shù)據(jù)的方法
  • 每個(gè)數(shù)據(jù)塊都被認(rèn)為是有一個(gè)類(lèi)型,接收者進(jìn)程接收的數(shù)據(jù)塊可以有不同的類(lèi)型值
  • 消息隊(duì)列也有管道一樣的不足,就是每個(gè)數(shù)據(jù)塊的最大長(zhǎng)度是有上限的,系統(tǒng)上全體隊(duì)列的最大總長(zhǎng)度也有一個(gè)上限
    IPC之消息隊(duì)列·即時(shí)通訊小程序(一)

    消息隊(duì)列函數(shù)

    頭文件
    #include < sys/types.h>
    #include < sys/ipc.h>
    #include < sys/msg.h>

    msgget

    int msgget(key_t key, int msgflg);
    作用:創(chuàng)建和訪問(wèn)一個(gè)消息隊(duì)列
    key:某個(gè)消息隊(duì)列的名字(類(lèi)似于每個(gè)進(jìn)程都有一個(gè)進(jìn)程ID一樣)
    msgflg:有9個(gè)權(quán)限標(biāo)志構(gòu)成。它們的用法和創(chuàng)建文件時(shí)使用的mode標(biāo)志是一樣的(比如:一個(gè)key已經(jīng)存在的消息隊(duì)列時(shí),要使用IPC_CREAT | IPC_EXCL,就類(lèi)似于文件操作的打開(kāi):O_CREAT | O_EXCL )。
    返回值:成功將返回一個(gè)非負(fù)整數(shù),即該消息隊(duì)列的標(biāo)識(shí)碼;失敗返回-1

    msgsnd

    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

    作用:把一條消息加到消息隊(duì)列里面
    msgid:由msgget函數(shù)返回的消息隊(duì)列標(biāo)識(shí)碼
    msgp:是一個(gè)指針,指向準(zhǔn)備發(fā)送的消息
    msgsz:msgp指向的消息長(zhǎng)度,這個(gè)長(zhǎng)度不能保存消息類(lèi)型里的“l(fā)ong int”類(lèi)型(下面會(huì)說(shuō))
    msgflg:控制著當(dāng)前消息隊(duì)列滿或達(dá)到系統(tǒng)上限時(shí)將要發(fā)生的事情。
    返回值:成功-0,失敗-1
    1)

       struct msgbuf {
           long mtype;       /* message type, must be > 0 */
           char mtext[1];    /* message data */
       };

    消息的指針就是指向這樣一個(gè)結(jié)構(gòu)的消息,這需要我們自己定義。但是,第一個(gè)一定得是long int,表示消息的類(lèi)型。消息的類(lèi)型是大于0的整數(shù)(當(dāng)然也是可以等于0的,但是這樣就意味著任何消息我都接收,不固定只收某一種類(lèi)型的消息了)。

  • msgtype=0返回隊(duì)列第一條信息
  • msgtype>0返回隊(duì)列第一條類(lèi)型等于msgtype的消息 
  • msgtype< 0返回隊(duì)列第一條類(lèi)型小于等于msgtype絕對(duì)值的消息

msgrcv

作用:從一個(gè)消息隊(duì)列里檢索消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgid:由msgget函數(shù)返回的消息隊(duì)列標(biāo)識(shí)碼
msgp:是一個(gè)指針,指向準(zhǔn)備接收的消息
msgsz:msgp指向的消息長(zhǎng)度,這個(gè)長(zhǎng)度不能保存消息類(lèi)型里的“l(fā)ong int”類(lèi)型(下面會(huì)說(shuō))
msgflg:控制著隊(duì)列中沒(méi)有相應(yīng)類(lèi)型的消息可供接收的時(shí)候?qū)⒁l(fā)生的事
msgtyp:可以實(shí)現(xiàn)接收優(yōu)先級(jí)的簡(jiǎn)單形式
返回值:成功-返回實(shí)際放到接收緩沖區(qū)里的字符個(gè)數(shù),失敗- “-1”
msgflg有以下幾個(gè)值:
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)

  • msgflg=IPC_NOWAIT,隊(duì)列沒(méi)有可讀消息不等待,返回ENOMSG錯(cuò)誤。
  • msgflg=MSG_NOERROR,消息大小超過(guò)msgsz時(shí)被截?cái)?/li>
  • msgtype>0且msgflg=MSC_EXCEPT,接收類(lèi)型不等于msgtype的第一條消息。

    msgctl

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

    作用:消息隊(duì)列的控制函數(shù)
    msgid:由msgget函數(shù)返回的消息隊(duì)列標(biāo)識(shí)碼
    cmd:將要采取的動(dòng)作,簡(jiǎn)單講常用的三個(gè)可取值:

IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
msgid_ds數(shù)據(jù)結(jié)構(gòu)定義如下:

 struct msqid_ds {
               struct ipc_perm msg_perm;    /* Ownership and permissions */
               time_t         msg_stime;    /* Time of last msgsnd(2) */
               time_t         msg_rtime;    /* Time of last msgrcv(2) */
               time_t         msg_ctime;    /* Time of last change */
               unsigned long  __msg_cbytes; /* Current number of bytes in
                                               queue (non-standard) */
               msgqnum_t      msg_qnum;     /* Current number of messages
                                               in queue */
               msglen_t       msg_qbytes;   /* Maximum number of bytes
                                               allowed in queue */
               pid_t          msg_lspid;    /* PID of last msgsnd(2) */
               pid_t          msg_lrpid;    /* PID of last msgrcv(2) */
           };

練習(xí)

試著寫(xiě)一段吧~
發(fā)送端:msgsnd.c

struct msg_t
{
    long mtype; //第一個(gè)必須是long,>=1
    char acMsg[20];
};

//用ipcs 
int main()
{
    int msgid;
    struct msg_t msg = {0};

    //消息隊(duì)列的創(chuàng)建、打開(kāi)、刪除
    msgid = msgget(1000,IPC_CREAT);
    if(msgid == -1)
    {
        perror("create msg");
    }
    printf("msgid = %d\n",msgid);

    msg.mtype = 1;
    strcpy(msg.acMsg,"hello");
    msgsnd(msgid,&msg,sizeof(struct msg_t)-sizeof(long),0);

    //msgctl(msgid,IPC_RMID,NULL);//也可以用命令ipcrm

    return 0;
}

接收端:msgrcv.c

struct msg_t
{
    long mtype; //第一個(gè)必須是long,>=1
    char acMsg[20];
};

int main()
{
    int msgid;
    struct msg_t msg = {0};

    //消息隊(duì)列的創(chuàng)建、打開(kāi)、刪除
    msgid = msgget(1000,0);
    if(msgid == -1)
    {
        perror("open msg!\n");
    }
    printf("msgid = %d\n",msgid);

    msgrcv(msgid,&msg,sizeof(struct msg_t)-sizeof(long),1,0);
    printf("recv msg: %s.\n",msg.acMsg);

    return 0;
}

運(yùn)行:
開(kāi)三個(gè)終端,一個(gè)運(yùn)行msgsnd.c,一個(gè)運(yùn)行msgrcv.c,一個(gè)用來(lái)查看消息隊(duì)列狀態(tài):
1、先發(fā)送:
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
2、查看:ipcs
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
3、讀?。?br/>IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
4、再查看(被取走了):
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
<br>
但是,也注意到了使用消息隊(duì)列,消息一旦被讀走,就沒(méi)了。

即時(shí)通訊小程序

使用消息隊(duì)列與共享內(nèi)存(后面會(huì)復(fù)習(xí))完成一個(gè)簡(jiǎn)單的終端聊天程序,要求如下。
1.程序有一個(gè)server服務(wù)器,服務(wù)器有一個(gè)在線列表,當(dāng)終端登入時(shí),將終端的進(jìn)程ID作為用戶名稱(chēng)添加到在線列表。(消息隊(duì)列和共享內(nèi)存)
2.終端登入時(shí),獲取用戶的在線列表。(消息隊(duì)列、信號(hào)、和共享內(nèi)存)
3.終端登入后,進(jìn)入聊天狀態(tài)。:(信號(hào)、共享內(nèi)存)
輸入#chat [pid],進(jìn)入私聊模式
例如:#chat 1234,與終端1234進(jìn)入私聊。只有終端1234才能接收消息。
輸入#chat 0,進(jìn)入群聊模式。全部終端可以接收消息。
說(shuō)明:1,2,3為基本功能,要求實(shí)現(xiàn)。4,5,6為附加功能,有能力同學(xué)可以嘗試實(shí)現(xiàn)。
4.終端登入時(shí),服務(wù)器發(fā)送消息,通知其他在線終端,更新在線列表。(信號(hào))
5.終端在聊天狀態(tài),輸入#user,列出在線用戶列表。
6.終端在聊天狀態(tài),輸入#exit,終端退出,服務(wù)器將終端的進(jìn)程ID移出在線列表,并通知在線終端,更新在線列表。(消息隊(duì)列和共享內(nèi)存)

學(xué)了消息隊(duì)列,至少可以把用戶的登錄、退出完成。
代碼如下:
public.h

#ifndef _PUBLIC_H_
#define _PUBLIC_H_

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

typedef struct login_t
{
    long type;
    pid_t pid;
}LOGIN_T;

#define MSG_KEY 1
#define MSG_SIZE sizeof(LOGIN_T)-sizeof(long)

#endif

server.c

#include " public.h"

int main()
{
    int msg_id;
    LOGIN_T login = {0};

    //創(chuàng)建用戶的消息隊(duì)列
    msg_id = msgget(MSG_KEY,0);
    if(msg_id == -1)
    {
        msg_id = msgget(MSG_KEY,IPC_CREAT);
        if (msg_id == -1)
        {
            perror("server msgget");
            return -1;
        }
    }
    //一直監(jiān)聽(tīng),是否有用戶上線
    while (1)
    {
        memset(&login,0,sizeof(LOGIN_T));
        msgrcv(msg_id,&login,MSG_SIZE,0,0); //任何消息都接收
        switch(login.type)
        {
        case 1:
            printf("client %d is logining...\n",login.pid);
            break;
        case 2:
            printf("client %d is exiting...\n",login.pid);
            break;

        }

    }

    return 0;
}

client.c

#include "public.h"

int main()
{
    char acBuf[20] = "";
    int msg_id;
    LOGIN_T login = {0};

    //打開(kāi)消息隊(duì)列
    msg_id = msgget(MSG_KEY,0);
    if(msg_id == -1)
    {
        perror("client msgget");
        return -1;
    }
    //登錄,寫(xiě)消息隊(duì)列
    login.type = 1;     //設(shè)置登錄的消息類(lèi)型為1
    login.pid = getpid();
    printf("%d is logining...\n",login.pid);
    msgsnd(msg_id,&login,MSG_SIZE,0);

    //等待寫(xiě)
    while(1)
    {
        putchar('#');
        fflush(stdout);
        scanf("%s",acBuf);
        if (strcmp(acBuf,"quit") == 0)
        {
            login.type = 2;     //設(shè)置退出的消息類(lèi)型為2
            msgsnd(msg_id,&login,MSG_SIZE,0);
            break;
        }
    }
    return 0;
}

運(yùn)行:先運(yùn)行服務(wù)器端,再運(yùn)行多個(gè)客戶端
客戶端:
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)
服務(wù)器:
IPC之消息隊(duì)列·即時(shí)通訊小程序(一)

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI