Unix消息队列实例

我们创建三个文件,一个recieve.c和pa.c,pb.c 。用recieve.c来监听pa.c和pb.c发送的消息:

recieve.c代码:

cpp 复制代码
#include<t_stdio.h>
 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#define MSG_TYPE	0
#define MSG_TYPE_A	10
#define MSG_TYPE_B	11//自定义消息类型


int main(int argc,char * argv[]){
    //定义自己的消息数据结构体
    struct  msgmbuf
    {
        long mtype;
        char mtext[100];
        int pid;
    };
    
    //创建key值
    key_t key= ftok(".",1);
    if(key==-1)E_MSG("ftok",-1);

      //创建消息队列, 如果内核不存在队列就创建
    int msg= msgget(key,IPC_CREAT|0666);
    if(msg==-1)E_MSG("msgget",-1);
        
    //定义一个消息变量用来接收消息
      struct msgmbuf m_msg;
     //定义一个整数用来获取监听信息的字节数
     int retval; 

     //开始监听
     while(1){

        retval= msgrcv(msg,&m_msg,sizeof(m_msg),MSG_TYPE, IPC_NOWAIT);//IPC_NOWAIT,不等待,即不阻塞
      if((retval == -1))
		{
		if (errno == ENOMSG) { // 没有消息
                continue; // 跳过本次循环,继续监听
            } else {
                perror("msgrcv error");
                return -1;
		}
        }
        else if(retval>0)
        {
            printf("[PID:%d], msg type:%ld, msg:%s\n",m_msg.pid,m_msg.mtype,m_msg.mtext);
            if(strncmp(m_msg.mtext, "exit", 4) == 0)//可以指定长度为4时strlen(msg.mtext)才进行比较,仅仅对exit生效
			{
				break;
			}
        }
     } 
    //删除消息队列
    msgctl(msg,IPC_RMID, NULL);
    return 0;
}

pa.c

cpp 复制代码
#include <t_stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


#define MSG_TYPE_A	10
struct msgmbuf 
{
    long mtype;       /* message type, must be > 0 */
    char mtext[100];    /* message data */
	int pid;
};

int main(void){
    //创建key值
    key_t key= ftok(".",1);
    if(key==-1)E_MSG("ftok",-1);

      //创建消息队列, 如果内核不存在队列就创建
   int msg_id = msgget(key, 0);
    if (msg_id == -1) {
        // 如果获取失败,尝试创建消息队列
        msg_id = msgget(key, IPC_CREAT | 0x0666);
        if (msg_id == -1) E_MSG("msgget", -1);
    }

        
    //定义一个消息变量用来接收消息
      struct msgmbuf msg;
      msg.pid=getpid();//获取当前进程pid
      msg.mtype=MSG_TYPE_A;


      //开始循环写入
      while(1){
        //写入数据
        fgets(msg.mtext,sizeof(msg.mtext),stdin);
        //发送数据
        msgsnd(msg_id,&msg,sizeof(msg),0);
         
         if(strncmp(msg.mtext, "exit", 4) == 0)
		{
			break;
		}
        
      }
      msgctl(msg_id, IPC_RMID, NULL);

    return 0;
}

理解:

监听代码(接收者)

  1. 创建Key值

    • ftok函数用来生成一个唯一的key值,这个值基于一个文件(在这里是当前目录.)和一个项目标识符(这里是1)。
    • 这个key值用于标识消息队列,确保发送者和接收者能够访问同一个队列。
  2. 创建消息队列

    • msgget函数用于创建一个新的消息队列或获取一个已经存在的消息队列的标识符。 -IPC_CREAT标志告诉系统如果队列不存在则创建它,0666是队列的权限。
  3. 定义消息结构体

    • msgmbuf结构体定义了消息的类型(mtype)和消息内容(mtext)以及发送进程的PID。
  4. 消息接收循环

    • msgrcv函数用于从消息队列中接收消息。
    • IPC_NOWAIT标志表示如果队列中没有消息,msgrcv不会阻塞,而是立即返回。
    • 如果msgrcv返回-1,并且错误码是ENOMSG,表示队列中没有消息,程序继续循环监听。
    • 如果接收到消息,程序会打印出消息内容和类型。
    • 如果消息内容是"exit",程序会跳出循环。
  5. 删除消息队列

    • msgctl函数用于控制消息队列,IPC_RMID标志表示删除消息队列。

发送消息代码(发送者)

  1. 创建Key值

    • 和接收者一样,发送者也需要创建一个key值来访问同一个消息队列。
  2. 获取或创建消息队列

    • 发送者尝试获取一个已经存在的消息队列的标识符。
    • 如果获取失败,它会尝试创建一个新的消息队列。
  3. 定义消息结构体并初始化

    • msgmbuf结构体用于存储发送的消息。
    • 进程ID(PID)被存储在消息结构体中,以便接收者知道哪个进程发送了消息。
  4. 消息发送循环

    • 程序从标准输入读取字符串,并将其存储在消息结构体的mtext字段中。
    • msgsnd函数用于将消息发送到消息队列。
    • 如果输入的是"exit",程序会跳出循环。
  5. 删除消息队列

    • 和接收者一样,发送者在结束前也会删除消息队列。

为什么两个代码都要创建Key值和消息队列?

  • 创建Key值

    • 在Unix系统中,每个IPC结构(如消息队列、共享内存、信号量)都有一个与之关联的唯一键(key)。这个键是由ftok生成的,它确保了不同的进程能够通过同一个键访问同一个IPC结构。
    • 发送者和接收者需要使用相同的key值来确保它们能够互相通信。
  • 创建消息队列

    • 消息队列是IPC的一种形式,它允许一个或多个进程写入和读取数据。
    • 在Unix中,消息队列是由操作系统管理的,因此需要使用msgget来创建或访问一个已经存在的消息队列。
    • 发送者和接收者都需要访问消息队列,因此它们都需要创建或获取队列的标识符。

这两个程序通过消息队列进行通信。发送者将消息放入队列,接收者从队列中读取消息。它们通过使用相同的key值来确保它们可以访问同一个消息队列。
扩展:我当时一直不明白两个文件为什么都要创建key值,以及为什么创建相同key值,以及如何实现利用消息队列进行通信:

在Unix-like系统中,ftok函数用于从一个路径名和一个项目标识符(通常是整数)生成一个key值。这个key值用于进程间通信(IPC),比如消息队列、共享内存和信号量。

ftok函数的定义如下:

复制

key_t ftok(const char *pathname, int proj_id);
  • pathname是一个指向现有路径名的指针,通常是文件路径。
  • proj_id是一个整数,用于进一步区分不同的key值。

重要的是要理解,ftok生成的key值不仅取决于pathname,还取决于proj_id。这意味着即使是在相同的路径下,只要proj_id不同,生成的key值也会不同。相反,即使是在不同的路径下,只要pathnameproj_id相同,生成的key值也会相同。

因此,一个文件路径并不限制只能创建一个key值。实际上,你可以使用相同的文件路径和不同的proj_id来生成多个不同的key值,每个key值可以用于创建或访问不同的IPC资源。

例如:

复制

key_t key1 = ftok("/path/to/file", 1);
key_t key2 = ftok("/path/to/file", 2);

在上面的例子中,key1key2将会是不同的,即使它们是基于相同的文件路径生成的。

总结来说,ftok生成的key值是唯一的,只要pathnameproj_id的组合是唯一的。这意味着在不同的文件路径下可以生成相同的key值,只要proj_id相同;同时,在相同的文件路径下也可以生成不同的key值,只要proj_id不同。

相关推荐
真真-真真21 分钟前
WebXR
linux·运维·服务器
轩辰~43 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
wanhengidc1 小时前
短视频运营行业该如何选择服务器?
运维·服务器
s_yellowfish2 小时前
Linux服务器pm2 运行chatgpt-on-wechat,搭建微信群ai机器人
linux·服务器·chatgpt
vvw&2 小时前
如何在 Ubuntu 22.04 上安装 Ansible 教程
linux·运维·服务器·ubuntu·开源·ansible·devops
我一定会有钱2 小时前
【linux】NFS实验
linux·服务器
王铁柱子哟-2 小时前
解决 正在下载VS Code 服务器... 问题
运维·服务器
Ven%2 小时前
如何在防火墙上指定ip访问服务器上任何端口呢
linux·服务器·网络·深度学习·tcp/ip
泰伦闲鱼2 小时前
nestjs:GET REQUEST 缓存问题
服务器·前端·缓存·node.js·nestjs
是阿建吖!2 小时前
【Linux】基础IO(磁盘文件)
linux·服务器·数据库