为什么这个不是极简版聊天了呢?因为它加强了,不像管道实现的聊天,只能一个人说话,而另一个人只能静静地看着。而消息队列实现的可以两个人正常交流。
在此之前,我们先来了解一下消息队列:
*unix早期通信机制之一的信号能够传送的信息量有限,管道则只能传送无格式的字节流,这无疑会给应用程序开发带来不便。消息队列(也叫做报文队列)则克服了这些缺点。
*消息队列就是一个消息的链表.可以把消息看作一个记录,具有特定的格式.进程可以向中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息
*消息队列的内核持续性要求每个消息队列都在系统范围内对应唯一的键值,所以,要获得一个消息队列的描述字,必须提供该消息队列的键值
键值
key_t ftok (char*pathname, char proj)
功能: 返回文件名对应的键值。
pathname: 文件名
proj: 项目名(不为0即可)
打开/创建
int msgget(key_t key, int msgflg)
key:键值,由ftok获得。
msgflg:标志位。
返回值:与健值key相对应的消息队列描述字
IPC_CREAT
创建新的消息队列
IPC_EXCL
与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。
IPC_NOWAIT
读写消息队列要求无法得到满足时,不阻塞
与FIFO的区别
在极简聊天中,只需要一个人进行写操作,另一个人进行读操作,所以只需要定义一个buf数组就好了,但是这次是要实现双方都能进行读写,而我们使用进程(暂时不涉及线程),一个进程只能干一件事(读或写),目前有两个.c文件(即两个进程),所以需要使用fork来创建进程,是每个进程程序中有两个进程。那么,就必须设置好这四个进程哪两个写,哪两个读,为了让一个进程写的内容保证让另一个进程读到,就需要在它俩之间创建一个“暗号”,就是消息类型,所以数组显然不可取了,我们得用结构体。
消息格式:
struct msgbuf
{
long mtype;/消息类型/
char mtext[100]; /消息数据的首地址/
}
下面开始实现:
MassageQueue1.c
#include
#include
#include
#include
#include
#include
#include
#define MSGKEY 1234 //键值
struct msgbuf
{
long mtype;
char mtext[100];
};
int main()
{
int msgid;
int ret;
struct msgbuf buf; //注意buf是结构体
pid_t pid;
msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL); //创建消息队列
if(-1 == msgid) //创建失败返回-1
{
perror("msgget");
exit(1);
}
pid = fork(); //创建子进程,注意必须在创建消息队列之后
if (-1 == pid) //创建失败返回-1
{
perror("fork");
exit(1);
}
else if (0 == pid) //pid为0,则进入子进程
{
while(1)
{
memset(&buf, 0, sizeof(buf)); //先清空缓冲区
scanf("%s", buf.mtext);
buf.mtype = 5; //消息类型(发)为5,可任意设置,但必须与后面一致
ret = msgsnd(msgid, &buf, sizeof(buf.mtext), 0); //发送消息
if (-1 == ret) //发送失败
{
perror("msgsnd");
exit(1);
}
if (!strncmp(buf.mtext, "bye", 3)) //以bye结束
{
/* 结束时通知父进程,要让四个进程全部死亡 */
buf.mtype = 3; //父进程(收)的消息类型
ret = msgsnd(msgid, &buf, sizeof(buf.mtext), 0);//服进程接收消息
if(-1 == ret)
{
perror("msgsnd");
exit(1);
}
break;
}
}
}
else //否则,进入父进程
{
while(1)
{
memset(&buf, 0, sizeof(buf));
ret = msgrcv(msgid, &buf, sizeof(buf.mtext), 3, 0);//父进程接收消息
if (-1 == ret)
{
perror("msgrcv");
exit(1);
}
if (!strncmp(buf.mtext, "bye", 3)) //以bye结束
{
kill(pid, SIGKILL);
break;
}
printf("Receive : %s\n", buf.mtext); //把收到的消息打印
}
waitpid(pid, NULL, 0); //父进程等待
}
msgctl(msgid, IPC_RMID, NULL); //销毁消息队列
return 0;
}
MassageQueue2.c
#include
#include
#include
#include
#include
#include
#include
#define MSGKEY 1234
struct msgbuf
{
long mtype;
char mtext[100];
};
int main()
{
int msgid;
int ret;
struct msgbuf buf;
pid_t pid;
msgid = msgget(MSGKEY, 0); //消息队列已经创建过了,不用再创建
if(-1 == msgid)
{
perror("msgget");
exit(1);
}
pid = fork(); //创建子进程
if (-1 == pid)
{
perror("fork");
exit(1);
}
else if (0 == pid) //子进程执行部分
{
while(1)
{
memset(&buf, 0, sizeof(buf)); //初始化缓冲区
scanf("%s", buf.mtext);
buf.mtype = 3; //消息类型为3,与另一个程序中父进程对应,这里是发消息
ret = msgsnd(msgid, &buf, sizeof(buf.mtext), 0); //发送消息
if (-1 == ret) //发送失败
{
perror("msgsnd");
exit(1);
}
if (!strncmp(buf.mtext, "bye", 3)) //以bye结束
{
/* 结束时发消息给父进程 */
buf.mtype = 5;
ret = msgsnd(msgid, &buf, sizeof(buf.mtext), 0);
if (-1 == ret)
{
perror("msgrcv");
exit(1);
}
break;
}
}
}
else //父进程执行部分
{
while(1)
{
memset(&buf, 0, sizeof(buf));
ret = msgrcv(msgid, &buf, sizeof(buf.mtext), 5, 0);
if (-1 == ret)
{
perror("msgrcv");
exit(1);
}
if (!strncmp(buf.mtext, "bye", 3)) //以bye结束
{
kill(pid, SIGKILL); //杀死子进程
break;
}
printf("Receive : %s\n", buf.mtext); //打印收到的消息
}
waitpid(pid, NULL, 0); //等待子进程
return 0;
}
}
第一个程序中的子进程发送消息,第二个程序中的父进程接收消息
第二个程序中的子进程发送消息,第一个程序中的父进程接收消息