命名管道实现本地通信

news2025/7/22 12:32:01

目录

命名管道实现通信

命名管道通信头文件

创建命名管道mkfifo

构造函数

以读方式打开命名管道

以写方式打开命名管道

读操作

写操作

析构函数

服务端

客户端

运行结果


命名管道实现通信

命名管道通信头文件

#pragma

#include <iostream>
#include <cstdio>
#include <cerrno>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

const std::string comm_path = "./myfifo";

class NamePiped
{
private:
    bool OpenNamePipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
            return false;
        return true;
    }

public:
    NamePiped(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creater)
        {
            int res = mkfifo(path.c_str(), 0666);
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater creat named pipe" << std::endl;
        }
    }

    bool OpenForRead()
    {
        return OpenNamePipe(Read);
    }

    bool OpenForWrite()
    {
        return OpenNamePipe(Write);
    }

    int ReadNamedPipe(std::string* out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer,sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }

    int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(),in.size());
    }

    ~NamePiped()
    {
        if (_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("unlink");
            }
            std::cout << "creater free named pipe" << std::endl;
        }
        if (_fd != DefaultFd)
            close(_fd);
    }

private:
    const std::string _fifo_path;
    int _id;
    int _fd;
};

我们要想实现用命名管道并将它封装成类来进行通信我们就要想好我们需要哪些基本成员,首先既然要创建命名管道我们是不是得先了解命名管道创建的调用接口?

创建命名管道mkfifo

查询我们的3号手册我们会发现它是我们C语言库帮我们封装好的一个接口,它的两个参数一个是你的路径名,另一个是你这个命名管道文件的权限。

创建成功,返回值返回0,否则返回-1错误码被设置。

由这个函数我们就知道我们得有命名管道的文件路径名,还有它的权限设置,权限设置我们可以直接传,但我们的路径就可以作为我们的成员变量。同样的创建命名管道我们希望只有创建者身份的进程能够创建它,所以我们需要需要一个id号来当身份证。到这里我们的3个成员变量的前两个就已经说明白了。

unlink就是C语言为我们提供的Linux上的系统调用,它的参数只有一个我们的文件路径名。这个函数的功能是将我们提供的文件名进行删除,文件内容是否进行删除是有条件的,即使文件名不再存在,只要还有进程通过打开的文件描述符访问该文件,文件就会继续保留在磁盘上。这是因为操作系统需要确保正在使用文件的进程能够正常访问其内容。只有当所有指向该文件的文件描述符都被关闭,且没有其他硬链接指向该文件时,文件的内容才会被真正删除,其占用的磁盘空间才会变得可用。

它的返回值也是一样的,成功就返回0,失败返回-1,错误码被设置。

命名管道的创建和删除我们了解了之后,我们就要来想它接下来的逻辑了。我们有两个进程,一个进程的身份是服务端(也就是我们的创建者),这个进程只会进行读操作。另一个进程的身份是客户端(我们的使用者),它只会进行我们的写操作。而我在之前的文章里有说过,命名管道也好匿名管道也好,都是文件的一种特殊形式,我们对文件怎么读,我们就对命名管道怎么读。所以既然一个只写,一个只读,那么我们就还需要一个文件描述符fd来进行我们的读写操作。至此3个成员变量的作用我们就都说明清楚了。

前面知识铺垫完毕,我再来跟大家将这个类的实现讲清楚。

构造函数

 NamePiped(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creater)
        {
            int res = mkfifo(path.c_str(), 0666);
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater creat named pipe" << std::endl;
        }
    }

我们的构造函数首先将我们的3个成员变量赋值,分别是文件名,身份,自身持有的文件描述符(我们暂设-1,因为还没打开文件),函数内部我们只对创建者进行创建命名管道的动作。

以读方式打开命名管道

创建好之后我们就要打开它了,打开它有两种方式,我们先来读打开

bool OpenNamePipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
            return false;
        return true;
    }


bool OpenForRead()
    {
        return OpenNamePipe(Read);
    }

从这里我们就能看到读打开跟打开普通文件没有任何区别。打开成功我们就可以讲文件描述符设置一下了。

以写方式打开命名管道

读是如此,写也一样。

bool OpenForWrite()
    {
        return OpenNamePipe(Write);
    }

我们可以看到两个复用的都是同一段的代码。

读操作

int ReadNamedPipe(std::string* out)
    {
        char buffer[BaseSize];
        int n = read(_fd, buffer,sizeof(buffer));
        if(n > 0)
        {
            buffer[n] = 0;
            *out = buffer;
        }
        return n;
    }

读取也是一样,文件我们怎么读,这里我们就怎么读,没有啥干货这里就不浪费时间了。

写操作

int WriteNamedPipe(const std::string &in)
    {
        return write(_fd, in.c_str(),in.size());
    }

写操作更是如此。我们不难发现,在解决了命名管道这个问题之后,其它的操作就不难了。

析构函数

~NamePiped()
    {
        if (_id == Creater)
        {
            int res = unlink(_fifo_path.c_str());
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater free named pipe" << std::endl;
        }
        if (_fd != DefaultFd)
            close(_fd);
    }

这里跟我们的构造函数相呼应,由谁创建就理应由谁来删除,最后我们再把各自的文件描述符一关,这样命名管道的空间就释放掉了。

服务端

#include "namedPipe.hpp"
// read:管理命名管道的整个生命周期
int main()
{
    NamePiped fifo(comm_path, Creater);
    //对于读端而言,如果我们打开文件,但是写还没来,我会阻塞在open调用中,知道对方打开
    //进程同步
    if (fifo.OpenForRead())
    {
        std::cout << "server open named pipe done" << std::endl;
        sleep(3);
        while (true)
        {
            std::string message;
            int n = fifo.ReadNamedPipe(&message);
            if (n > 0)
            {
                std::cout << "Client Say> " << message << std::endl;
            }
            else if (n == 0)
            {
                std::cout << "Client quit,Server Too!" << std::endl;
                break;
            }
            else
            {
                std::cout << "fifo.ReadNamedPipe Error" << std::endl;
                break;
            }
        }
    }

    return 0;
}

服务端作为我们命名管道的创建者,它创建管道并以读方式打开,任然后一直读取数据,如果客户端关闭,那么意味着写端也关闭了,我们同样会退出循环。

客户端

#include "namedPipe.hpp"
// write
int main()
{
    NamePiped fifo(comm_path, User);
    if (fifo.OpenForWrite())
    {
        std::cout << "client open named pipe done" << std::endl;
        while (true)
        {
            std::cout << "Please Enter> ";
            std::string message;
            std::getline(std::cin, message);
            fifo.WriteNamedPipe(message);
        }
    }

    return 0;
}

客户端作为我们的使用者,它会一直写一直写,知道我们自己手动退出。

运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2401347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iOS上传应用包错误问题 “Invalid bundle. The “UIInterfaceOrientationPortrait”“

引言 在开发 iOS 应用的整个生命周期中&#xff0c;打包上传到 App Store 是一个至关重要的步骤。每一次提交&#xff0c;Xcode 都会在后台执行一系列严格的校验流程&#xff0c;包括对 Info.plist 配置的检查、架构兼容性的验证、资源完整性的审查等。如果某些关键项配置不当…

猎板厚铜PCB工艺能力如何?

在电子产业向高功率、高集成化狂奔的今天&#xff0c;电路板早已不是沉默的配角。当5G基站、新能源汽车、工业电源等领域对电流承载、散热效率提出严苛要求时&#xff0c;一块能够“扛得住大电流、耐得住高温”的厚铜PCB&#xff0c;正成为决定产品性能的关键拼图。而在这条赛道…

Flutter快速上手,入门教程

目录 一、参考文档 二、准备工作 下载Flutter SDK&#xff1a; 配置环境 解决环境报错 zsh:command not found:flutter 执行【flutter doctor】测试效果 安装Xcode IOS环境 需要安装brew&#xff0c;通过brew安装CocoaPods. 复制命令行&#xff0c;打开终端 分别执行…

算法:前缀和

1.【模版】前缀和 【模板】前缀和_牛客题霸_牛客网 这道题如果使用暴力解法时间复杂度为O(n*m)&#xff0c;会超时&#xff0c;所以要使用前缀和算法。 前缀和->快速求出数组中某一个连续区间的和。 第一步&#xff1a;预处理出一个前缀和数组 dp。 dp[i]表示[1, i] 区间…

DEVICENET转MODBUS TCP网关与AB数据输出模块的高效融合方案研究

在工业自动化领域&#xff0c;多样化的设备通常采用不同的通信协议&#xff0c;这为系统集成带来了显著的挑战。特别是在需要将遵循DeviceNet协议的设备与基于MODBUS TCP协议的系统进行互连时&#xff0c;这一挑战尤为突出。AB数据输出作为一种功能卓越的DeviceNet分布式输入/输…

牛客小白月赛113

前言&#xff1a;这场的E题补的我头皮都发麻了。 A. 2025 题目大意&#xff1a;一个仅有‘-’‘*’组成的字符串&#xff0c;初始有一个sum 1&#xff0c; 从左到右依次遍历字符串&#xff0c;遇到-就让sum--&#xff1b;遇到*就让sum* 2&#xff0c;问sum有没有可能大于等于…

Mac版本Android Studio配置LeetCode插件

第一步&#xff1a;Android Studio里面找到Settings&#xff0c;找到Plugins&#xff0c;在Marketplace里面搜索LeetCode Editor。 第二步&#xff1a;安装对应插件&#xff0c;并在Tools->LeetCode Plugin页面输入帐号和密码。 理论上&#xff0c;应该就可以使用了。但是&a…

电子电路基础1(杂乱)

电路基础知识 注意&#xff1a;电压源与电流源的表现形式 注意&#xff1a;在同一根导线上电势相等 电阻电路的等效变换 电子元器件基础 电阻

【openssl】升级为3.3.1,避免安全漏洞

本文档旨在形成 对Linux系统openssl版本进行升级 的搭建标准操作过程&#xff0c;搭建完成后&#xff0c;实现 openssl 达到3.3以上版本&#xff0c;避免安全漏洞 效果。 一、查看当前版本 版本不高于3.1的&#xff0c;均需要升级。 # 服务器上运行以下命令&#xff0c;查看…

使用 HTML +JavaScript 从零构建视频帧提取器

在视频编辑、内容分析和多媒体处理领域&#xff0c;常常需要从视频中提取关键帧。手动截取不仅效率低下&#xff0c;还容易遗漏重要画面。本文介绍的视频帧提取工具通过 HTML5 技术栈实现了一个完整的浏览器端解决方案&#xff0c;用户可以轻松选择视频文件并进行手动或自动帧捕…

论文速读《DexWild:野外机器人策略的灵巧人机交互》

项目链接&#xff1a;https://dexwild.github.io/ 论文链接&#xff1a;https://arxiv.org/pdf/2505.07813 0. 简介 2025年5月&#xff0c;卡内基梅隆大学&#xff08;CMU&#xff09;发布了一篇突破性论文《DexWild: Dexterous Human Interactions for In-the-Wild Robot Pol…

Bug问题

一、list 页面 import React, { useEffect, useState } from react; import { shallowEqual, useHistory, useSelector } from dva; import { Button, message } from choerodon-ui/pro; import formatterCollections from hzero-front/lib/utils/intl/formatterCollections; …

【数据结构】5. 双向链表

文章目录 一、链表的分类1、双向链表的结构 二、双向链表的实现0、准备工作1、初始化2、打印3、尾插4、头插5、尾删6、头删7、查找8、在指定位置之后插入数据9、删除指定位置10、销毁 一、链表的分类 链表总共分为8种&#xff0c;具体的分组方式如图所示&#xff1a; 带头指的…

【Linux手册】冯诺依曼体系结构

目录 前言 五大组件 数据信号 存储器&#xff08;内存&#xff09;有必要吗 常见面试题 前言 冯诺依曼体系结构是当代计算机基本架构&#xff0c;冯诺依曼体系有五大组件&#xff0c;通过这五大组件直观的描述了计算机的工作原理&#xff1b;学习冯诺依曼体系可以让给我们更…

Mobile App UI自动化locator

在开展mobile app UI层自动化测试时&#xff0c;编写目标元素的locator是比较耗时的一个环节&#xff0c;弄清楚locator背后的逻辑&#xff0c;可以有效降低UI层测试维护成本。此篇博客以webdriverioappium作为UI自动化工具为例子&#xff0c;看看有哪些selector方法&#xff0…

(LeetCode 每日一题) 1061. 按字典序排列最小的等效字符串 (并查集)

题目&#xff1a;1061. 按字典序排列最小的等效字符串 思路&#xff1a;使用并查集&#xff0c;来将等价的字符连起来&#xff0c;形成一棵树。这棵树最小的字母&#xff0c;就代表整颗树&#xff0c;时间复杂度0(n)&#xff0c;细节看注释。 C版本&#xff1a; class Solutio…

linux 安装mysql8.0;支持国产麒麟,统信uos系统

一&#xff1a;使用我已经改好的mysql linux mysql8.0解压可用&#xff0c;点我下载 也在国产麒麟系统&#xff0c;统信uos系统也测试过&#xff0c;可用&#xff1b; 下载后&#xff0c;上传mysql.tar.gz 然后使用root角色去执行几个命令即可&#xff1b;数据库密码&#xf…

C#实现远程锁屏

前言 这是一次提前下班没有锁屏进而引发的一次思考后的产物&#xff0c;思考的主要场景是当人离开电脑后&#xff0c;怎么能控制电脑锁屏&#xff0c;避免屏幕上的聊天记录被曝光。 首先想到通过系统的电源计划设置闲置超时时间熄屏&#xff0c;这可能是最接近场景的解决方案&a…

SpringBoot3整合MySQL8的注意事项

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 注意事项 1、请添加添加如下依赖&#xff1a; <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><…

智语心桥:当AI遇上“星星的孩子”,科技如何点亮沟通之路?

目录: 引言:当科技的温度,遇见“星星的孩子”“智语心桥”:一座为孤独症儿童搭建的AI沟通之桥核心技术探秘:AI如何赋能“读心”与“对话”?个性化魔法:AI如何实现“千人千面”的精准干预?应用场景畅想:从家庭到机构,AI的全方位支持为什么是“智语心桥”?——价值、可…