新聞中心
消息隊列
上次說到了進程間通信的管道,不過匿名管道有個缺點就是,只能做到有親緣關(guān)系的進程間通信,所以今天學(xué)習(xí)一個新的進程間通信方式——消息隊列。
成都創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站制作、成都網(wǎng)站設(shè)計、內(nèi)蒙古網(wǎng)絡(luò)推廣、微信小程序、內(nèi)蒙古網(wǎng)絡(luò)營銷、內(nèi)蒙古企業(yè)策劃、內(nèi)蒙古品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供內(nèi)蒙古建站搭建服務(wù),24小時服務(wù)熱線:18980820575,官方網(wǎng)址:www.cdcxhl.com
- 消息隊列提供了一個從一個進程向另外一個進程發(fā)送一塊數(shù)據(jù)的方法
- 每個數(shù)據(jù)塊都被認為是有一個類型,接收者進程接收的數(shù)據(jù)塊可以有不同的類型值
消息隊列也有管道一樣的不足,就是每個數(shù)據(jù)塊的最大長度是有上限的,系統(tǒng)上全體隊列的最大總長度也有一個上限
消息隊列函數(shù)
頭文件
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/msg.h>msgget
int msgget(key_t key, int msgflg);
作用:創(chuàng)建和訪問一個消息隊列
key:某個消息隊列的名字(類似于每個進程都有一個進程ID一樣)
msgflg:有9個權(quán)限標志構(gòu)成。它們的用法和創(chuàng)建文件時使用的mode標志是一樣的(比如:一個key已經(jīng)存在的消息隊列時,要使用IPC_CREAT | IPC_EXCL,就類似于文件操作的打開:O_CREAT | O_EXCL )。
返回值:成功將返回一個非負整數(shù),即該消息隊列的標識碼;失敗返回-1msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
作用:把一條消息加到消息隊列里面
msgid:由msgget函數(shù)返回的消息隊列標識碼
msgp:是一個指針,指向準備發(fā)送的消息
msgsz:msgp指向的消息長度,這個長度不能保存消息類型里的“l(fā)ong int”類型(下面會說)
msgflg:控制著當(dāng)前消息隊列滿或達到系統(tǒng)上限時將要發(fā)生的事情。
返回值:成功-0,失敗-1
1)struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ };
消息的指針就是指向這樣一個結(jié)構(gòu)的消息,這需要我們自己定義。但是,第一個一定得是long int,表示消息的類型。消息的類型是大于0的整數(shù)(當(dāng)然也是可以等于0的,但是這樣就意味著任何消息我都接收,不固定只收某一種類型的消息了)。
- msgtype=0返回隊列第一條信息
- msgtype>0返回隊列第一條類型等于msgtype的消息
- msgtype< 0返回隊列第一條類型小于等于msgtype絕對值的消息
msgrcv
作用:從一個消息隊列里檢索消息
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgid:由msgget函數(shù)返回的消息隊列標識碼
msgp:是一個指針,指向準備接收的消息
msgsz:msgp指向的消息長度,這個長度不能保存消息類型里的“l(fā)ong int”類型(下面會說)
msgflg:控制著隊列中沒有相應(yīng)類型的消息可供接收的時候?qū)⒁l(fā)生的事
msgtyp:可以實現(xiàn)接收優(yōu)先級的簡單形式
返回值:成功-返回實際放到接收緩沖區(qū)里的字符個數(shù),失敗- “-1”
msgflg有以下幾個值:
- msgflg=IPC_NOWAIT,隊列沒有可讀消息不等待,返回ENOMSG錯誤。
- msgflg=MSG_NOERROR,消息大小超過msgsz時被截斷
- msgtype>0且msgflg=MSC_EXCEPT,接收類型不等于msgtype的第一條消息。
msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
作用:消息隊列的控制函數(shù)
msgid:由msgget函數(shù)返回的消息隊列標識碼
cmd:將要采取的動作,簡單講常用的三個可取值:
msgid_ds數(shù)據(jù)結(jié)構(gòu)定義如下:
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (non-standard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
};
練習(xí)
試著寫一段吧~
發(fā)送端:msgsnd.c
struct msg_t
{
long mtype; //第一個必須是long,>=1
char acMsg[20];
};
//用ipcs
int main()
{
int msgid;
struct msg_t msg = {0};
//消息隊列的創(chuàng)建、打開、刪除
msgid = msgget(1000,IPC_CREAT);
if(msgid == -1)
{
perror("create msg");
}
printf("msgid = %d\n",msgid);
msg.mtype = 1;
strcpy(msg.acMsg,"hello");
msgsnd(msgid,&msg,sizeof(struct msg_t)-sizeof(long),0);
//msgctl(msgid,IPC_RMID,NULL);//也可以用命令ipcrm
return 0;
}
接收端:msgrcv.c
struct msg_t
{
long mtype; //第一個必須是long,>=1
char acMsg[20];
};
int main()
{
int msgid;
struct msg_t msg = {0};
//消息隊列的創(chuàng)建、打開、刪除
msgid = msgget(1000,0);
if(msgid == -1)
{
perror("open msg!\n");
}
printf("msgid = %d\n",msgid);
msgrcv(msgid,&msg,sizeof(struct msg_t)-sizeof(long),1,0);
printf("recv msg: %s.\n",msg.acMsg);
return 0;
}
運行:
開三個終端,一個運行msgsnd.c,一個運行msgrcv.c,一個用來查看消息隊列狀態(tài):
1、先發(fā)送:
2、查看:ipcs
3、讀?。?br/>
4、再查看(被取走了):
但是,也注意到了使用消息隊列,消息一旦被讀走,就沒了。
即時通訊小程序
使用消息隊列與共享內(nèi)存(后面會復(fù)習(xí))完成一個簡單的終端聊天程序,要求如下。
1.程序有一個server服務(wù)器,服務(wù)器有一個在線列表,當(dāng)終端登入時,將終端的進程ID作為用戶名稱添加到在線列表。(消息隊列和共享內(nèi)存)
2.終端登入時,獲取用戶的在線列表。(消息隊列、信號、和共享內(nèi)存)
3.終端登入后,進入聊天狀態(tài)。:(信號、共享內(nèi)存)
輸入#chat [pid],進入私聊模式
例如:#chat 1234,與終端1234進入私聊。只有終端1234才能接收消息。
輸入#chat 0,進入群聊模式。全部終端可以接收消息。
說明:1,2,3為基本功能,要求實現(xiàn)。4,5,6為附加功能,有能力同學(xué)可以嘗試實現(xiàn)。
4.終端登入時,服務(wù)器發(fā)送消息,通知其他在線終端,更新在線列表。(信號)
5.終端在聊天狀態(tài),輸入#user,列出在線用戶列表。
6.終端在聊天狀態(tài),輸入#exit,終端退出,服務(wù)器將終端的進程ID移出在線列表,并通知在線終端,更新在線列表。(消息隊列和共享內(nèi)存)
學(xué)了消息隊列,至少可以把用戶的登錄、退出完成。
代碼如下:
public.h
#ifndef _PUBLIC_H_
#define _PUBLIC_H_
#include < stdio.h>
#include < string.h>
#include < sys/types.h>
#include < sys/ipc.h>
#include < sys/msg.h>
typedef struct login_t
{
long type;
pid_t pid;
}LOGIN_T;
#define MSG_KEY 1
#define MSG_SIZE sizeof(LOGIN_T)-sizeof(long)
#endif
server.c
#include " public.h"
int main()
{
int msg_id;
LOGIN_T login = {0};
//創(chuàng)建用戶的消息隊列
msg_id = msgget(MSG_KEY,0);
if(msg_id == -1)
{
msg_id = msgget(MSG_KEY,IPC_CREAT);
if (msg_id == -1)
{
perror("server msgget");
return -1;
}
}
//一直監(jiān)聽,是否有用戶上線
while (1)
{
memset(&login,0,sizeof(LOGIN_T));
msgrcv(msg_id,&login,MSG_SIZE,0,0); //任何消息都接收
switch(login.type)
{
case 1:
printf("client %d is logining...\n",login.pid);
break;
case 2:
printf("client %d is exiting...\n",login.pid);
break;
}
}
return 0;
}
client.c
#include "public.h"
int main()
{
char acBuf[20] = "";
int msg_id;
LOGIN_T login = {0};
//打開消息隊列
msg_id = msgget(MSG_KEY,0);
if(msg_id == -1)
{
perror("client msgget");
return -1;
}
//登錄,寫消息隊列
login.type = 1; //設(shè)置登錄的消息類型為1
login.pid = getpid();
printf("%d is logining...\n",login.pid);
msgsnd(msg_id,&login,MSG_SIZE,0);
//等待寫
while(1)
{
putchar('#');
fflush(stdout);
scanf("%s",acBuf);
if (strcmp(acBuf,"quit") == 0)
{
login.type = 2; //設(shè)置退出的消息類型為2
msgsnd(msg_id,&login,MSG_SIZE,0);
break;
}
}
return 0;
}
運行:先運行服務(wù)器端,再運行多個客戶端
客戶端:
服務(wù)器:
本文名稱:IPC之消息隊列·即時通訊小程序(一)
網(wǎng)站網(wǎng)址:http://www.dlmjj.cn/article/ghchis.html