Linux系统之----命名管道模拟实现客户端、服务器
一、Makefile的编写
这里的Makefile文件的编写与前面不太一样,我们先看正确的代码:
.PHONY:all
all:server clientserver:server.cppg++ -o $@ $^ -std=c++11client:client.cpp g++ -o $@ $^ -std=c++11.PHONY:clean
clean:rm -f clientrm -f server
此处的all不可以省略,如果省略了你之后在make时只会编译server.cpp文件,client.cpp不会编译,不信可以试一下~
讲的专业一点就是,在这个 Makefile
中,all
是一个 PHONY
目标,它依赖于 server
和 client
两个目标。这意味着当你运行 make
命令时,如果没有指定其他目标,make
将默认执行 all
目标,从而编译 server.cpp
和 client.cpp
文件。
二、项目本体的编写
由于客户端和服务端都是通过命名管道来写的,所以二者基本逻辑相同,故项目组成由三部分:
client.cpp server.cpp NamePipe.hpp,但是为了简化NamePipe.hpp中的代码,我们在额外设计一个common.hpp来辅助减轻一下其代码量,主要用于存放头文件以及一些全局变量啥的~
三、NamedPipe.hpp的编写
在此文件中,我们要构造出命名管道的模样,即命名管道的模拟实现,包括类的初始化,析构以及各种库函数的实现。我们主要分享一下方法函数实现部分内容~
方法函数主要包括管道的创建,读,写,移除,关闭,以及只读打开还有只写打开
#pragma once
#include "common.hpp"
const int defaultfd = -1; // 检查错误用的
class NamedPipe
{
public:NamedPipe(const string &name): _name(name), _fd(defaultfd){}~NamedPipe(){}bool Create(){// 1.创建管道文件int n = mkfifo(fifoname.c_str(), mode);if (n == 0){cout << "mkfifo success" << endl;}else{perror("mkfifo");return false;}return true;}void Close(){if (_fd == defaultfd) // 这里如果open那里没打开文件,_fd就为-1,这里直接返回{return;}elseclose(_fd);}bool OpenForRead(){_fd = open(fifoname.c_str(), O_RDONLY);if (_fd < 0){// 打开失败perror("open");return false;}cout << "open file success" << endl;return true;}bool OpenForWrite(){_fd = open(fifoname.c_str(), O_WRONLY);if (_fd < 0){perror("open");return false;}return true;}// 输入参数:const &// 输出参数:*// 输入输出参数:&bool Read(string *out){char buffer[SIZE]={0};ssize_t num = read(_fd,buffer,sizeof(buffer)-1);if(num>0){//读取成功buffer[num]=0;*out =buffer;}else{return false;}return true;}void Write(const string &in){write(_fd,in.c_str(),in.size());}void Remove(){int m=unlink(fifoname.c_str());(void)m;}
private:string _name; // path+nameint _fd; // who
};
我们还要定义一些变量,不如再创一个文件,就叫commom.hpp吧
#ifndef __COMMON_HPP__
#define __COMMON_HPP__#include <iostream>
#include <cstdio>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>using namespace std;
const string fifoname="fifo";
mode_t mode=0666;
#define SIZE 128#endif
四、client.cc和server.cc的编写
这是client.cc
#include"NamedPipe.hpp"int main()
{NamedPipe namedpipe(fifoname);namedpipe.OpenForWrite();while(true){cout<<"Please Enter# ";string line;getline(cin,line);namedpipe.Write(line);}namedpipe.Close();return 0;
}
这是server.cc
#include "NamedPipe.hpp"int main()
{NamedPipe namedpipe(fifoname);namedpipe.Create();namedpipe.OpenForRead();// 2. 打开文件,就和普通文件没有区别// 命名管道操作特点: 打开一端,在另一端没打开的时候,open会阻塞// 3. 通信string message;while(true){bool res=namedpipe.Read(&message);if(!res)break; cout<<"client say@"<<message<<endl;}// 4. 归还资源cout<<"对方已将你删除!!!"<<endl;namedpipe.Close();namedpipe.Remove();return 0;
}
五、运行结果
结果解释:client端发送的消息可以在server端接收到,在使用时,先连接./server,再连接./client
(ps:作者小趴菜不是舔gou哈~)
好了,这个小项目基本到这了,感兴趣的可以给尝试改造为双端的,本文实现的仅仅是单端通信~