赞
踩
目录
用C/C++代码编译实现 client进程 和 server进程的通信案例 :
管道,是Linux下常用的进程间通信手段,具体的通信方法是 父进程打开管道文件,被子进程继承,通过文件描述符fd,父子进程能看到相同的内存级文件,而后 一个进程往管道中写,另一个进程读,完成信息的传送。
而打开管道文件与普通文件不同的是,前者具有访问控制,即同步和互斥机制,也就是具有原子性:
1.从管道中读取数据时,如果管道为空,或正在被写入,执行读取的进程会阻塞等待
2.往管道中写入数据时,如果管道满了,或正在被读取,执行写入的进程会阻塞等待
3.当写端关闭后,读端读取到EOF
以上三点机制,使得管道能像文件一样操作的同时,避同时免了使用普通文件进程间通信的 极度的 不安全性。并且,数据在管道文件中的读写是彻底的内存级别的,即不与磁盘交互,读和写皆是在内存中,效率也有一定的保证。
注意管道文件只是单向的,也就是一个进程读,一个进程写的模式,原生规定好的,使用需要符合规范
匿名管道的通信特点是,通过系统调用pipe(int fd[2]),直接在内核中创建一个内存级文件,并自动被调用它的函数分别以读和写的方式打开两次,获得两个fd。存放在输出型参数fd数组中,fd[0]代表读端fd,fd[1]代表写端fd。两个参与通信的进程能分别通过read和write这样的系统调用接口,以文件的方式对管道进行读写操作,实现数据的传递。
1.子进程 进程从标准输入按行读取信息
2.通过管道传入到父进程进程中
3.由父进程进程读取并输出到标准输出流。
1.调用pipe(int*fd[2])创建匿名通信管道
2.fork()创建子进程,子进程会继承fd[2]。
子进程:
(1)关闭读端:close(fd[0])
注:管道只支持单向通信,这里要求子写父读,你也可以实现为父写子读.
(2)从标准输入(键盘)读取按行读入
(3)将从键盘读入的内容写入管道
父进程
(1)关闭写端 close(fd[1])
(2)从管道读取数据知道读到文件结尾
(3)等待子进程退出
- #include<string.h>
- #include<stdlib.h>
- #include<errno.h>
- #include<assert.h>
- #include<stdio.h>
- #include<sys/wait.h>
- #include<sys/types.h>
- #include<unistd.h>
-
- #define buffer_size (128)
- int main(){
- int fd[2]={0,0};
- int pipe_ret=pipe(fd);
- if(pipe_ret) return errno;//调用出错返回错误码
- pid_t child_pid=fork();
- if(child_pid==-1) return errno;
- if(child_pid==0){
- //child
- //关闭不需要的端口
- close(fd[0]);
- char child_buffer[buffer_size];
- while(1){
- //从键盘读入数据
- ssize_t read_size=read(0,child_buffer,buffer_size-1);
- //保证安全性
- assert(read_size<buffer_size);
- child_buffer[read_size]=0;
- //结束条件
- if(strcmp(child_buffer,"_exit\n")== 0)
- break;
- //发送给父进程
- write(fd[1],child_buffer,read_size+1);
- }
- close(fd[1]);
- exit(0);
- }
- else{
- //father
- close(fd[1]);
- char father_buffer[buffer_size];
- while(1){
- //从管道读取数据
- ssize_t read_size=read(fd[0],father_buffer,buffer_size-1);
- //判断是否为文件结尾(子进程关闭写端)
- if(read_size==0)
- break;
- //保证安全性
- assert(read_size<=buffer_size);
- //输出内容
- printf("父进程收到:%s",father_buffer);
- }
- close(fd[0]);
- //回收子进程
- pid_t wait_ret=waitpid(child_pid,(int*)0,0);
- if(wait_ret==-1) return errno;
- }
-
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
运行效果:
首先明确 进程间通信的本质是 "让不同的进程,访问到同一份资源"
匿名管道如何使不同进程访问到同一份资源?
父进程分别以读和写两次打开管道文件,而后创建子进程继承文件表述符,父子进程能够访问到同一份资源,此时能够实现一个进程写一个进程读,进而就具备了通信间通信的客观条件。
但是这样通过继承管道文件的文件描述符fd,来访问到同一份资源的方式有一个明显的缺陷,那就是匿名管道只能用于有血缘关系的进程间通信
而命名管道则解决了这个问题,能让没有血缘关系的进程也能访问到同一个管道文件
在磁盘上创建一个命名管道文件,利用文件名来表示管道的唯一性,只要打开相同的文件名,就能访问到相同的管道文件。两个进程通过open分别以读和写打开文件,利用write和read就能完成进程间通信,也不需要血缘关系。
需要注意的是,文件名只是用来标识唯一性,数据的传输更匿名管道一样,完全是内存级的。
方法一:系统指令 mkfifo
方法二:系统调用 mkfifo(const char* 文件名,mode_t 文件权限)
成功返回0,失败返回-1(包括文件已经存在的情况),并设置好退出码。
1.client 进程从标准输入按行读取信息
2.通过管道传入到server进程中
3.由server进程读取并输出到标准输出流。
1.创建 client.c server.c myfifo.hpp 在头文件中定义命名管道的路径
2.client 和 server 都先调用mkfifo。若返回-1根据errno判断是否存在。若errno不为-1则终止进程返回退出码
3.client 调用open 以只写的方式打开,server以只写的方式打开管道文件
4.client调用write server调用read 完成通信
头文件: myfifo.hpp
- #pragma once
- #include<assert.h>
- #include<string.h>
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/stat.h>
- #include<sys/fcntl.h>
- #include<errno.h>
- #include<stdio.h>
- //文件路径 隐藏文件
- #define fifo_path "./.myfifo"
- #define fifo_mode 0666
- //缓冲区大小
- #define buffer_size 128
源文件:client.c server.c
client.c:
- #include"myfifo.hpp"
- int main(){
- //创建命名管道
- int mkfifo_ret=mkfifo(fifo_path,fifo_mode);
- //判断是否存在
- if(mkfifo_ret==-1&&errno!=EEXIST)
- return errno;
- //打开管道 只写
- int myfifo_fd= open(fifo_path,O_WRONLY);
- char client_buffer[buffer_size];
- while(1){
- //从标准输入读取数据
- printf("请输入:> ");
- fflush(stdout);
- ssize_t read_ret=read(0,client_buffer,buffer_size-1);
- //保证安全性
- assert(read_ret<buffer_size);
- client_buffer[read_ret]=0;
- //像管道写入
- write(myfifo_fd,client_buffer,read_ret+1);
- if(strcmp(client_buffer,"_exit\n")==0)
- break;
- }
- //关闭写端 server会读取到EOF
- close (myfifo_fd);
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
server.c
- #include"myfifo.hpp"
- int main(){
- //创建命名管道
- int mkfifo_ret=mkfifo(fifo_path,fifo_mode);
- //判断是否存在
- if(mkfifo_ret==-1&&errno!=EEXIST)
- return errno;
- //打开管道 只读
- int myfifo_fd= open(fifo_path,O_RDONLY);
- char server_buffer[buffer_size];
- //从管道接收数据
- while(1){
- ssize_t read_ret=read(myfifo_fd,server_buffer,buffer_size-1);
- if(read_ret==0)
- break;
- //保证安全性
- assert(read_ret<=buffer_size);
- //输出到标准输出
- printf("服务器收到:> %s",server_buffer);
- }
- close(myfifo_fd);
- return 0;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
运行效果
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。