网络编程之FIFO

      管道没有名字,因此它们最大的劣势在于只能用于有一个祖先进程的各个进程之间。我们无法在无亲缘关系的两个进程间创建一个管道并将它用作IPC通道(不考虑文件描述符)。

      FIFO指先进先出,UNIX中的FIFO类似管道。它是一个单向的(半双工)的数据流。不同于管道的是,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO,FIFO也成为有名管道。

      FIFO由mkfifo函数创建。

           int  mkfifo(const char* pathname,mode_t mode)

      mkfifo函数已隐含指定O_CREAT | O_EXCL,也就是说,它要么创建一个新的FIFO,要么返回一个EEXIST错误。如果不希望创建一个新的FIFO,那么就改为调用open而不是mkfifo。要打开一个已存在的FIFO或创建一个新的FIFO,应先调用mkfifo,再检查它是否返回EEXIST错误,若返回该错误则改为调用open。

  函数说明:

      mkfifo ()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此 umask值也会影响到FIFO文件的权限。Mkfifo()建立的FIFO文件其他进程都可以用读写一般文件的方式存取。当使用open()来打开 FIFO文件时,O_NONBLOCK旗标会有影响

1、当使用O_NONBLOCK 旗标时,打开FIFO 文件来读取的操作会立刻返回,但是若还没有其他进程打开FIFO 文件来读取,则写入的操作会返回ENXIO 错误代码
2、没有使用O_NONBLOCK 旗标时,打开FIFO 来读取的操作会等到其他进程打开FIFO文件来写入才正常返回。同样地,打开FIFO文件来写入的操作会等到其他进程打开FIFO 文件来读取后才正常返回。

返回值

若成功则返回0,否则返回-1,错误原因存于errno中。
错误代码
EACCESS 参数pathname所指定的目录路径无可执行的权限
EEXIST 参数pathname所指定的文件已存在。
ENAMETOOLONG 参数pathname的路径名称太长。
ENOENT 参数pathname包含的目录不存在
ENOSPC 文件系统的剩余空间不足
ENOTDIR 参数pathname路径中的目录存在但却非真正的目录。
EROFS 参数pathname指定的文件存在于只读文件系统内

现在重新编写之前在管道那节博客中的客户端服务器程序,这次改用两个FIFO代替两个管道,客户端向服务器发出文件的路径,服务器返回文件的内容。

其布局如图:

首先给出main函数:

#include"uitil.h"

int main(int argc,char*argv[])
{
    int pipe1[2],pipe2[2];
    pid_t pid;

    int res1;
    int res2;
    //创建管道
    res1 = pipe(pipe1);
    if(res1 == -1){
        cout << "pipe error." << endl;
        exit(1);
    } 
    
    res2 = pipe(pipe2);
    if(res2 == -1){
        cout << "pipe error." << endl;
        exit(1);
    } 
    //fork用于创建一个子进程
    pid = fork();
    if(pid == 0){
        cout << "child." << endl;
        //child
        //子进程从管道1中读数据,写到管道2中
        close(pipe1[1]);
        close(pipe2[0]);
        
        server(pipe1[0],pipe2[1]);
       
        exit(0);

    }else if(pid > 0){
        cout << "parent." << endl;
        //father
        //父进程从管道2中读数据,写到管道1中
        close(pipe1[0]);
        close(pipe2[1]);
      
        client(pipe2[0],pipe1[1]);
       
        waitpid(pid,NULL,0);
    
        exit(0);
    }else{
        cout << "fork error." << endl;
        exit(1);
    }
    return 0;
}

客户端程序:
void client(int readfd,int writefd)
{
    size_t len;
    ssize_t n;
    char buff[MAXLINE];

    //char *fgets(char*buf,int bufsize,FILE* stream)
    //参数
    //    *buf:字符型指针,指向用来存储所的数据的地址
    //    bufsize:整型数据,指明存储数据的大小
    //    *stream:文件结构体指针,将要读取的文件流
    //返回值:
    //    1.成功,则返回第一个参数buf
    //    2.在读字符时遇到end-of-file,则eof指示器被设置,
    //    如果还没读入任何字符就遇到这种情况,则buf保持原来的内容,返回NULL
    //    3.如果发生读入错误,error指示器被设置,返回NULL,buf的值可能被改变
    
    //stdin代表输入的路径名
    fgets(buff,MAXLINE,stdin);
    len = strlen(buff);
    if(buff[len - 1] == '\n')
        len--;
    //将路径名写入到管道1
    write(writefd,buff,len);

    //客户端从管道2中读取文件的内容
    while((n = read(readfd,buff,MAXLINE)) > 0){
        write(STDOUT_FILENO,buff,n);
    }
    
}

服务器程序:
void server(int readfd,int writefd)
{
    int fd;
    ssize_t n;
    char buff[MAXLINE + 1];

    // ssize_t read(int fd,void* buf,size_t nbytes)
    // 把参数fd所指的文件的nbyte个字节读到buf指针所指的内存,
    // 若nbytes为0,则read()不会有作用并返回0;
    // 返回值为实际读取到的字节数,返回0表示已到达文件尾或无可取的数据,错误返回-1
    // 并将根据不同的错误原因适当的设置错误码
    
    //服务器从管道1中读取路径名
    n = read(readfd,buff,MAXLINE);
    if(n == 0){
        cout << "end_of_file while reading pathname." << endl;
        exit(1);
    }
    buff[n] = '\0';
    //以只读方式打开文件
    fd = open(buff,O_RDONLY);
    if(fd < 0){
        snprintf(buff + n,sizeof(buff) - n,":cant't open ,%s\n",strerror(errno));
        n = strlen(buff);
        write(writefd,buff,n);
    }else{
        //当服务器打开文件成功时,将文件的内容写入到管道2
        while((n = read(fd,buff,MAXLINE)) > 0){
            write(writefd,buff,n);
        }
        //关闭文件
        close(fd);
    }
}

头文件uitil.h

#pragma once

#include<iostream>
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#define MAXLINE 4096

using namespace std;

其客户端程序和服务器的程序基本没变,只是在main函数中有变动!!!

其执行结果:

程序执行结果是一样的。。。。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值