当前位置: 首页 > news >正文

命名管道实现本地通信

目录

命名管道实现通信

命名管道通信头文件

创建命名管道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 4096const 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.lqws.cn/news/156457.html

相关文章:

  • 知识图谱:为什么说它是AI突破认知瓶颈的最后一块拼图?
  • 云原生 DevOps 实践路线:构建敏捷、高效、可观测的交付体系
  • 口语考试准备part1(西电)
  • 接IT方案编写(PPT/WORD)、业务架构设计、投标任务
  • 视觉前沿算法复现环境配置1——2025CVPR风格迁移网络SaMam
  • 【MATLAB去噪算法】基于CEEMDAN联合小波阈值去噪算法(第四期)
  • Socket编程UDP\TCP
  • 从理论崩塌到新路径:捷克科学院APL Photonics论文重构涡旋光技术边界
  • vue-router路由问题:可以通过$router.push()跳转,但刷新后又变成空白页面
  • 【Java Web】9.Maven高级
  • 【opencv】基础知识到进阶(更新中)
  • 老项目的xtp1.19升级否
  • 开疆智能Etherenet转Modbus网关连接欧姆龙PLC配置案例
  • 为什么需要自动下载浏览器驱动?
  • 如何实现ModbusRTU转ProfibusDP网关与三菱PLC的完美通讯!
  • 广东餐饮服务初级证值得考吗?
  • 【Python训练营打卡】day44 @浙大疏锦行
  • C#、VB.net——如何设置窗体应用程序的外边框不可拉伸
  • dvwa10——XSS(DOM)
  • 使用 Preetham 天空模型与硬边太阳圆盘实现真实感天空渲染
  • 【iOS】cache_t分析
  • 益莱储参加 Keysight World 2025,助力科技加速创新
  • C# ExcelWorksheet 贴图
  • 一些实用的chrome扩展0x01
  • C及C++编译链接过程详解
  • Devops系列---python基础篇二
  • LSTM-XGBoost多变量时序预测(Matlab完整源码和数据)
  • 【Go】3、Go语言进阶与依赖管理
  • 【VLAs篇】02:Impromptu VLA—用于驱动视觉-语言-动作模型的开放权重和开放数据
  • 【图像处理3D】:世界坐标系