消息队列
消息队列是消息的链接表,存储在内核中,,由消息队列标识符标识。
在linux下查看当前所有的消息队列
ipcs -q
删除一个消息队列
ipcrm -q 消息队列的id
消息队列的特点
- 面向数据报
- 全双工
- 支持一个进程向另一个进程发送一块数据的方法
- 每个数据块都可以是不同的类型,接收者进程接收的数据块可以有不同的类型值。
- 消息队列中的每个消息的大小是有上限的,每个消息队列的总的字节数是有上限的,系统上消息队列可以创建的总数也是有上限的。
- 创建的消息队列的生命周期是随内核的。不会随着进程的结束而销毁,需要人为显式的终止。
内核为每个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 */
};
消息队列的几个常用接口函数
- msgget函数—用于创建和访问一个消息队列
原型: int msgget(key_t key, int msgflg);
参数:
key :消息队列对应的唯一id,一般用ftok函数获得
msgflag : 常用的就是IPC_CREAT(不存在就创建,存在就打开) 、IPC_EXCL(若不存在就创建,否则就创建失败) - msgctl函数—消息队列的控制函数
原型:int msgctl(int msgid, int cmd, struct msqid_ds *buf);
参数:
msgid : 要控制的消息队列的id,一般是msgget的返回值
cmd : 对消息队列要采取的操作(IPC_SET、IPC_STAT、IPC_RMID)
buf :是一个输出型参数,返回的是消息队列的一些性质
cmd 操作:
- IPC_STAT:把msqid_ds结构中的数据设置为消息队列的当前关联值
- IPC_SET :在进程有足够权限的前提下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值
- IPC_RMID:删除消息队列
- msgsnd函数—把一条消息添加到消息队列中
原型:int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
参数:
msgid:要添加到的消息队列的id
msgp:是一个指向准备发送的的消息
msgsz:准备发送数据的长度,即是msgp所指向的消息的长度
msgflg:标志位
返回值:
成功返回 0 ,失败返回 -1 。 - 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;
}