进程间通信(十)——消息队列应用:不同进程之间的点对点通信

消息队列应用

不同进程之间的点对点通信

  • 不同进程之间通过各自指定的消息类型点对点通信
  • 不需要经过服务器“中转”分发,由内核充当"代理人"角色
  • 不同的进程可以操作一个消息队列
  • 各自发送、接收自定义类型的消息,互不影响

int msgget(key_t key, int msgflg);

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

  1. 使用消息队列时,首先要创建一个消息队列
  2. 使用msgget之前首先要获取一个key值
    • 获取key值的方法有三种:
      1. 直接给key赋一个整数值
      2. 直接放一个宏IPC_PRIVATE
      3. ftok生成一个key值
    • msgflg参数
      1. IPC_CREAT:如果没有与指定的key对应消息队列,则创建一个
    • 成功返回消息队列ID(msqid)
  3. 数据交换
    1. 发送数据

      1. msgsnd()系统调用向消息队列写入一条消息
      2. 参数分析
        1. msqid是由msgget返回的标识符ID
        2. 参数msgp指向用户定义的缓冲区。
        3. 它的第一个成员必须是一个指定消息类型的long型,后面跟着消息文本的内容。
        4. 第三个参数msgsz指定了mtext字段中包含的字节数
        5. 最后一个参数msgflg是一组标志位的位掩码,用于控制msgsnd的行为。只定义了IPC_NOWAIT一个标志位。
        6. struct msgbuf { 
          	long mtype; /*消息类型,必须大于 0*/
                  char mtext[1]; /*消息体,不一定是字符数组,可以是任意结构 */
          };
          
    2. 接收数据

      1. msgrcv()从消息队列中读去一条消息并放到msgp缓冲区
      2. 其中前三个参数与msgsnd的含义是一致的。
      3. msgrcv调用进程也需要定义结构体,而结构体的定义要和发送端的定义一致,并且第一个字段必须是long类型
      4. 第4个参数msgtyp是消息队列的精华,提取消息时,可以选择进程感兴趣的消息类型。
      5. 当msgtyp的值大于0时,会将消息队列中第一条mtype值等于msgtyp的消息取出。通过指定不同msgtyp,多个进程可以在同一个消息队列中挑选各自感兴趣的消息。一种常见的场景是各个进程提取和自己进程ID匹配的消息。
      6. 第5个参数是可选标志位。
      7. msgrcv函数调用成功时,返回消息体的大小;失败时返回-1,并且设置errno。

client1.c

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define handle_error(s) \
			{perror(s);exit(EXIT_FAILURE);}
#define MSG_TYPE1 1
#define MSG_TYPE2 2

struct msgbuf {
	long mtype;
	char mtext[80];
};

int main(int argc, char *argv[])
{
 	key_t key = 510;
	int msg_id = msgget(key, IPC_CREAT | 0666);
	struct msgbuf msg1,msg2;

	int ret_from_fork;
	ret_from_fork = fork();
	if(ret_from_fork == -1)
			handle_error("fork")
	else if (ret_from_fork == 0)
	{
		while(1)
		{
			fgets(msg1.mtext, 80, stdin);
			msg1.mtype = MSG_TYPE1;
			msgsnd(msg_id, &msg1, 80, 0);
		}
	}		
	else 
	{
		while(1)
		{
			memset(&msg2, 0, sizeof(msg2));
			if (msgrcv(msg_id, (void *)&msg2, \
						sizeof(msg2.mtext), MSG_TYPE2, 0) == -1)
					handle_error("msgrcv");
			printf("client2:%s\n", msg2.mtext);
		}	
	}	
	msgctl(msg_id, IPC_RMID, NULL);
	return 0;
}

client2.c

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define handle_error(s) \
			{perror(s);exit(-1);}

#define MSG_TYPE1 1
#define MSG_TYPE2 2

struct msgbuf {
	long mtype;
	char mtext[80];
};

int main(int argc, char *argv[])
{
    key_t key = 510;
	int msg_id = msgget(key, IPC_CREAT | 0666);
	struct msgbuf msg1, msg2;

	int ret_frm_fork;
	ret_frm_fork = fork();
	if (ret_frm_fork == -1)
			handle_error("fork")		
	else if (ret_frm_fork == 0)
	{
		while(1)
		{
			fgets(msg2.mtext, 80, stdin);
			msg2.mtype = MSG_TYPE2;
			msgsnd(msg_id, (void *)&msg2, 80, 0);
		}
	}
	else
	{
		while(1)
		{
			memset(&msg1, 0, sizeof(msg1));
			if (msgrcv(msg_id, (void *)&msg1, \
								sizeof(msg1.mtext), MSG_TYPE1, 0) == -1)
					handle_error("msgrcv");
			printf("client1:%s\n", msg1.mtext);
		}	
	}
	return 0;
}

小结

消息队列与FIFO比较

  • 引用方式
    • 用来标识消息队列的是一个key,而不是普通文件所用的pathname
    • 用来引用消息队列的句柄是一个由msgget()调用返回的标识符
    • 这些标识符类似于普通文件I/O通过open返回的文件描述符
  • 传输的数据
    • FIFO发送的数据是流式数据,raw数据
    • 通过消息队列进行的通信是面向消息的
    • 除了包括数据之外,每条消息还有一个用整数表示的类型
  • 消息队列比FIFO优越的地方
    • 消息队列双方通过消息通信,无需花费精力从字节流中解析出完整信息
    • 每条消息都有type字段,read进程可通过消息类型选择自己感兴趣的消息

消息队列的优点

  • 降低系统耦合

    • 生产者-消费者模式,自助餐模式,多个读写进程通过容器建立联系、互不影响、实现解耦
    • 消息跟平台语言无关
  • 提速系统性能

    • 非核心流程异步化,非阻塞模式节省时间,不需要双方同时在线
  • 广播:

    • 一个消息可以发送给多个进程,只需要发送到队列就可以了
  • 削峰:

    • 生产者消费者的负载平衡、秒杀活动
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值