linux远程开发——(IPC通信)消息队列的使用

目录

一、前言

二、认识消息队列

1、什么是队列?

2、队列动图演示

3、消息队列

三、消息队列相关函数

1、msgget()函数

2、msgsnd()函数

3、msgrcv()函数

四、实战演练(使用消息队列实现两个进程互相聊天)

1、发送消息端

2、接收消息端

3、实现效果

五、总结


一、前言

回顾一下关于进程间的通信(IPC,InterProcess Communication)我们学习了哪些

  • 1️⃣信号 :信号的使用
  • 2️⃣管道:管道的使用
  • 3️⃣共享内存:共享内存的使用

今天来学习一个新的IPC通信: 消息队列 

、认识消息队列

  • 学习消息队列之前我们先简单了解一下队列(Queue)

1、什么是队列?

        队列是一种 特殊的线性表 ,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作。进行 插入操作的端称为队尾 ,进行 删除操作的端称为队头

2、队列动图演示

  • 下面是博主用ppt做的动图,可以形象生动的演示队列 入队出队 的操作

记笔记

✏️:队列是一种特殊的 线性表  

✏️:数据只能从队列的 队尾入队,队头出队 

✏️:队列的特点是 先进先出 

3、消息队列

  • 消息队列其实就是一个严格意义上的队列,在消息入队出队过程中,保证这些消息严格有序

消息队列提供了一个 从一个进程向另外一个进程发送一块数据 的方法。

因为队列先进先出的特点,所以先发送的消息先收到, 保证 消息严格有序

消息队列也有管道一样的不足,就是 每个数据块的最大长度是有上限的 ,系统上全体队列的 最大总长度也有一个上限

一个进程发送的 消息会保留在消息队列中 ,直到另一个进程读取了队列中的消息,消息才会从消息队列 “出队” 。

、消息队列相关函数

  • 下面为创建消息队列和使用消息队列用到的几个函数

1、msgget()函数

  • 用来创建和访问一个消息队列,消息队列不存在则创建,存在则访问

头文件

#include
#include
#include

函数原型

int msgget(key_t key, int msgflg);

参数

key➡️某个消息队列的名字。

msgflg➡️由九个权限标志构成,用法和创建文件时使用的mode模式标志是一样的。

返回值

成功:将返回一个 非负整数 ,即该消息队列的 标识码 

失败:返回 “-1” 

  • 下面为创建消息队列的代码 
#include 
#include 
#include 
#include 
#include 

using namespace std;

int main()
{
	int msgid = 0;
	//消息队列不存在则创建  存在则访问
	msgid = msgget((key_t)1001, IPC_CREAT | 0777);

	if (msgid == -1)
	{
		perror("msgget error");
	}
	else
	{
		cout << "消息队列创建成功 " << "msgid = " << msgid << endl;
	}
	
	return 0;
}
  • 在linux下找到对应的main.cpp ,通过g++ 的方式编译, ./ 运行

linux远程开发——(IPC通信)消息队列的使用_第1张图片 

可以看见消息队列创建成功,且 id 为131072 

  • 通过命令: ipcs ,可以看见我们新创建出来的消息队列,权限为777, id 为131072 。

linux远程开发——(IPC通信)消息队列的使用_第2张图片

  • 可以通过命令 ipcrm -a 将创建出来的消息队列删掉

linux远程开发——(IPC通信)消息队列的使用_第3张图片 

2、msgsnd()函数

  • 用于把一条消息添加到消息队列里去

头文件

#include
#include
#include

函数原型

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

参数

msqid➡️由msgget() 函数返回的消息队列标识码。

msgp➡️是一个指针,指针指向准备发送的消息。

msgsz➡️指向的消息长度,这个长度不能保存消息类型的那个“long int”长整型计算在内。

msgflg➡️控制着当前消息队列满或到达系统上限时将要发生的事情,为0即可。

返回值

成功:返回

失败:返回 -1 

  • 发送的消息结构应该用如下结构体

消息结构体

struct msgbuf

{
        long mtype;       /* message type, must be > 0 */
        char mtext[50];    /* message data */
 };

注意

发送信息前应该 先用这个 mtype 确定消息的类型 ,接收消息时会用到这个 mtype。

 

3、msgrcv()函数

  • 作用是从一个消息队列里检索消息,即读取消息。读取一条消息,该消息就 “出队”。

头文件

#include
#include
#include

函数原型

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

参数

msqid➡️由msgget() 函数返回的消息队列标识码。

msgp➡️是一个指针,指针指向准备接收的消息。

msgsz➡️指向的消息长度,这个长度不能保存消息类型的那个“long int”长整型计算在内。

msgtyp➡️发送消息时指定的 msgtyp。

msgflg➡️控制着队列中没有相应类型的消息可供接收时将要发生的事,为0即可。

返回值

成功:返回 实际放到接收缓冲区里去的字符个数 

失败:返回 -1 

记笔记

✏️:msgrcv()函数是一个 阻塞函数 ,只有读到消息才会继续执行。

        好了,消息队列使用到的函数就介绍到这里了,接下来通过一个代码实战来巩固消化这些知识,话不多说

、实战演练(使用消息队列实现两个进程互相聊天)

1、发送消息端

发送消息端,通过控制台输入,先消息类型(msgtyp)为 1 的消息,再等待 接收 对方发过来的消息类型(msgtyp)为 2 的消息。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef struct sndMsgbuf 
{
	long mtype;       /* message type, must be > 0 */
	char mtext[50];    /* message data */
}MSGBUF;

int main()
{
	int msgid = 0;
	MSGBUF sendbuf;
	//消息队列不存在则创建  存在则访问
	msgid = msgget((key_t)1001, IPC_CREAT | 0777);

	if (msgid == -1)
	{
		perror("msgget error");
	}
	else
	{
		cout << "消息队列创建成功 " << "msgid = " << msgid << endl;
	}
	while (1)
	{
		//先发送消息
		//指定发送消息类型为 1
		sendbuf.mtype = 1;
		cin >> sendbuf.mtext;//控制台输入作为消息数据
		int res = msgsnd(msgid, &sendbuf, sizeof(sendbuf.mtext), 0);
		if (res == -1)
		{
			perror("msgsnd error");
		}
		else
		{
			cout << "消息已发送" << endl;
			cout << endl;
			bzero(&sendbuf, sizeof(sendbuf));

			//再接收消息
			//msgrcv为阻塞函数 接收消息类型为 2 的消息
			int res = msgrcv(msgid, &sendbuf, sizeof(sendbuf.mtext), 2, 0);
			if (res == -1)
			{
				perror("msgrcv error");
			}
			else
			{
				cout << "对方发来一条消息: " << sendbuf.mtext << endl;
				cout << endl;
				bzero(&sendbuf, sizeof(sendbuf));
			}
		}
	}
	return 0;
}

 

2、接收消息端

接收消息端,先等待 接收 对方发过来的消息类型(msgtyp)为 1 的消息,再通过控制台输入, 发送 消息类型(msgtyp)为 2 的消息。

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef struct rcvMsgbuf
{
	long mtype;       /* message type, must be > 0 */
	char mtext[50];    /* message data */
}MSGBUF;

int main()
{
	int msgid = 0;
	MSGBUF receivebuf;
	msgid = msgget((key_t)1001, IPC_CREAT | 0777);

	if (msgid == -1)
	{
		perror("msgget error");
	}
	else
	{
		cout << "消息队列创建成功 " << "msgid = " << msgid << endl;
	}
	while (1)
	{
		//先接收消息
		//msgrcv为阻塞函数 接收消息类型为 1 的消息
		int res = msgrcv(msgid, &receivebuf, sizeof(receivebuf.mtext), 1, 0);
		if (res == -1)
		{
			perror("msgrcv error");
		}
		else
		{			
			cout << "对方发来一条消息: " << receivebuf.mtext << endl;
			cout << endl;
			bzero(&receivebuf, sizeof(receivebuf));

			//再发送消息
			//指定发送消息类型为 2
			receivebuf.mtype = 2;
			cin >> receivebuf.mtext;//控制台输入作为消息数据
			int res = msgsnd(msgid, &receivebuf, sizeof(receivebuf.mtext), 0);
			if (res == -1)
			{
				perror("msgsnd error");
			}
			else
			{
				cout << "消息已发送" << endl;
				cout << endl;
				bzero(&receivebuf, sizeof(receivebuf));
			}
		}
	}
	return 0;
}

3、实现效果

  • 在linux下通过g++的方式进行编译,在用 ./ 的方式运行,测试结果如下

写端先发消息,然后读端收到消息后再发送给写端,循环往复完成回合制聊天。

linux远程开发——(IPC通信)消息队列的使用_第4张图片

、总结

✏️:由于队列先进先出的特点,可以保证 消息按序接收

✏️:发送消息端和接收消息端发送的 消息类型(msgtyp)要不一致 ,否则消息收发会乱套。

✏️:消息发送出去就存在消息队列里,只有当某个进程读取里面的内容时, 读几条消息就“出队”几条

✏️:每个数据(消息)块的最大长度是有上限的,系统上全体队列的最大总长度也有一个上限,即 无法进行大数据传输

✏️:msgrcv()函数是一个 阻塞函数 ,只有读到消息才会继续执行。

原创不易,转载请标明出处。

对您有帮助的话可以一键三连,会持续更新的(嘻嘻)。

你可能感兴趣的:(linux远程开发,消息队列,远程开发,vs,2019)