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

命名管道(用命名管道模拟server和client之间的通信)

目录

  • 命名管道
  • 创建命名管道
    • 使用命令行创建命名管道(FIFO)
    • 在程序中创建
  • 命名管道的打开规则
  • 用命名管道实现server和client通信

命名管道

bash进程并不会给我们写的两个不同的程序创建通信的管道,即使这两个进程看起来好像都是bash的子进程,但是此时再用血缘关系那一套来看就不适用了。通信的本质还是要有一个共同的访问的内存,此时bash并不会帮我们创建。所以此时需要通信就需要自己开辟一个内存空间去进行两个不相关进程的通信。
具体方式是创建一份有名管道文件,文件可以让两个进程进行通信,这个文件就叫命名管道
命名管道是一个特殊的文件。可以使不相关的进程之间进行通信,创建管道时创建一个名字,以后其它进程就可以通过这个名字来使用这个管道的另一端。这也是为什么我们称这样的管道为命名管道。命名管道是以一个普通的文件形式出现的,包括创建管道写管道读管道。值得注意的是,打开普通文件建立内核级缓冲区后,操作系统会及时刷新里面的数据到磁盘文件中,而管道文件(FIFO)的缓冲区则不会。

需要知道的是,命名管道也符合管道的四种情况和五大特性。

创建命名管道

使用命令行创建命名管道(FIFO)

使用mkfifo指令创建命名管道,其中filename表示文件名。
在这里插入图片描述
命名管道文件又叫FIFO(first in first out)文件,其文件类型为p,表示是一个管道文件。

删除管道使用unlink命令。

在程序中创建

命名管道也可以在程序中使用mkfifo函数创建。此时mkfifo是函数,而上面那个是指令。
具体使用方式如下:

#include<sys/types.h>
#include<sys/stat.h>
int mkfifo(const char* pathname,mode_t mode);

在这里插入图片描述

其中,参数pathname表示的是创建管道文件的路径可以是绝对路径也可以是相对路径。mode表示管道文件的权限,但文件最终的权限还要考虑umask(权限掩码)。如果创建成功返回0,否则-1.
当某个进程要使用管道文件时,像普通文件那样,得先打开文件(open),且要确定以什么方式打开(write or read)。

命名管道的打开规则

如果当前是以读的方式打开FIFO文件,而此时没有进程以写的方式打开该FIFO文件时,读端进程就会阻塞,直到有进程以写的方式打开该FIFO。

如果当前是以写的方式打开FIFO文件,而此时没有进程以读的方式打开该FIFO文件时,写端进程就会堵塞,直到有进程以读的方式打开该FIFO。

用命名管道实现server和client通信

下面通过命名管道来实现server(服务端)和client(用户端)的通信。具体步骤如下:

  1. 将命名管道的操作方法及其属性封装成一个类,方便代码复用
  2. server端创建管道之后接收数据
  3. client端使用管道发送数据
  4. server端回收管道,使用unlink函数。

NamePipe.hpp

#pragma once

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

const std::string comm_path = "./myfifo";
#define DefaultFd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

class NamePiped
{
private:
    bool OpenNamedPipe(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(_fifo_path.c_str(), 0666);
            if (res != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creater create named pipe" << std::endl;
        }
    }
    bool OpenForRead()
    {
        return OpenNamedPipe(Read);
    }
    bool OpenForWrite()
    {
        return OpenNamedPipe(Write);
    }
    // const &: const std::string &XXX
    // *      : std::string *
    // &      : std::string & 
    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;
};

client.cc

#include "namedPipe.hpp"

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

    return 0;
}

server.cc

#include "namedPipe.hpp"

// server 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;
}

相关文章:

  • 关于elementui的时间组件与后端时间和oracle数据库时间的对应格式
  • 计算机毕业设计Python小说推荐系统 K-means聚类推荐算法 深度学习 Kears 小说数据分析 可视化 Scrapy爬虫 协同过滤
  • 物业管理系统源码 物业小程序源码
  • 如何将JAR交由Systemctl管理?
  • 扩展用户空间
  • 甘特图开发代码(测试版)
  • 伍[5],伺服电机,电流环,速度环,位置环
  • SQL经典常用查询语句
  • c++ cin输入流的使用总结
  • Docker概念与架构
  • LangGraph实战:构建智能文本分析流水线
  • AI-Ollama本地大语言模型运行框架与Ollama javascript接入
  • 点云 PCL分割聚类适用场景
  • Spring学习笔记04:spring mvc和Spring Boot之间是什么关系?
  • 基于python实现基础的文本编辑器
  • 5分钟快速搭建一个 SpringBoot3 + MyBatis-Plus 工程项目
  • 【Python项目】基于Python的答题卡识别与判分系统
  • 告别GitHub连不上!一分钟快速访问方案
  • 关于养成数值的感悟
  • Ubuntu20.04双系统安装及软件安装(五):VSCode
  • 受欢迎的邢台做网站/腾讯域名注册官网
  • js做网站好吗/哪里可以做
  • win2003做网站/线上网络推广怎么做
  • 在家帮诈骗团伙做网站/网络seo推广
  • wordpress发送邮件插件/seo网络营销招聘
  • 黑龙江网上建设局报建网站/青岛网站制作