消息队列
消息队列特点:
- 是一个全双工通信,可读可写;
- 具有入口和出口,消息的传输是先入先出的,是以队列形式存储;(与管道相似)
- 发送的是一个带有类型的数据块。<创建在内核中>(区别于管道字节流传输)
- 每个消息的最大长度是有上限的(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
- 由于无法确定进程间通信需要多少消息队列,为节省内存资源,消息队列是可扩展的;
- msgget新创建的消息队列集合中有 0 个消息队列;
- 在需要使用消息队列时,再在集合中创建所需的消息队列。
- 发送数据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 用于指定接收的数据类型
- 取队列中第一个节点,不分类型
- >0 取指定类型数据块的第一个节点
- <0 取小于msgtype绝对值类型的第一个节点
- msgflag:
默认:0 & MSG_NOERROR 当数据长度超过指定长度,则会截断数据
- 可以通过msgtyp识别消息队列集合中的消息队列,以便从该队列中读取消息;
- 在发送消息时,是在结构体中指定,当前消息发送到消息队列集合中的那一消息队列上;
- 消息体结构体中必须包含long类型 type值,且是第一个成员,其之后成员都是要发送的数据;
- 无论发送,或是接收,,type值对应的消息队列不存在,则立即创建;
- 发送方与接收方的数据块大小要与结构体的数据部分一致。
- 释放消息队列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端:
关于消息队列的优缺点可以看下面的博客:
<转>消息队列优缺点:http://www.cnblogs.com/nufangrensheng/p/3561681.html