目录
一、管道通信
管道是由操作系统维护的一个文件,管道通信的本质就是将管道文件作为临界资源,实现不同进程之间的数据读写,但是管道只允许父子进程或者兄弟进程之间的通信。
管道文件本身是全双工机制的,但是在管道通信中,它的工作模式是半双工的,在管道通信之前,读端会关闭对管道文件的写通道,写端会关闭对管道文件的读通道。
当读端在读数据时,若写端关闭,读端会将管道文件中剩余数据读取结束之后再关闭;当写端在写数据时,若读端关闭,写端会被操作系统直接关闭。
那么管道通道如何实现数据交互呢?方法是创建两个管道进行通信。
管道通信方式分为匿名管道和命名管道,由 pipe() 系统调用创建并打开,命名管道由 mkfifo() 系统调用创建并由 open() 打开,他们的通信本质是一样的。
// comm.hpp 头文件
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <fstream>
#define NAMED_PIPE "./named_pipe"
bool create_fifo(const std::string& path) {
umask(0);
int n = mkfifo(path.c_str(), 0666);
if (n == 0) {
return true;
}
else {
std::cout << "errno: " << errno << "err string: " << strerror(errno) << std::endl;
return false;
}
}
void remove_fifo(const std::string& path) {
int n = unlink(path.c_str());
// unlink 函数功能是删除文件,但会在判断此文件状态之后再删除
// 若有进程打开此文件,则不会立即删除,等到无进程打开该文件时才会删除
// 若此文件有多个链接,则进行连接数减一操作
// 执行成功返回 0,失败返回 -1
assert(n == 0);
}
// Server
#include "comm.hpp"
int main() {
std::cout << "server begin" << std::endl;
int rfd = open(NAMED_PIPE, O_RDONLY);
if (rfd < 0) {
bool r = create_fifo(NAMED_PIPE);
assert(r);
rfd = open(NAMED_PIPE, O_RDONLY);
}
// read
char buffer[1024];
while (true) {
ssize_t s = read(rfd, buffer, sizeof(buffer) - 1);
if (s > 0) {
buffer[s] = 0;
std::cout << "client->server# " << buffer << std::endl;
}
else if (s == 0) {
std::cout << "client quit, me too" << std::endl;
break;
}
else {
std::cout << "err string: " << strerror(errno) << std::endl;
break;
}
}
close(rfd);
std::cout << "server end" << std::endl;
remove_fifo(NAMED_PIPE);
return 0;
}
// Client
#include "comm.hpp"
int main() {
std::cout << "client begin" << std::endl;
int wfd = open(NAMED_PIPE, O_WRONLY, 0666);
if (wfd < 0) {
exit(1);
}
// write
char buffer[1024];
while (true) {
std::cout << "please say# ";
fgets(buffer, sizeof(buffer), stdin);
if (strlen(buffer) > 0) {
buffer[strlen(buffe