UNIX网络编程之管道与FIFO
来源:互联网 发布:卖家多久查出淘宝联盟 编辑:程序博客网 时间:2024/06/05 06:52
管道是最初的Unix IPC形式,它们的最大局限是没有名字,所以,管道只能用于有亲缘关系的进程之间使用。之后,慢慢随着FIFO的加入,这点才有所改观。FIFO也成为有名管道。管道和FIFO的共同点就是它们都是通过read和write函数进行访问的。
管道:
管道是由pipe函数创建,提供一个单路数据流。也就是说,所有的管道都是半双工。
管道创建方法:
#include <unistd.h> 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<iostream>#include<unistd.h>#include<stdio.h>#include<string.h>#include<stdlib.h>#include<sys/wait.h>using 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:>%s\n",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:>%s\n",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 <sys/types.h>#include <sys/stat.h>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<iostream>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<fcntl.h>#include <sys/stat.h>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:>%s\n",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:>%s\n",recvbuf); //客户端通过write_fifo_name写入数据 printf("Cli:>"); scanf("%s",sendbuf); write(write_fd,sendbuf,strlen(sendbuf)+1); } return 0;}
- UNIX网络编程之管道与FIFO
- UNIX网络编程之管道与FIFO
- UNIX网络编程卷二 笔记 管道和FIFO
- unix网络编程卷2:管道和FIFO
- UNIX网络编程之管道
- 【UNIX网络编程】FIFO
- unix网络编程----------fifo
- Unix管道与命名FIFO
- UNIX编程——管道和FIFO
- Linux系统编程之管道:匿名管道pipe与命名管道fifo
- 网络编程之FIFO
- UNIX命名管道FIFO
- unix网络编程----------管道 pipe
- 《UNIX网络编程 卷2》 笔记: FIFO
- UNIX环境高级编程——管道和FIFO限制
- 【UNIX网络编程】进程间通信之管道
- 基于Linux下C/C++的网络编程之进程(管道和FIFO)
- linux系统编程之管道(三):命令管道(FIFO)
- JAVA设计模式(02):创建型-抽象工厂模式(Abstract Factory)
- 架构
- 数据挖掘—LDA,PCA特征提取降维与SVM多分类在人脸识别中的应用-数据集ORL
- auto_ptr
- javascript学习——继承
- UNIX网络编程之管道与FIFO
- 认识WebSocket
- git patch操作
- 141. Linked List Cycle
- 一张图了解Android开源框架
- as3实现图片轮播
- PostMessage 向Windows窗口发送Alt组合键
- IoC 容器
- QRadioButton 分组