1 命名管道
前面講到匿名管道,有一個很大的限制,那就是只有具有相同祖先(具有親緣關系)的進程間才能進行通信,但是如果想實現不同進程間的通信,這個時候命名管道就發揮著巨大作用。
命名管道是一種特殊類型的文件,和匿名管道不一樣的是這個命名管道是有文件名的,有路徑的,有對應的inode,只是不用向磁盤中進行IO。
多個進程是否可以打開同一個文件呢?這個問題在前面講文件描述符時,就已經知道是可以的,例如:顯示器文件,多個進程都可以打開,并向顯示器寫入。
1.1 創建命名管道
命令行創建:
mkfifo filename
系統調用創建:
int mkfifo(const char *filename,mode_t mode);
參數為:創建管道的文件名,權限。?
1.2 匿名管道和命名管道使用不同
匿名管道由pipe函數創建并打開。
命名管道由mkfifo函數創建,打開用open
FIFO(命名管道)與pipe(匿名管道)之間唯一區別在它們創建與打開的方式不同,?但這些工作完成之后,它們具有相同的語義。
2 用命名管道實現server與client之間的通信
讀寫端共同包含的部分:
common.hpp
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <string>
#include <fcntl.h>
#include <unistd.h>const std::string filename="fifo";
const int mode=0666;const int size=1024;
權限,默認文件名。
namepipe類
默認構造函數
namepipe(const std::string &filename) : _filename(filename),_fd(defaultfd){}
成員_fd表示文件描述符,所以要得等到,打開文件時再確認,這里默認為-1。
初始化創建管道文件
bool InitNamepipe(){int n = mkfifo(filename.c_str(), mode);if (n == 0){std::cout << "mkfifo success " << std::endl;}else{perror("mkfifo");return false;}return true;}
利用mkfifo函數創建一個管道。
這里要注意一個從管道中寫入數據,一個向管道中讀取數據,所以就要有兩種打開方式:
也就是在open函數設置不同的權限
讀打開
bool OpenForRead(){_fd = open(filename.c_str(), O_RDONLY);if (_fd < 0){perror("open");return false;}else{std::cout << "open success: " << _fd << std::endl;}return true;}
寫打開
bool OpenForWrite(){_fd = open(filename.c_str(), O_WRONLY);if (_fd < 0){perror("open");return false;}else{std::cout << "open success: " << _fd << std::endl;}return true;}
讀數據
void Write(const std::string &in){write(_fd, in.c_str(), in.size());}
寫數據
bool Read(std::string *out){char buffer[size] = {0};ssize_t num = read(_fd, buffer, sizeof(buffer) - 1);if (num > 0){buffer[num] = 0;*out = buffer;}else if (num == 0){std::cout << "write out,me too" << std::endl;return false;}else{return false;}return true;}
讀取數據失敗或寫端關閉返回false,這里out是一個輸出型參數,將讀出的數據通過out帶出。
這里read也是默認阻塞等待寫端寫入。
回收資源
關閉文件描述符:
void Close(){if(_fd==defaultfd)return;elseclose(_fd);}
防止沒有打開文件,就直接close,所以這里有一個defaultfd的操作。
刪除管道文件:
void Remove(){int m=unlink(_filename.c_str());(void)m;}
namepipe.hpp
#pragma once
#include "common.hpp"const int defaultfd = -1;class namepipe
{
public:namepipe(const std::string &filename) : _filename(filename),_fd(defaultfd){}bool InitNamepipe(){int n = mkfifo(filename.c_str(), mode);if (n == 0){std::cout << "mkfifo success " << std::endl;}else{perror("mkfifo");return false;}return true;}bool OpenForRead(){_fd = open(filename.c_str(), O_RDONLY);if (_fd < 0){perror("open");return false;}else{std::cout << "open success: " << _fd << std::endl;}return true;}bool OpenForWrite(){_fd = open(filename.c_str(), O_WRONLY);if (_fd < 0){perror("open");return false;}else{std::cout << "open success: " << _fd << std::endl;}return true;}void Write(const std::string &in){write(_fd, in.c_str(), in.size());}bool Read(std::string *out){char buffer[size] = {0};ssize_t num = read(_fd, buffer, sizeof(buffer) - 1);if (num > 0){buffer[num] = 0;*out = buffer;}else if (num == 0){std::cout << "write out,me too" << std::endl;return false;}else{return false;}return true;}void Close(){if(_fd==defaultfd)return;elseclose(_fd);}void Remove(){int m=unlink(_filename.c_str());(void)m;}~namepipe(){}private:std::string _filename;int _fd;
};
client
#include"namepipe.hpp"int main()
{namepipe pipe(filename);pipe.OpenForWrite();while(true){std::string in;std::getline(std::cin,in);pipe.Write(in);}pipe.Close();
}
client端發送數據。
server
#include"namepipe.hpp"int main()
{namepipe pipe(filename);pipe.InitNamepipe();pipe.OpenForRead();while(true){std::string* out;if(pipe.Read(out))std::cout<<*(out)<<std::endl;elsebreak;}pipe.Close();pipe.Remove();return 0;
}
server端讀數據。
結果演示:
?
在client端輸入數據,server就可以看到對應的讀取數據。?