系列文章目录
《ZLToolKit源码学习笔记》(1)VS2019源码编译
《ZLToolKit源码学习笔记》(2)工具模块之日志功能分析
《ZLToolKit源码学习笔记》(3)工具模块之终端命令解析
《ZLToolKit源码学习笔记》(4)工具模块之消息广播器
《ZLToolKit源码学习笔记》(6)线程模块之整体框架概述
《ZLToolKit源码学习笔记》(7)线程模块之线程池组件:任务队列与线程组
《ZLToolKit源码学习笔记》(8)线程模块之线程负载计算器
《ZLToolKit源码学习笔记》(9)线程模块之任务执行器
《ZLToolKit源码学习笔记》(11)线程模块之工作线程池WorkThreadPool
《ZLToolKit源码学习笔记》(12)事件轮询模块之整体框架概述
《ZLToolKit源码学习笔记》(13)事件轮询模块之管道的简单封装(本文)
《ZLToolKit源码学习笔记》(14)事件轮询模块之定时器
《ZLToolKit源码学习笔记》(15)事件轮询模块之事件轮询器EventPoller
《ZLToolKit源码学习笔记》(16)网络模块之整体框架概述
《ZLToolKit源码学习笔记》(17)网络模块之基础接口封装类SockUtil
《ZLToolKit源码学习笔记》(18)网络模块之Buffer缓存
《ZLToolKit源码学习笔记》(19)网络模块之套接字封装
《ZLToolKit源码学习笔记》(20)网络模块之TcpServer
《ZLToolKit源码学习笔记》(21)网络模块之TcpClient与Session
《ZLToolKit源码学习笔记》(22)网络模块之UdpServer
前言
管道是进程(线程)间通信的一种常用手段,本节学习下ZLToolKit对管道的封装。
目录
一、概述
ZLToolKit的管道使用主要是在EventPoller中,用于事件轮询器内部事件处理。管道的封装类是PipeWrap,在linux中,直接用pipe函数实现,在windows下,作者使用了tcp socket模拟管道的实现(实际上windows也有管道,如createPipe、CreateNamedPipe)。
二、PipeWrap
这里主要看下该类构造函数中关于管道的创建。
可以看到,windows下,使用基于TCP的socket实现了管道的模拟。pipe_fd[0]是accept的返回值,用于管道的读端fd,pipe_fd[1]是connect的返回数据,用于管道的写端。linux下,使用的pipe函数。
PipeWrap::PipeWrap(){
#if defined(_WIN32)
_listenerFd = SockUtil::listen(0, "127.0.0.1");
checkFD(_listenerFd)
SockUtil::setNoBlocked(_listenerFd,false);
auto localPort = SockUtil::get_local_port(_listenerFd);
_pipe_fd[1] = SockUtil::connect("127.0.0.1", localPort,false);//客户端相当于管道写端
checkFD(_pipe_fd[1])
_pipe_fd[0] = (int)accept(_listenerFd, nullptr, nullptr);//服务端相当于管道读端
checkFD(_pipe_fd[0])
SockUtil::setNoDelay(_pipe_fd[0]);
SockUtil::setNoDelay(_pipe_fd[1]);
#else
if (pipe(_pipe_fd) == -1) {
throw runtime_error(StrPrinter << "create posix pipe failed:" << get_uv_errmsg());\
}
#endif // defined(_WIN32)
SockUtil::setNoBlocked(_pipe_fd[0],true);
SockUtil::setNoBlocked(_pipe_fd[1],false);
SockUtil::setCloExec(_pipe_fd[0]);
SockUtil::setCloExec(_pipe_fd[1]);
}
三、Pipe
Pipe类比较简单,主要是将其两个成员变量关联起来,即PipeWrap和EventPoller。
class Pipe
{
public:
Pipe(const function<void(int size,const char *buf)> &onRead=nullptr, const EventPoller::Ptr &poller = nullptr);
~Pipe();
void send(const char *send,int size=0);
private:
std::shared_ptr<PipeWrap> _pipe;
EventPoller::Ptr _poller;
};
这里看下构造函数:
Pipe::Pipe(const function<void(int size, const char *buf)> &onRead,
const EventPoller::Ptr &poller) {
_poller = poller;
if(!_poller){
_poller = EventPollerPool::Instance().getPoller();
}
_pipe = std::make_shared<PipeWrap>();
auto pipeCopy = _pipe;
_poller->addEvent(_pipe->readFD(), Event_Read, [onRead, pipeCopy](int event) {
#if defined(_WIN32)
unsigned long nread = 1024;
#else
int nread = 1024;
#endif //defined(_WIN32)
ioctl(pipeCopy->readFD(), FIONREAD, &nread);
#if defined(_WIN32)
std::shared_ptr<char> buf(new char[nread + 1], [](char *ptr) {delete[] ptr; });
buf.get()[nread] = '\0';
nread = pipeCopy->read(buf.get(), nread + 1);
if (onRead) {
onRead(nread, buf.get());
}
#else
char buf[nread + 1];
buf[nread] = '\0';
nread = pipeCopy->read(buf, sizeof(buf));
if (onRead) {
onRead(nread, buf);
}
#endif // defined(_WIN32)
});
}
将PipeWrap的读端fd添加到EventPoller中管理,并给该fd读事件关联了一个lambda回调,当用户调用send函数通过PipeWrap写端发送数据后,EventPoller监听到读事件,调用其关联的lambda回调,在该lambda回调中读取数据,最后再通过OnRead返回给用户。
四、测试
ZLToolKit内部仅使用了PipeWrap类,没有用到Pipe。对于Pipe的测试代码,可以参见test_pipe.cpp。