(P27)system v消息隊列

1.單個消息隊列實現回射服務器

  • 隊列複用功能:客戶端——>服務器端,服務器端——>客戶端端

  • 客戶1:只接收類型爲1234的消息,客戶2只接收類型爲9876的消息
    在這裏插入圖片描述

  • 單個隊列的死鎖現象: 當多個客戶端往發送請求,將隊列堵滿了,服務器不能夠應答,客戶端在等待服務器回射數據回來,此時會產生死鎖。

  • eg服務端程序:NetworkProgramming-master (1)\NetworkProgramming-master\P27msgsrv.c

//
// Created by wangji on 19-8-12.
//

// p25 system v消息隊列(三)回射服務器端

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

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>//memset函數的頭文件

#define ERR_EXIT(m) \
        do \
        { \
             perror(m); \
             exit(EXIT_FAILURE);    \
        } while (0);


#define MSGMAX 8192

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

void echo_srv(int msgid)
{
    struct msgbuf msg;
    int n;
    int type;
    memset(&msg, 0, sizeof(msgbuf));
    while (1)
    {
        if ((n = msgrcv(msgid, &msg, MSGMAX, 1, 0)) < 0)//不停的接收類型=1的消息,0表示以阻塞的方式接收
        {
            ERR_EXIT("msgsnd");
        }

        //解析的來自客戶端的pid+回射行line
        type = *((int*)msg.mtext);//取出前4個字節
        msg.mtype = type;

        //前4個字節是pid
        fputs(msg.mtext+4, stdout);

        if (msgsnd(msgid, &msg, n, 0) < 0)
        {
            ERR_EXIT("msgsnd");
        }
        //memset(&msg, 0, sizeof(msgbuf));
    }

}

int main(int argc, char** argv)
{
    int msgid;
    msgid = msgget(1234, IPC_CREAT|0666);
    if (msgid == -1)
    {
        ERR_EXIT("msgget");
    }

    echo_srv(msgid);

    return 0;
}
  • eg客戶端程序:NetworkProgramming-master (1)\NetworkProgramming-master\P27msgcli.c
//
// Created by wangji on 19-8-12.
//

// p25 system v消息隊列(三)回射客戶端

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

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

#define ERR_EXIT(m) \
        do \
        { \
             perror(m); \
             exit(EXIT_FAILURE);    \
        } while (0);

#define MSGMAX 8192

#define MSGMAX 8192

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


int echocli(int msgid)
{
    struct msgbuf msg;
    memset(&msg, 0, sizeof(msgbuf));
    int pid = getpid();
    msg.mtype = 1;//客戶端給服務器段發送的消息類型總是=1
    *((int*)msg.mtext) = pid;//msg.mtext前4個字節是pid
    int n;
    //msg.mtext + 4,表示前4個字節是pid
    while (fgets(msg.mtext + 4, MSGMAX, stdin) != NULL)//不停的從鍵盤中獲取1行數據
    {
        //msgsnd(msgid, &msg,4 + strlen(4+msg.mtext), 0)也對,客戶端發送的是:pid+回射行line
        if (msgsnd(msgid, &msg, sizeof(long) + strlen(msg.mtext), 0) < 0)
        {
            ERR_EXIT("msgsnd");
        }
        
        //前4個字節不需要清空,用以保存pid
        memset(msg.mtext+4, 0, sizeof(msg.mtext + 4));

        if ((n = msgrcv(msgid, &msg, MSGMAX, pid, 0)) < 0)//接收類型是pid的消息
        {
            ERR_EXIT("msgsnd");
        }
        fputs(msg.mtext + 4, stdout);
        memset(msg.mtext + 4, 0, sizeof(msg.mtext + 4));
    }


}

int main(int argc, char** argv)
{
    int msgid;
    msgid = msgget(1234, 0);
    if (msgid == -1)
    {
        ERR_EXIT("msgget");
    }

    echocli(msgid);



    return 0;
}

  • 測試結果如下:
    在這裏插入圖片描述
    在這裏插入圖片描述

2.多個隊列解決單個隊列的死鎖問題

  • 多個隊列解決死鎖現象
    (1)當一個客戶端創建時,同時創建一個私有的隊列;
    (2)當客戶端往服務器端發送消息時,會將私有隊列的標識符傳遞給服務器端,以便服務器端能夠向私有隊列填充數據;
    (3)服務器端收到消息後,會fork一個子進程來將消息回射到客戶端,子進程將消息填充到私有隊列中;

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章