8.Linux网络编程-System V消息队列

2023-05-16

一:消息队列
消息队列是进程间通信的一种手段,进程产生的数据块以链表的形式存储在消息队列中,每个数据块都被认为是有一个类型,接收者进程接收的数据块可以有不同的类型。
消息队列有以下的几个限制,每个消息有最大长度不能超过MSGMAX,每个消息队列的总的字节数不能超过MSGMNB,消息队列的总数不能超过MSGMNI。

//查看消息队列限制的值
root@epc:~# cat /proc/sys/kernel/msgmax
8192
root@epc:~# cat /proc/sys/kernel/msgmnb
16384
root@epc:~# cat /proc/sys/kernel/msgmni
32000
root@epc:~# 

二:消息队列的数据结构
常用的IPC相关linux命令:
查看ipc:ipcs
删除:ipcrm -Q //只能删除非私有ipc,私有的ipc的key为0x0
ipcrm -q

//内核为每个IPC对象维护一个数据结构
struct ipc_perm {
   key_t          __key;       /* Key supplied to xxxget(2) */
   uid_t          uid;         /* Effective UID of owner */
   gid_t          gid;         /* Effective GID of owner */
   uid_t          cuid;        /* Effective UID of creator */
   gid_t          cgid;        /* Effective GID of creator */
   unsigned short mode;        /* Permissions */
   unsigned short __seq;       /* Sequence number */
};

//linux查看IPC的命令
root@epc:~# ipcs

------ Message Queues --------	=>消息队列
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------	=>共享内存
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------	=>信号量
key        semid      owner      perms      nsems 

//消息队列结构
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 inqueue (nonstandard) */
   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) */
};

三:消息队列的函数

//头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
//msgget
int msgget(key_t key, int msgfl_g); 
/*
@msgget:用来创建和访问一个消息队列
@int:成功返回非负整数,是该消息队列的标识码,即id,失败返回-1
当返回-1时,errno的值有以下情况:
	EACCESS(权限不允许)
	EEXIST(队列已经存在,无法创建)
	EIDRM(队列标志为删除)
	ENOENT(队列不存在)
	ENOMEM(创建队列时内存不够)
	ENOSPC(超出最大队列限制)
@key:整数或者IPC_PRIVATE(0),消息队列的名字。
	IPC_PRIVATE,表示私有的,意味消息队列只能用于亲缘进程通信。
@msgfl_g:九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的;
如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符;
如果IPC_EXCL和IPC_CREAT一起使用,如果队列已经存在则返回一个失败值-1,否则创建一个新的消息队列,IPC_EXCL单独使用是没有用处的。
 */
//msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
/*
@msgget:用来查看/设置/删除消息队列
@int:成功返回0,失败返回-1
@cmd:操作类型,可取值IPC_STAT或者IPC_SET或者IPC_RMID
	IPC_STAT:获取消息队列的状态,并将结果输出到buf中
	IPC_SET:在进程有足够权限的条件下,把消息队列的当前关联值设置为msqid_ds数据结构中给出的值,通过导入buf的值进行设置
	IPC_RMID:删除消息队列。(此时buf可置为null)
@buf:输出输入消息队列状态的空间
 */
//msgsnd
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgfl_g);
/*
@msgsnd:把一条消息添加到消息队列中去返回值
@int:成功返回0,失败返回-1
@msqid:消息队列的标志,即id
@msgp:数据块内容
	数据块内容必须是如下结构:
	struct msgbuf{
		long mtype; //接收者函数通过这个长整数确定消息的类型        
	  char mtext[X]; // 这里存放具体的消息内容,X必须小于等于MSGMAX
	};
@msgsz:msgp中mtext长度,这个长度不含保存消息类型的那个long int长整型,此值必须小于MSGMAX,否则返回-1
@msgfl_g:控制位,控制当前消息队列满或到达系统上限时将要发生的事情。
   为0表示等待;msgfl_g =IPC_NOWAIT。表示队列满不等待,返回EAGAIN。
 */
//msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgfl_g)
/*
@msgrcv:从一个消息队列中接收消息
@ssize_t :成功返回实际接收到放到接收缓冲区里去的字符个数,失败返回-1
@msqid:消息队列的标志,即id
@msgp:数据块内容输出
@msgsz:msgp指向的消息长度,不包含消息类型的长度
@msgtyp:接收的消息类型
	=0:表示返回队列里的第一条消息
	>0:返回队列第一条类型等于msgtype的消息
	<0:返回队列第一条类型小于等于msgtype绝对值的消息
@msgfl_g:控制位,控制着队列中没有相应类型的消息可工接收时将要发生的事情。
	msgfl_g=IPC_NOWAIT:队列没有可读消息不等待,返回ENOMSG错误
	msgfl_g=MSG_NOERROR:消息大小超过msgsz时被截断
	msgtype>0且msgfl_g=MSG_EXCEPT:接收类型不等于msgtype的第一条消息
 */

四:利用System V消息队列实现简单的回射客户端/服务器

对于服务器端来说,接收到一个消息结构体的类型如果为1,表示是客户请求,而mtex 字段的前4个字节存放着不同进程的pid ,后续字节才是真正的数据,服务器回射客户端时,将pid 作为类型,mtex 为实际数据,客户端只接收对应类型的数据,故可以区分不同客户端。

//server
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<string.h>

#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	}while(0)

#define MSGMAX 8192

struct Msgbuf{
    long mtype;
    char mtext[MSGMAX];
};

void echo_ser(int msgid)
{
    struct Msgbuf msg;
    memset(&msg, 0, sizeof(msg));
    int nrcv = 0;
    while (1)
    {
        if ((nrcv = msgrcv(msgid, &msg, MSGMAX, 1, 0)) < 0)
        	ERR_EXIT("msgrcv");
        int pid = *((int *)msg.mtext);
        printf("recv from pid=%d, length=%d, buf=%s\n", pid, nrcv, msg.mtext+4);
        msg.mtype = pid;
        msgsnd(msgid, &msg, nrcv, 0);
        memset(&msg, 0, sizeof(msg));
    }
}

int main(int argc, char *argv[])
{
    int msgid;
    msgid = msgget(1234, IPC_CREAT | 0666);
    if (msgid == -1)
        ERR_EXIT("msgget");

    echo_ser(msgid);
    return 0;
}

//client
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define MSGMAX 8192

struct Msgbuf
{
    long mtype;
    char mtext[MSGMAX];
};

void echo_cli(int msgid)
{
    int nrcv;
    int pid = getpid();
    struct Msgbuf msg;
    memset(&msg, 0, sizeof(msg));
    while (fgets(msg.mtext+4, MSGMAX, stdin) != NULL)
    {
    	msg.mtype = 1;
    	*((int *)msg.mtext) = pid;
        if (msgsnd(msgid, &msg, 4 + strlen(msg.mtext + 4), IPC_NOWAIT) < 0)
            ERR_EXIT("msgsnd");

        if ((nrcv = msgrcv(msgid, &msg, MSGMAX, pid, 0)) < 0)
            ERR_EXIT("msgsnd");
        fputs(msg.mtext + 4, stdout);
        memset(&msg, 0, sizeof(msg));
    }
}

int main(int argc, char *argv[])
{

    int msgid;
    msgid = msgget(1234, 0);
    if (msgid == -1)
        ERR_EXIT("msgget");

    echo_cli(msgid);

    return 0;
}

五:实现每个客户一个队列
在这里插入图片描述

//server
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<wait.h>
#include<unistd.h>
#include<errno.h>

#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	}while(0)

#define MSGMAX 8192
#define MSGQ_KEY 1234
#define MTYPE_GLOBAL 1

struct Msgbuf{
    long mtype;
    char mtext[MSGMAX];
};

void Handler(int signo)
{
	pid_t pid;
	int stat;
	while ((pid = waitpid(-1, &stat, WNOHANG)) > 0);
	return;
}

/*
 * void echo_ser(int msgid);
 * @echo_ser:实现用单独的队列和每个客户进行交互,为了避免单线程处理一个客户信息等待时间过长,引入了用子进程进行处理的机制;
 *
 * */
void echo_ser(int msgid)
{
	signal(SIGCHLD, Handler);
    struct Msgbuf msg;
    memset(&msg, 0, sizeof(msg));
    int nrcv = 0;
    while (1)
    {
    	do
    	{
    	//处理子进程退出时,会发生中断,导致msgrcvc返回错误;
    		nrcv = msgrcv(msgid, &msg, MSGMAX, MTYPE_GLOBAL, 0);
    	}while(nrcv<0 && errno==EINTR);
        if (nrcv < 0)
        	ERR_EXIT("msgrcv");
        int clientMsgid = *((int *)msg.mtext);
        printf("recv from clientMsgid=%d, length=%d, buf=%s", clientMsgid, nrcv, msg.mtext+4);

        pid_t pid = fork();
        if (pid < 0)
        	ERR_EXIT("fork");
        else if (pid == 0)
        {
            msg.mtype = MTYPE_GLOBAL;
            msgsnd(clientMsgid, &msg, nrcv, IPC_NOWAIT);
            memset(&msg, 0, sizeof(msg));
            break;
        }
        memset(&msg, 0, sizeof(msg));
    }
}

int main(int argc, char *argv[])
{
    int msgid;
    msgid = msgget(MSGQ_KEY, IPC_CREAT | 0666);
    if (msgid == -1)
        ERR_EXIT("msgget");

    echo_ser(msgid);
    return 0;
}

//客户端
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>

#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define MSGMAX 8192
#define MSGQ_KEY 1234
#define MTYPE_GLOBAL 1
struct Msgbuf
{
    long mtype;
    char mtext[MSGMAX];
};

void echo_cli(int msgidForWr, int msgidForRead)
{
    int nrcv;
    int pid = getpid();
    struct Msgbuf msg;
    memset(&msg, 0, sizeof(msg));
    while (fgets(msg.mtext+4, MSGMAX, stdin) != NULL)
    {
    	msg.mtype = MTYPE_GLOBAL;
    	*((int *)msg.mtext) = msgidForRead;
        if (msgsnd(msgidForWr, &msg, 4 + strlen(msg.mtext + 4), IPC_NOWAIT) < 0)
            ERR_EXIT("msgsnd");

        if ((nrcv = msgrcv(msgidForRead, &msg, MSGMAX, MTYPE_GLOBAL, 0)) < 0)
            ERR_EXIT("msgsnd");
        fputs(msg.mtext + 4, stdout);
        if (msg.mtext[4] == '\n') break;
        memset(&msg, 0, sizeof(msg));
    }
    msgctl(msgidForRead, IPC_RMID, NULL);
}

int main(int argc, char *argv[])
{
    int msgidForWr, msgidForRead;
    msgidForWr = msgget(MSGQ_KEY, IPC_CREAT);
    if (msgidForWr == -1)
        ERR_EXIT("msgget");

    msgidForRead = msgget(IPC_PRIVATE, 0666 | IPC_CREAT |IPC_EXCL);
    if (msgidForWr == -1)
        ERR_EXIT("msgget");

    echo_cli(msgidForWr, msgidForRead);

    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

8.Linux网络编程-System V消息队列 的相关文章

  • 树莓派 Nginx+php7搭建网站

    1 6 安装Nginx 43 php7 并且启动 sudo apt get update sudo apt get install nginx sudo apt get install php7 0 fpm php7 0 cli php7
  • Python的GUI编程(一)Label(标签)

    常用Python GUI库有 xff1a 1 Tkinter 2 WxPython 3 PyQT 4 pyGtk 5 Jython 6 MFC 7 PythonCard 8 Dabo 9 AnyGui 10 WPY 11 IronPytho
  • 使用 Drools 规则引擎实现业务逻辑

    要求施加在当今软件产品上的大多数复杂性是行为和功能方面的 xff0c 从而导致组件实现具有复杂的业务逻辑 实现 J2EE 或 J2SE 应用程序中业务逻辑最常见的方法是编写 Java 代码来实现需求文档的规则和逻辑 在大多数情况下 xff0
  • Python的GUI编程(九)Menu(菜单)OptionMenu(为可选菜单)

    在用户界面程序中 菜单以图标和文字的方式展示可用选项 用鼠标选择一个选项 程序的某个行为既被触发 这种行为通常包括比如 打开 保存文件 退出程序 等功能 上下文菜单是一种根据用户当前所在程序位置 上下文 动态生成的菜单 简单程序 xff1a
  • Linux同时使用无线和有线网络

    有时候我们可能同时插入了无线网卡和有线网络 xff0c 但是默认是通过有线网络连接的外网 如果同时需要使用无线网络连接外网以及使用有线网络进行高速局域网连接的话需要将默认网关设置成无线网络 xff1a 使用ip route show分别查看
  • 【Python】计算两个日期相差天数

    Python 计算两个日期相差天数
  • 《Zoom to learn Learn to zoom》学习笔记

    创新点 xff1a 使用真实原始LR和HR图像作为数据进行训练网络 xff0c 区别于其他大部分网络 xff08 用HR图和经过HR图进行下采样的LR图作为网络数据进行训练 xff09 采用由CX loss 改进的CoBi loss作为网络
  • cmake安装以及更新

    直接install cmake wget https cmake org files v3 6 cmake 3 6 2 tar gz span class token function tar span zxvf cmake 3 6 2 t
  • manjaro安装以及配置、安装输入法、向日葵、anaconda、pycharm、QQ

    为什么不用Ubuntu xff0c 用manjaro就不说了 xff0c 直接上步骤吧 xff01 1 做个优盘 找个优盘 xff0c 最好是3 0接口吧 xff0c 我用的2 0有点慢 先下载系统 xff0c 找个自己喜欢的风格 Manj
  • Python编程中的常见语句

    4 1 if条件判断语句 4 1 1 if条件判断语句单分支 单分支格式 xff1a if 判断条件 xff1a 语句块1 else xff1a 语句块2 例 xff1a name 61 input 39 请输入您的用户名 39 if na
  • Linux下创建新用户及用户权限

    一 用户创建 增加用户 1 在root权限下 xff1b 命令 xff1a useradd 43 用户名 xff0c 它不会在 home目录下创建同名文件夹 xff0c 也没有创建密码 xff0c 因此利用这个用户登录系统 xff0c 是登
  • cacert.pem是怎么来的

    小弟最近在搞支付宝支付接口 xff0c 碰到个问题 xff0c help 我看demo中有下面一行代码 xff1a PHP code 1 2 3 ca证书路径地址 xff0c 用于curl中ssl校验 请保证cacert pem文件在当前文
  • mapping文件的编写

    mapping文件的编写 xff08 以及实体类与xml中类型的对应关系 xff09 2017年02月18日 11 31 36 转角人生 阅读数 xff1a 2918 标签 xff1a mapping文件 更多 个人分类 xff1a map
  • java-兔子繁殖问题

    題目 xff1a 古典问题 xff1a 有一对兔子 xff0c 从出生后第3个月起每个月都生一对兔子 xff0c 小兔子长到第三个月后每个月又生一 对兔子 xff0c 假如兔子都不死 xff0c 问每个月的兔子总数为多少 xff1f 64
  • Nginx服务器的安装部署和框架简介

    Nginx服务器的安装部署 1 如何获取Nginx服务器安装文件 Nginx服务器的软件版本包括 Windows版 和 Linux版俩种 官网下载地址为http nginx org en download html 网页上提供了Nginx服
  • error - problem conecting

    解决的办法是在树莓派里安装如下模块 xff1a sudo apt get install tightvncserver 接着reboot重启 xff0c 重新连接即可
  • SQL 日期函数应用实例!!

    sql view plain copy select convert varchar 10 getdate 120 只返回当前日期 xff0c 且为2012 12 12格式 xff08 最有用 xff09 sql view plain co
  • 使用docker-compose搭建Harbor私有仓库

    一 安装docker compose 1 下载docker compose的最新版本 curl L 34 https github com docker compose releases download 1 22 0 docker com
  • Zabbix安装部署

    一 服务端安装配置 1 环境检查 root 64 m01 cat etc redhat release CentOS Linux release 7 4 1708 Core root 64 m01 uname r 3 10 0 693 el
  • Docker安装部署

    一 安装 devicemapper 存储驱动 使用说明 xff1a 为了在生产级别的环境中使用 docker 运行环境 xff0c 必须使用 direct lvm 模式来运行devicemapper 存储驱动 这种模式使用块设备来创建 th

随机推荐

  • MySql安装部署

    下载并安装MySQL官方的 Yum Repository wget i c http dev mysql com get mysql57 community release el7 10 noarch rpm 使用上面的命令就直接下载了安装
  • Nginx安装部署

    Nginx 安装配置 Nginx 34 engine x 34 是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器 xff0c 也是一个 IMAP POP3 SMTP 代理服务器 在高连接并发的情况
  • Rancher安装部署

    直接通过docker镜像来运行我们的rancher xff0c 首先 xff0c 先从镜像中心下载rancher镜像 xff0c 如果是1 x系列的 xff0c 镜像名为rancher server xff0c 而2 x是rancher r
  • form表单提交onclick和onsubmit进行表单验证

    onsubmit只能表单上使用 提交表单前会触发 onclick是按钮等控件使用 用来触发点击事件 在提交表单前 xff0c 一般都会进行数据验证 xff0c 可以选择在submit按钮上的onclick中验证 也可以在onsubmit中验
  • iperf3网络测试工具

    一 iperf能用来做什么 测量网络带宽和网络质量提供网络延迟抖动 数据包丢失率 最大传输单元等统计信息 二 iperf3主要功能介绍 TCP 测试网络带宽支持多线程 xff0c 在客户端与服务端支持多重连接报告MSS MTU值的大小支持T
  • C++多线程5-单例模式详解

    单例模式 xff1a 只允许创建一个类对象 xff0c 实现的关键是将构造函数变为私有 单例模式有几种实现方式 xff1a 懒汉模式饿汉模式线程安全模式 锁实现和call once实现 局部静态变量模式 1 懒汉模式 当需要使用类对象时 x
  • c++多线程1-多线程的创建

    什么是多线程 xff1f 我们可以理解为一个线程执行一个代码段 xff0c 所以多个线程就是执行多个代码段 xff0c 如果当一个线程结束后 xff0c 进程就退出了 xff0c 这个线程我们称之为主线程 每个进程可以有一个或一个以上的线程
  • c++多线程2-线程参数传递需要注意的几个问题

    一 线程的初始化参数需要注意以下几个问题 xff1a 1 回调函数使用引用参数接收值时 xff0c 必须声明为const xff0c 否则报错 xff1b xff08 线程基于数据安全保护的考虑 xff09 2 回调函数必须声明为指针 xf
  • c++11-智能指针

    c 43 43 智能指针 为了更安全地管理动态内存 xff0c c 43 43 11引入了智能指针 xff0c 提供了包括shared ptr unique ptr weak ptr三种不同类型的智能指针 目录结构 xff1a 一 三种指针
  • C++多线程3-共享数据操作保护

    目录 xff1a 1 多线程操作共享数据引出的问题 2 解决多线程操作共享数据的方法 xff1a 锁 3 互斥量mutex的概念和用法 4 lock普通锁的用法 5 lock guard类模板的用法 6 死锁的概念和解决 7 unique
  • C++多线程4-unique_lock详解

    unique lock和lock guard都是可以自动解锁的类 xff0c 但是lock guard更加高效体现在永远在析构函数中解锁 xff0c 而unique lock更加灵活 xff0c 但执行效率会比lock guard低一些 x
  • C++多线程6-条件变量

    1 条件变量 std condition variable是多线程中经常用到的一个类 xff0c 它的头文件为condition variable 它常用的成员函数包括 xff0c wait notify one notify all等 它
  • Centos6.5系统升级软件操作文档

    为什么要用Centos6 5系统 xff1f 答 xff1a 因为计算板官方推荐Centos6 5 为什么要升级软件 xff1f 答 xff1a 软件需支持C 43 43 11相关库 1 系统信息 系统版本 xff1a CentOS 6 5
  • c++11多线程7-异步线程

    异步线程 异步线程的引入解决了线程有依赖关系的情景 c 43 43 11提供了std async xff0c std packaged task xff0c std promise xff0c 三种方法 1 std async std as
  • HTML中meta标签如何正确使用

    HTML中 lt meta gt 标签如何正确使用 如果我们在浏览器中按下F12或者Ctrl 43 shift 43 J xff0c 便可以打开开发者工具 xff0c 在element中即可看到 lt head gt 元素中有不少 lt m
  • 4.Linux网络编程-select和poll模型

    目录 xff1a 1 补充知识 2 简易版回射服务器的实现 3 select模型实现 4 poll模型实现 1 补充知识 span class token comment 显示进程的pid xff1a span span class tok
  • 5.Linux网络编程-select实现超时API

    一 alarm函数设置超时 它的主要功能是设置信号传送闹钟 信号SIGALRM在经过seconds指定的秒数后传送给目前的进程 xff0c 如果在定时未完成的时间内再次调用了alarm函数 xff0c 则后一次定时器设置将覆盖前面的设置 x
  • 6.Linux网络编程-epoll原理

    一 xff1a 对比select发现epoll的有点 要比较epoll相比较select高效在什么地方 xff0c 就需要比较二者做相同事情的方法 要完成对I O流的复用需要完成如下几个事情 xff1a 1 用户态怎么将文件句柄传递到内核态
  • 7.Linux网络编程-UNIX域套接字

    一 xff1a UNIX套接字 用于同一台pc上运行的进程之间通信 xff0c 它仅仅复制数据 xff0c 不执行协议处理 xff0c 不需要增加删除网络报头 xff0c 无需计算校验和 xff0c 不产生顺序号 xff0c 无需发送确认报
  • 8.Linux网络编程-System V消息队列

    一 xff1a 消息队列 消息队列是进程间通信的一种手段 xff0c 进程产生的数据块以链表的形式存储在消息队列中 xff0c 每个数据块都被认为是有一个类型 xff0c 接收者进程接收的数据块可以有不同的类型 消息队列有以下的几个限制 x