消息隊列
消息隊列是消息的鏈接表,存儲在內核中,,由消息隊列標識符標識。
在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:刪除消息隊列
- IPC_SET :在進程有足夠權限的前提下,把消息隊列的當前關聯值設置爲msqid_ds數據結構中給出的值
- IPC_STAT:把msqid_ds結構中的數據設置爲消息隊列的當前關聯值
- 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;
}