进程间通信之命名管道(完成一个类似QQ的聊天应用)

The world is more malleable than you think, and it is waiting for you ti hammer it into shape.--------这个世界的可塑性比你想的还要大,而它正等着你去敲打成型

今天我们将要使用命名管道实现小黑与小白的聊天功能哦。

1,管道以及无名管道回顾

在这之前,小黑写了一篇管道还有无名管道的博客:
https://blog.csdn.net/weixin_46027505/article/details/105151719

2,为什么叫做命名管道?

  • 它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),

  • 它在磁盘上有对应的节点,但 没有数据块——换言之,只是拥有一个名字和相应的访问权限,通过mknode()系统调用或者mkfifo()函数来建立的。一旦建 立,任何进程都可以通过文件名将其打开和进行读写,而不局限于父子进程,当然前提是进程对FIFO有适当的访问权。当不再被进程使用时,FIFO在内存中释放,但磁盘节点仍然存在。

  • 因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如lseek()等文件定位操作。

3,FIFO文件和普通文件区别

有名管道又称为FIFO 文件,我们对有名管道的操作可以采用操作文件的方法,如使用 open, read,write 等,有名管道适用于任何两个进程间通信。

  • FIFO 文件普通文件的区别
  1. 读取FIFO文件的进程只能以“O_RDONLY”方式打开FIFO 文件。

  2. 写FIFO文件的进程只能以“O_WRONLY”的方式打开FIFO文件。

  3. FIFO文件里面的内容被读取后,就消失了,但是普通文件的内容读取后还存在。

4,有名管道相关操作API函数

mkfifo()创建管道(fifo 文件)

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char * pathname,mode_t mode)
  • 函数功能:创建一个FIFO文件(有名管道)
  • 返回值:成功 :0
    失败:-1
  • 参数说明
    pathname:创建的fifo文件名字(带路径)
    mode_t :文件的操作权限

删除管道unlink()

#include<unistd.h>
int unlink(const char * pathname);
  • 函数功能:删除文件(包括fifo文件)
  • 返回值
    成功 :0
    失败 :-1
  • 参数
    pathname:要删除的文件名字(带路径)

其他API函数

下面的就是文件i/o的函数,一样的用法,就不阐明。

  • 打开管道 open()
  • 关闭管道 close()
  • 读/写管道 read/write

5,简单代码,用于理解(使用命名管道实现单向通信)

下面实现两个任意进程间通信,创建两个进程,一个是读进程,另一个写进程;
写进程:主要是创建fifio 文件;并将数据写入文件, 退出;
读进程: 主要读取fifo 文件的数据,并打印出来,退出

在这里插入图片描述

  • 写进程代码:a.c
#include <sys/types.h>
#include<sys/fcntl.h>
#include<sys/stat.h>
#include <unistd.h>

#define FIFO_PATH "/home/xuxiaohei/FIFO/.file"

int main()
{
    int fd;
    char buf[1024]="HELLO, B proccss. I am A proccess";
    
    mkfifo(FIFO_PATH , 0777);
    
    fd=open(FIFO_PATH , O_WRONLY);//写进程打开方式只能以O_WRONLY方式
    
    write(fd, buf, sizeof(buf));
    close(fd);
}
  • 读进程范例代码:b.c
#include<sys/types.h>
#include<sys/fcntl.h>
#include<unistd.h>
#include<stdio.h>

#define FIFO_PATH "/home/xuxiaohei/FIFO/.file"

int main()
{
    int fd;
    char buf[1024]={0};
    
    fd =open(FIFO_PATH ,O_RDONLY);//读进程只能以O_RDONLY方式打开fifo文件
    
    read(fd,buf,1024);
    printf("read data from A proccess:%s\n",buf);
    close(fd);
    unlink(FIFO_PATH );
}

运行程序:
首先运行 a 程序,会处于阻塞状态,直到运行b程序读取fife文件数据;

这个容易理解:水管的另一端都没打开,你又怎么可能往里面注水呢? 所以你写端只能阻塞等待读端打开,才能连接

在这里插入图片描述

  • 然后我们打开另外一个终端,ls 后发现多了一个文件,这个文件就是管道文件。

在这里插入图片描述

6,复杂代码,实现双向通信(实现类似QQ聊天的小应用)

6.1 简单介绍

其实这个小应用就是上面代码的升级版本,只不过上面的代码只能单向通信,其实如何实现双向通信,之前在无名管道的博客中有提到过,就是建立两根管道。同时加入select(),因为我们需要判断是标准输入(就是我们输入到屏幕,然后发送给对方的信息),还是对方发来的信息。

  • 如果是对方发来的信息,我们就打印到屏幕,我们就知道了对方发来什么信息。
  • 如果是标准输入发来的我们就写入管道发送给对方。

在这里插入图片描述

下面是小黑写的关于select() 的博客,忘记的人可以看看:

https://blog.csdn.net/weixin_46027505/article/details/104836269

6.2 写这个小应用之前有2点需要注意:

6.2.1 关闭管道会产生信号

关闭管道的一端     
(1)当读一个写端被关闭的管道时,在所有数据都被读取后,read返回0,表示文件结束;     
(2)当写一个读端被关闭的管道时,则产生信号SIGPIPE,如果忽略该信号或者捕捉该信号并从其处理程序返回,则 wirte返回-1.

我们可以利用信号这点,实现如果对方断开,也退出自己进程。

如果忘记信号可以阅读:
https://blog.csdn.net/weixin_46027505/article/details/104530022

6.2.2 死锁问题

在这里插入图片描述

所以,如果小白在代码里先打开只写端,那么小黑必须先打开只读端

6.3 具体代码实现

小黑和小白的原理是一样的。

  • 下面是小白进程的代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <libgen.h>
#include <stdlib.h>

#define FIFO_FILE1      ".fifo_chat1"
#define FIFO_FILE2      ".fifo_chat2"



int g_stop = 0;



void sig_pipe(int signum)
{
    if (SIGPIPE == signum)
    {
        printf("get pipe broken signal and let programe exit\n");
        g_stop = 1;
    }
}



int main(int argc, char **argv)
{
    int     fdr_fifo;
    int     fdw_fifo;
    int     rv;
    fd_set  rdset;
    char    buf[1024];
    



    
    if (access(FIFO_FILE1, F_OK))
    {
            printf("FIFO file \"%s\" not exist and create it now\n", FIFO_FILE1);
            mkfifo(FIFO_FILE1, 0666);
    }
     
    if (access(FIFO_FILE2, F_OK))
    {
            printf("FIFO file \"%s\" not exist and create it now\n", FIFO_FILE2);
            mkfifo(FIFO_FILE2, 0666);
    }
    


    signal(SIGPIPE, sig_pipe);
                            
    


    printf("start open '%s' for read and it will blocked untill write endpoint opened...\n",  FIFO_FILE1);
          

    
    if ((fdr_fifo = open(FIFO_FILE1, O_RDONLY)) < 0 )
    
    {
    
        printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE1,
        strerror(errno));
                
        return -1;
    }
            

        
    printf("start open '%s' for write...\n", FIFO_FILE2);
    
    if ((fdw_fifo = open(FIFO_FILE2, O_WRONLY)) < 0)
    
    {
    
        printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE2,
        strerror(errno));            
        return -1;
        
    }
    
    


    printf("\nxiaobai have done all preparetion job and be ready to chat with xiaohei\n");    
    printf("start chating with xiaohei now, please input message now: \n");
    while (!g_stop)
    {
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(fdr_fifo, &rdset);
        
        rv =  select(fdr_fifo + 1, &rdset, NULL, NULL, NULL);
        if (rv <= 0)
        {
            printf("Select get timeout or error: %s\n", strerror(errno));
            continue;
        }
        
        if (FD_ISSET(fdr_fifo, &rdset))
        {
            memset(buf, 0, sizeof(buf));
            rv = read(fdr_fifo, buf, sizeof(buf));
            if (rv < 0)
            {
                printf("read data from FIFO get errorr: %s\n", strerror(errno));
                break;
            }
            else if (0 == rv)   
            {
                printf("Another side of FIFO get closed and program will exit now\n");
                break;
            }
            printf("xiaohei: %s", buf);
        }
        
        if (FD_ISSET(STDIN_FILENO, &rdset))
        {
            memset(buf, 0, sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            write(fdw_fifo, buf, strlen(buf));
        }
    }
}

  • 下面是小黑的代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <libgen.h>
#include <stdlib.h>

#define FIFO_FILE1      ".fifo_chat1"
#define FIFO_FILE2      ".fifo_chat2"



int g_stop = 0;



void sig_pipe(int signum)
{
    if (SIGPIPE == signum)
    {
        printf("get pipe broken signal and let programe exit\n");
        g_stop = 1;
    }
}



int main(int argc, char **argv)
{
    int     fdr_fifo;
    int     fdw_fifo;
    int     rv;
    fd_set  rdset;
    char    buf[1024];
    




    
    if (access(FIFO_FILE1, F_OK))
    {
            printf("FIFO file \"%s\" not exist and create it now\n", FIFO_FILE1);
            mkfifo(FIFO_FILE1, 0666);
    }
     
    if (access(FIFO_FILE2, F_OK))
    {
            printf("FIFO file \"%s\" not exist and create it now\n", FIFO_FILE2);
            mkfifo(FIFO_FILE2, 0666);
    }
    


    signal(SIGPIPE, sig_pipe);
   

        
    {                         
        
        printf("start open '%s' for write and it will blocked untill read endpoint opened...\n",
               FIFO_FILE1);
        
        if ((fdw_fifo = open(FIFO_FILE1, O_WRONLY)) < 0)
        {
            printf("Open fifo[%s] for chat write endpoint failure: %s\n", FIFO_FILE1,
                   strerror(errno));
            return -1;
        }
        
        printf("start open '%s' for read...\n", FIFO_FILE2);
        if ((fdr_fifo = open(FIFO_FILE2, O_RDONLY)) < 0)
        {
            printf("Open fifo[%s] for chat read endpoint failure: %s\n", FIFO_FILE2,
                   strerror(errno));
            return -1;
        }
    }
    



    printf("\nxiaohei have done all preparation job and be ready to chat with xiaobai......\n");
    printf("start chating with xiaobai now, please input message now: \n");
    while (!g_stop)
    {
        FD_ZERO(&rdset);
        FD_SET(STDIN_FILENO, &rdset);
        FD_SET(fdr_fifo, &rdset);
        
        rv = select(fdr_fifo + 1, &rdset, NULL, NULL, NULL);
        if (rv <= 0)
        {
            printf("Select get timeout or error: %s\n", strerror(errno));
            continue;
        }
        
        if (FD_ISSET(fdr_fifo, &rdset))
        {
            memset(buf, 0, sizeof(buf));
            rv = read(fdr_fifo, buf, sizeof(buf));
            if (rv < 0)
            {
                printf("read data from FIFO get errorr: %s\n", strerror(errno));
                break;
            }
            else if (0 == rv)   
            {
                printf("Another side of FIFO get closed and program will exit now\n");
                break;
            }
            printf("xiaobai: %s", buf);
        }
        
        if (FD_ISSET(STDIN_FILENO, &rdset))
        {
            memset(buf, 0, sizeof(buf));
            fgets(buf, sizeof(buf), stdin);
            write(fdw_fifo, buf, strlen(buf));
        }
    }
}

6.4 代码运行结果演示

  • 下面我们先完成准备工作,编译

在这里插入图片描述

  • 然后先运行小白的QQ,发现阻塞,正在等待对方打开读端。
    在这里插入图片描述

  • 然后我们运行小黑的QQ,就可以通信了
    在这里插入图片描述

在这里插入图片描述

  • 因为我们在程序中调用unlink(),所以程序退出后两个管道文件依旧存在。
    在这里插入图片描述
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值