虚位以待(AD)
虚位以待(AD)
首页 > 操作系统 > Unix/BSD > UNIX网络编程之管道与FIFO

UNIX网络编程之管道与FIFO
类别:Unix/BSD   作者:码皇   来源:Dandelion_gong的博客     点击:

管道是最初的Unix IPC形式,它们的最大局限是没有名字,所以,管道只能用于有亲缘关系的进程只见使用。之后,慢慢随着FIFO的加入,这点才有所改观。FIFO也成为又名管道。管道和FIFO的共同点就是它们都是通过rea

管道是最初的Unix IPC形式,它们的最大局限是没有名字,所以,管道只能用于有亲缘关系的进程只见使用。之后,慢慢随着FIFO的加入,这点才有所改观。FIFO也成为又名管道。管道和FIFO的共同点就是它们都是通过read和write函数进行访问的。

管道:
管道时有pipe函数创建,提供一个单路数据流。也就是说,所有的管道都是半双工。
管道创建方法:
#include
int pipe(int fd[2]);

该函数返回两个文件描述符:fd[0] (用来打开读)、fd[1] (用来打开写)。管道只是它形象的叫法,它的本质实际上就是文件。

单个进程中的管道模式:
这里写图片描述
一般管道很少只在单个进程中进行使用。管道最常用于两个不同但有亲缘关系的进程(一个父进程,一个子进程。或两个有共同祖先的进程)中,提供进程间的通信。

进程间通信模式:
这里写图片描述

  只要有亲缘关系的两个进程都可以用管道进行通信。这里我们用父进程和子进程进行介绍。

  首先,我们有主进程创建一个管道后,调用fork()函数派生一个自身的副本。此时主进程将成为父进程,它的副本将成为子进程。完成这些预备操作后,父进程将关闭相应管道的读出端(fd[0]),子进程将关闭该管道的写入端(fd[1])。这样父进程可以通过write函数写入数据,而子进程通过read函数读出数据【必须先写入数据才能读出】。

  进程间双向数据流:

 

  双向数据与进程间单向数据流十分相似。只是它是创建了两个管道。父进程关闭了管道1的读端口(fd1[0])和管道2的写端口(fd2[1]),子进程则恰好相反,它关闭的是管道2的读端口(fd2[0])和管道1的写端口(fd1[1])。这样,两个管道可以保证数据的双向流动。父进程由管道2进行读数据,由管道1进行写数据,而子进程则由管道2写数据,由管道1读数据。

下面是进程间双向数据流的实现代码:
步骤:
(1)、创建管道1和管道2(利用pipe函数)
(2)、fork一个子进程
(3)、父进程关闭管道1的读端口(fd1[0])和管道2的写端口(fd2[1])
(4)、子进程关闭管道1的写端口(fd1[1])和管道2的读端口(fd2[1])

    #include#include#include#include#include#includeusing namespace std;
    int main(){
    //分别定义一个字符串数组记录父进程和子进程所传数据,最后一个为NULL char* parent_talk[] = {
    "Hello", "can you tell me current data and time?", "I have to go, Bye", NULL}
    ;
    char* child_talk[] = {
    "Hi", "No problem:", "Bye.",NULL}
    ;
    int fd1[2], fd2[2];
    //创建两个管道 //检测管道是否创建成功,如果创建成功会返回0,否则返回-1 if(pipe(fd1)<0) {
    printf("create pipe1 error.n");
    exit(1);
    }
    if(pipe(fd2)<0) {
    printf("create pipe2 error.n");
    exit(1);
    }
    pid_t pid;
    pid = fork();
    //fork一个子进程,并将子进程的id号符给父进程的pid if(pid == 0) //子进程没有自己的子进程,所以子进程pid = 0 {
    char buffer[256];
    //关闭子进程需要关闭的端口 close(fd1[1]);
    close(fd2[0]);
    int i=0;
    char *child = child_talk[i];
    while(child != NULL) {
    //只要子进程字符串数组不为NULL,就说明通信为及未结束 //从管道1中读出数据,并打印出来 read(fd1[0],buffer,256);
    printf("Parent:>%sn",buffer);
    //给管道2中写入数据 if(i == 1) {
    time_t t;
    time(&t);
    sprintf(buffer,"%s%s",child,ctime(&t));
    write(fd2[1],buffer,strlen(buffer)+1);
    }
    else{
    write(fd2[1],child,strlen(child)+1);
    }
    i++;
    child = child_talk[i];
    }
    //数据传输结束后,关闭所有端口 close(fd1[0]);
    close(fd2[1]);
    }
    //父进程 else if(pid > 0) {
    char buffer[256];
    close(fd1[0]);
    close(fd2[1]);
    int i = 0;
    char *parent = parent_talk[i];
    //父进程的字符串数组中数据不为NULL时,继续写入数据 while(parent != NULL) {
    //将数据写入管道1 write(fd1[1],parent,strlen(parent)+1);
    //从管道2中读出子进程发送的数据 read(fd2[0],buffer,256);
    printf("Child:>%sn",buffer);
    i++;
    parent = parent_talk[i];
    }
    //通信结束后,关闭所有端口 close(fd1[1]);
    close(fd2[0]);
    //等待子进程结束,然后回收它的空间,防止它成为孤儿进程 int status;
    wait(&status);
    }
    //如果pid不满足上述条件,则说明fork子进程失败 else {
    printf("Create child process error!n");
    }
    return 0;
    }

FIFO(有名管道):
FIFO即先进先出,每个FIFO有一个路径名与之相关联,所以它可以实现无亲缘关系的进程之间进行通信访问同一个FIFO。FIFO又称为又名管道。与管道不同的是,FIFO时有mkfifo函数创建,创建成功则返回0,失败则返回1。

    #include #include int mkfifo(const char *pathname, mode_t mode);

其中pathname是一个普通的路径名,它将是该FIFO的名字;mode则指定文件权限位,一般使用的权限位参数为:O_CREAT|O_EXCL,意思为,它要么创建一个新的FIFO,要么返回一个EEXIST(已存在错误)。在使用mkfifo函数时,它会检测是否返回EEXIST错误,如果返回该错误,则直接调用open函数打开即可。
在创建出一个FIFO后,必须打开读或写,但不能同时打开读和写,因为它和管道一样也是半双工。
对于管道和FIFO而言,write是往末尾添加数据,而read则是从头部返回数据。

用两个FIFO实现客户-服务器:
这里写图片描述
它的原理和用管道实现双向数据流相似,它是用FIFO1来进行服务器给客户端发送数据,而用FIFO2来实现客户端给服务器传送数据。

实现程序:
utili.h :头文件

    #pragma once#include#include#include#include#include#include#include using namespace std;
    //创建两个路径名(mkfifo函数中pathname参数)const char *write_fifo_name = "write_fifo";
    const char *read_fifo_name = "read_fifo";

ser.cpp:服务端程序:

    #include"utili.h"int main(){
    int write_fd;
    int read_fd;
    //创建一个write_fifo_name的FIFO int res = mkfifo(write_fifo_name,O_CREAT|O_EXCL|S_IRUSR|S_IWUSR);
    if(res == -1) //如果返回值为-1,则创建FIFO失败 {
    printf("make write fifo error.n");
    exit(1);
    }
    //创建成功后,以只写方式打开write_fifo_name管道 write_fd = open(write_fifo_name,O_WRONLY);
    //如果返回-1则表明打开失败 if(write_fd == -1) {
    printf("open write fifo error.n");
    unlink(write_fifo_name);
    exit(1);
    }
    //打开成功后等待客户端 printf("Wait Client Connect......n");
    //以只读方式打开read_fifo_name,并等待客户端的连接 while((read_fd = open(read_fifo_name, O_RDONLY)) == -1) {
    sleep(1);
    }
    printf("Client Connect Ok.n");
    定义一个发送数组和接收数组 char sendbuf[256];
    char recvbuf[256];
    while(1) {
    //服务器从write_fifo_name写入数据 printf("Ser:>");
    scanf("%s",sendbuf);
    write(write_fd,sendbuf,strlen(sendbuf)+1);
    //服务器从read_fifo_name读出来自客户端的数据 read(read_fd,recvbuf,256);
    printf("Cli:>%sn",recvbuf);
    }
    return 0;
    }

cli.cpp:客户端程序

    #include"utili.h"int main(){
    int write_fd, read_fd;
    //创建一个名为read_fifo_name的FIFO,如果创建失败则返回-1,成功则返回0 int res = mkfifo(read_fifo_name, O_CREAT|O_EXCL|S_IRUSR|S_IWUSR);
    if(res == -1) {
    printf("make read fifo error.n");
    exit(1);
    }
    //客户端以只读的形式打开write_fifo_name read_fd = open(write_fifo_name, O_RDONLY);
    if(read_fd == -1) //如果返回值为-1则表明打开失败 {
    printf("Server Error.n");
    unlink(read_fifo_name);
    exit(1);
    }
    //客户端以只写方式打开read_fifo_name write_fd = open(read_fifo_name,O_WRONLY);
    if(write_fd == -1) //如果返回值为-1,则打开失败 {
    printf("Client Connect Server Error.n");
    exit(1);
    }
    //定义两个字符串数组,分别用来存放客户端发送数据和接收的数据 char sendbuf[256];
    char recvbuf[256];
    while(1) {
    //客户端通过write_fifo_name来读取来自服务器的数据 read(read_fd,recvbuf,256);
    printf("Ser:>%sn",recvbuf);
    //客户端通过write_fifo_name写入数据 printf("Cli:>");
    scanf("%s",sendbuf);
    write(write_fd,sendbuf,strlen(sendbuf)+1);
    }
    return 0;
    }
相关热词搜索: