一、Makefile的编写
这里的Makefile文件的编写与前面不太一样,我们先看正确的代码:
.PHONY:all
all:server client
server:server.cpp
g++ -o $@ $^ -std=c++11
client:client.cpp
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f client
rm -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;
}
else
close(_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+name
int _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的编写
#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;
}
#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哈~)
好了,这个小项目基本到这了,感兴趣的可以给尝试改造为双端的,本文实现的仅仅是单端通信~