消息队列:看到队列,我们可以想到数据结构中所学到的消息队列,它是由一段连续的空间或者以空间块的形式连接在一起的空间。我们可以想到队列中肯定会有 头指针和尾指针,该指针指向的空间肯定存放着我们想要的数据。那么我们进程间通信的消息队列是如何实现的?我们下面来看。
消息队列的定义:
1.消息队列提供了一个进程向另一个进程发送数据块的方法
2.接受的数据块被认为是一个有类型的,所以接受者可以接受不同类型的数据块
3.对比管道,消息队列的消息最大长度也是有一定上限的(MSGMAX),每个消息队列的字节数是有上限的(MSGMNB),系统上消息队列的总数也有一个上限(MSGMNI)
我们来看一下消息队列的结构:
struct msqid_ds {
struct ipc_perm msg_perm; /* 共有的IPC结构体 */
struct msg *msg_first; /* 消息队列头指针 */
struct msg *msg_last; /* 消息队列尾指针 */
__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; /* 消息的总数 */
unsigned short msg_qbytes; /* 消息队列的最大长度 */
__kernel_ipc_pid_t msg_lspid; /* 最后一次发送进程的PID */
__kernel_ipc_pid_t msg_lrpid; /* 最后一次发送消息的PID */
};
我们可以看到消息队列在系统中的结构,有一个共享的结构体 struct ipc_perm ;这个结构体中是怎么样的结构呢?
struct ipc_perm
{
__kernel_key_t key; //每个消息队列唯一的标识 key
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
我们了解了消息队列的结构,下面我们看看具体的应用:
发送数据:
int msgsnd(int id, //msgget返回的id
const void* msgp, //数据的起始地址
size_t msgsz, //有效数据数据大小,不包括通道号
int msgflg); // 0
取出数据:
ssize_t msgrcv(int msqid,// id
void *msgp,//收到数据放这里
size_t msgsz,//装数据的地方大小,不包括通道号
long msgtyp,//从哪个通道读数据
int msgflg);// 0 IPC_NOWAIT
删除消息队列:
int msgctl(int msqid,//id
int cmd,//IPC_RMID
0);//删除消息队列不关注这个参数
comm.h
#ifndef _COMM_H
#define _COMM_H
#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
struct msgbuf
{
long mtype;
char mtext[1024];
};
int createMsgQueue();
int getMsgQueue();
int destroyMsgQueue(int msgid);
int sendMsg(int msgid,int who,char *msg);
int recvMsg(int msgid,int recvType,char out[]);
#endif
~
comm.c
#include"comm.h"
//创建消息队列 msgget
static int commMsgQueue(int flag)
{
key_t key = ftok(PATHNAME,PROJ_ID);
if(key < 0)
{
perror("ftok\n");
return -1;
}
int msgid = msgget(key,flag);
if(msgid < 0)
{
perror("msgget\n");
return -1;
}
return msgid;
}
//创建消息队列
int createMsgQueue()
{
return commMsgQueue(IPC_CREAT|IPC_EXCL|0666);
}
//获取消息队列
int getMsgQueue()
{
return commMsgQueue(IPC_CREAT);
}
//销毁消息队列
int destroyMsgQueue(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl\n");
return -1;
}
return 0;
}
server.c
#include "comm.h"
#include<stdio.h>
#include<fcntl.h>
int main()
{
int msgid = createMsgQueue();
char buf[1024];
while(1)
{
buf[0] = 0;
recvMsg(msgid,CLIENT_TYPE,buf);
printf("client # %s\n",buf);
printf("please enter# ");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf));
if(s < 0)
{
buf[s-1] = 0;
sendMsg(msgid,SERVER_TYPE,buf);
printf("send done, wait recv...\n");
}
}
destroyMsgQueue(msgid);
}
client.c
#include "comm.h"
int main()
{
int msgid = getMsgQueue();
char buf[1024];
while(1)
{
buf[0] = 0;
printf("please enter#");
fflush(stdout);
ssize_t s = read(0,buf,sizeof(buf));
if(s < 0)
{
printf("cleint\n");
buf[s - 1] = 0;
sendMsg(msgid,CLIENT_TYPE,buf);
printf("send done,wait recv...\n");
}
recvMsg(msgid,SERVER_TYPE,buf);
printf("server # %s\n",buf);
}
return 0;
}
消息队列的存在是有限制的,我们该怎么查看消息队列和删除消息队列呢?
ipcs -q 查看系统中消息队列
还有其他选项
删除消息队列:
ipcrm -q msgid