进程间通信:消息队列

消息队列

      消息队列特点:

  • 是一个全双工通信,可读可写
  • 具有入口和出口,消息的传输是先入先出的,是以队列形式存储;(与管道相似)
  • 发送的是一个带有类型的数据块。<创建在内核中>(区别于管道字节流传输)
  • 每个消息的最大长度是有上限的(MSGMAX),
  • 每个消息队列的总的字节数是有上限的(MSGMNB),
  • 系统上消息队列的总数也有上限(MSGMNI)。

        --> 所以很少用,多用共享内存。

        本质是操作系统在内核为我们创建的一个队列。通过这个队列的标识符key,每个进程都可以打开这个队列,每个进程可以通过向队列中添加节点或获取节点来进行数据传输(进程间通信)。

  • 如何传输数据?

       用户组织一个带有类型的数据块,添加到队列中,其他的进程从队列中获取数据块。接收者进程接受的数据块可以有不同的类型值。

  • 消息队列生命周期<随内核>:

1. 创建消息队列;

2. 发送数据/接受数据;

3. 释放消息队列。

功能

接口

创建

msgget

发送

msgsnd

接受

msgrcv

控制

msgctl

 

  • 创建消息队列msgget:

msgget所创建的是一个可扩展的消息队列集合。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
       
int msgget(key_t key, int msgflg);

说明:

  • key 内核中消息队列的标识
  • msgflg:

          IPC_CREAT 不存在就创建,存在则打开

          IPC_EXCL 与IPC_CREAT同用时,若存在则报错

          mode 权限

  • 返回值: 代码操作的句柄 失败:-1
  1. 由于无法确定进程间通信需要多少消息队列,为节省内存资源,消息队列是可扩展的
  2. msgget新创建的消息队列集合中有 0 个消息队列
  3. 在需要使用消息队列时,再在集合中创建所需的消息队列。
  • 发送数据msgsnd:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
        
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

说明:

  • msqid msgget返回的操作句柄
  • msgp 用于接受数据
  • msgsz 用于指定接受数据的大小
  • msgflg 标志选项 默认给 0即可

 

在发送消息结构体时,只是发送了消息结构体中成员的值。

如果结构体成员是指针,则只会发送该指针变量所保存的空间地址。

如果结构体成员是数组,则可以将发送整个数组。

 

  • 接收数据msgrcv:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
       
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

说明:

  • msgrcv 默认阻塞的获取数据
  • msgid 操作句柄
  • msgsz 用于指定要接收的最大数据长度,不包含mtype
  • msgtyp 用于指定接收的数据类型
  1. 取队列中第一个节点,不分类型
  2. >0 取指定类型数据块的第一个节点
  3. <0 取小于msgtype绝对值类型的第一个节点
  • msgflag:

       默认:0 & MSG_NOERROR 当数据长度超过指定长度,则会截断数据

 

  1. 可以通过msgtyp识别消息队列集合中的消息队列,以便从该队列中读取消息;
  2. 在发送消息时,是在结构体中指定,当前消息发送到消息队列集合中的那一消息队列上;
  3. 消息体结构体中必须包含long类型 type值,且是第一个成员,其之后成员都是要发送的数据;
  4. 无论发送,或是接收,,type值对应的消息队列不存在,则立即创建;
  5. 发送方与接收方的数据块大小要与结构体的数据部分一致。
  • 释放消息队列msgctl:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
       
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

 代码实现:

// Server.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define RECV 200
#define SEND 100

typedef struct{
    long msg_type;
    char msg_data[50];
}MsgBlock;

int main ()
{
    key_t msg_key = ftok("tmp", '7');
    printf("Key: %d\n", msg_key);
    int msgid = msgget(msg_key, IPC_CREAT | 0775);

    if(msgid < 0){ 
        perror("Msg Error\n");
        return 0;
    }   


    MsgBlock msg;
    while(1){
        printf("Server: ");
        scanf("%s", msg.msg_data);
        if(strncmp(msg.msg_data, "quit", 4) == 0)
            break;
        msg.msg_type = SEND; 
        msgsnd(msgid, &msg, strlen(msg.msg_data)+1, 0); 

        msgrcv(msgid, &msg, 50, RECV, 0); 
        printf("Client: %s\n", msg.msg_data);
    }   
    return 0;
}     
// Client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define RECV 100
#define SEND 200

typedef struct{
    long msg_type;
    char msg_data[50];
}MsgBlock;

int main ()
{
    key_t msg_key = ftok("tmp", '7');
    printf("Key: %d\n", msg_key);
    int msgid = msgget(msg_key, 0);
    if(msgid < 0){
        perror("Msg Error\n");
        return 0;
    }

    MsgBlock msg;
    while(1){
        msgrcv(msgid, &msg, 50, RECV, 0);
        printf("Server: %s\n", msg.msg_data);

        printf("Client: ");
        scanf("%s", msg.msg_data);
        if(strncmp(msg.msg_data, "quit", 4) == 0)
            break;

        msg.msg_type = SEND; 
        msgsnd(msgid, &msg, strlen(msg.msg_data)+1, 0);
    }
    return 0;
}

 

效果演示:

Server端: 

进程间通信:消息队列

Client端: 

进程间通信:消息队列

关于消息队列的优缺点可以看下面的博客: 

<转>消息队列优缺点:

;