muduo网络库学习(二)对套接字和监听事件的封装Channel
来源:互联网 发布:淘宝客服介入会怎么样 编辑:程序博客网 时间:2024/05/22 02:07
muduo对描述符fd,需要监听的事件events,当fd被激活调用的可读/可写/关闭/错误回调函数进行了封装,实现在Channel
类中,Poller
监听的其实就是一个个Channel
对象,Channel
可以向Poller
注册自己关心的事件,当被激活后调用相应的回调函数。类似libevent的struct event。
Channel在整个事件驱动循环中的流程大致如下
- Acceptor接收到客户端请求,调用TcpServer回调函数
- TcpServer回调函数中创建TcpConnection对象,代表着一个Tcp连接
- TcpConnection构造函数中创建Channel对象,保存客户端套接字fd和关心的事件(可读)
- Channel注册自己到所属事件驱动循环(EventLoop)中的Poller上
- Poller开始监听,当发现Channel被激活后将其添加到EventLoop的激活队列中
- EventLoop在poll返回后处理激活队列中的Channel,调用其处理函数
- Channel在处理函数中根据被激活的原因调用不同的回调函数(可读/可写等)
其中
- Acceptor,监听类,用于监听客户端请求,然后接收客户端
- TcpServer,服务器类,用于管理所有的TcpConnection
- TcpConnection,Tcp连接类,代表一个Tcp连接,内部保存对应的Channel
- Channel,套接字和相应事件及回调函数的封装,一个事件类
- Poller,io复用的封装,用于监听Channel
- EventLoop,事件驱动主循环,调用poll函数,管理激活队列,类似libevent的struct event_base
函数对象std::function
C++基于对象其实就是利用std::function/std::bind
实现的,作用是绑定某个类的函数指针
使用方法如
#include <functional>typedef std::function<void()> Callback;void function_bind_test(Callback func){ func();/* 这里调用相当于conn->connectionEstablished() */}function_bind_test(std::bind(&TcpConnection::connectionEstablished, conn));
std::function/std::bind
为类的成员函数的调用提供更多灵活性,在C++11引入lambda
后逐渐取代了std::bind
,C++14后lambda
支持模板参数后完全取代了std::bind
Channel定义如下,成员函数主要就是
- 设置回调函数
set*Callback
- 设置对fd关心的事件,将自己存储的fd及相应的事件注册到Poller中
enable*
- 删除对fd的监听,将其从
Poller
的ChannelMap
中移除disableAll
- 被激活时调用的回调函数
hanleEvent
/* * Channel其实就是一个事件类,保存fd和需要监听的事件,以及各种回调函数 * 类似libevent的struct event */class Channel : noncopyable{ public: /* * function,可调用函数对象,类似函数指针 * 模板参数是函数标签,例如 * function<int(int, int)> func; * 这个函数对象的两个参数类型都是int,返回值类型是int */ typedef std::function<void()> EventCallback; typedef std::function<void(Timestamp)> ReadEventCallback; Channel(EventLoop* loop, int fd); ~Channel(); /* 被激活后调用的函数,内部调用下面设置的几个函数 */ void handleEvent(Timestamp receiveTime); /* 设置回调函数 */ void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); } void setWriteCallback(EventCallback cb) { writeCallback_ = std::move(cb); } void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); } void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); } /// Tie this channel to the owner object managed by shared_ptr, /// prevent the owner object being destroyed in handleEvent. /* 用于保存TcpConnection指针 */ void tie(const std::shared_ptr<void>&); int fd() const { return fd_; } int events() const { return events_; } void set_revents(int revt) { revents_ = revt; } // used by pollers // int revents() const { return revents_; } bool isNoneEvent() const { return events_ == kNoneEvent; } /* 将自己注册到Poller中,或从Poller中移除,或只删除某个事件,update实现 */ void enableReading() { events_ |= kReadEvent; update(); } void disableReading() { events_ &= ~kReadEvent; update(); } void enableWriting() { events_ |= kWriteEvent; update(); } void disableWriting() { events_ &= ~kWriteEvent; update(); } void disableAll() { events_ = kNoneEvent; update(); } bool isWriting() const { return events_ & kWriteEvent; } bool isReading() const { return events_ & kReadEvent; } // for Poller int index() { return index_; } void set_index(int idx) { index_ = idx; } // for debug string reventsToString() const; string eventsToString() const; void doNotLogHup() { logHup_ = false; } EventLoop* ownerLoop() { return loop_; } void remove(); private: static string eventsToString(int fd, int ev); void update(); void handleEventWithGuard(Timestamp receiveTime); static const int kNoneEvent; static const int kReadEvent; static const int kWriteEvent; EventLoop* loop_; const int fd_; int events_; int revents_; // it's the received event types of epoll or poll /* * 保存fd在epoll/poll中的状态,有: * 还没有添加到epoll中 * 已经添加到epoll中 * 添加到epoll中,又从epoll中删除了 */ int index_; // used by Poller. bool logHup_; /* * tie_存储的是TcpConnection类型的指针,即TcpConnectionPtr * 一个TcpConnection代表一个已经建立好的Tcp连接 */ std::weak_ptr<void> tie_; bool tied_; bool eventHandling_; bool addedToLoop_; ReadEventCallback readCallback_; EventCallback writeCallback_; EventCallback closeCallback_; EventCallback errorCallback_;};
.cpp
中的几个比较重要的函数介绍
/* * 由TcpConnection中connectEstablished函数调用 * 作用是当建立起一个Tcp连接后,让用于监测fd的Channel保存这个Tcp连接的弱引用 * tie_是weak_ptr的原因 * weak_ptr是弱引用,不增加引用基数,当需要调用内部指针时 * 可通过lock函数提升成一个shared_ptr,如果内部的指针已经销毁 * 那么提升的shared_ptr是null * 可以通过是否是null判断Tcp是否还处于连接,因为如果断开,那么这个TcpConnection就被销毁了 */void Channel::tie(const std::shared_ptr<void>& obj){ tie_ = obj; tied_ = true;}
因为Channel
的回调函数其实调用的是TcpConnection
中的函数,所以如果想要调用,就必须知道TcpConnection
的指针,而如果TcpConnection
被销毁了,那么指针调用会出错,所以采用智能指针,又因为Channel
只需要知道TcpConnection
是否还存在,所以不需要使用shared_ptr
,这会增加引用计数,导致TcpConnection
销毁不了(如果close连接,TcpConnection
本应该被销毁,但是因为Channel
中也留有对TcpConnection
的引用,所以不会销毁,糟糕的事情…),所以采用weak_ptr
,不增加引用计数,适当时可以提升为shared_ptr
/* * 根据fd激活事件的不同,调用不同的fd的回调函数 */void Channel::handleEvent(Timestamp receiveTime){ /* * RAII,对象管理资源 * weak_ptr使用lock提升成shared_ptr,此时引用计数加一 * 函数返回,栈空间对象销毁,提升的shared_ptr guard销毁,引用计数减一 */ std::shared_ptr<void> guard; if (tied_) { guard = tie_.lock(); if (guard) { handleEventWithGuard(receiveTime); } } else { handleEventWithGuard(receiveTime); }}
经典的RAII应用场景,配合智能指针
如果想要判断Channel
所在的TcpConnection
是否仍然存在(没有被关闭),可以直接将weak_ptr
提升为shared_ptr
,如果提升成功(不为NULL),代表TcpConnection
存在,调用回调函数即可。
提升成shared_ptr
后TcpConnection
引用计数变为2,handleEvent
函数返回后局部变量guard
销毁,引用计数恢复到1。
同样的应用还有锁对象,创建时上锁,析构时解锁
handleEventWithGuard
其实就是根据不同的激活原因滴啊用不同的回调函数,这些回调函数都在TcpConnection
中,也是通过上面std::function/std::bind
设置的,所以在调用前必须判断Channel
所在的TcpConnection
是否还存在
/* * 根据被激活事件的不同,调用不同的回调函数 */void Channel::handleEventWithGuard(Timestamp receiveTime){ eventHandling_ = true; LOG_TRACE << reventsToString(); if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) { if (logHup_) { LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLHUP"; } if (closeCallback_) closeCallback_(); } if (revents_ & POLLNVAL) { LOG_WARN << "fd = " << fd_ << " Channel::handle_event() POLLNVAL"; } if (revents_ & (POLLERR | POLLNVAL)) { if (errorCallback_) errorCallback_(); } if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) { if (readCallback_) readCallback_(receiveTime); } if (revents_ & POLLOUT) { if (writeCallback_) writeCallback_(); } eventHandling_ = false;}
- muduo网络库学习(二)对套接字和监听事件的封装Channel
- Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)
- Muduo网络库源码分析(一) EventLoop事件循环(Poller和Channel)
- muduo网络库学习(一)对io复用的封装Poller,面向对象与基于对象
- muduo库阅读(26)——Net部分:套接字常用操作的封装
- 学习muduo库之对互斥锁和条件变量的封装
- muduo网络库学习之EventLoop(一):事件循环类图简介和muduo 定时器TimeQueue
- muduo网络库学习(四)事件驱动循环EventLoop
- Muduo网络库源码分析(四)EventLoopThread和EventLoopThreadPool的封装
- Muduo网络库源码分析(四)EventLoopThread和EventLoopThreadPool的封装
- muduo网络库学习之EventLoop(二):进程(线程)wait/notify 和 EventLoop::runInLoop
- muduo网络库学习(五)服务器监听类Acceptor及Tcp连接TcpConnection的建立与关闭
- muduo网络库学习笔记(8):高效日志类的封装
- muduo网络库学习之Exception类、Thread 类封装中的知识点(重点讲pthread_atfork())
- muduo网络库学习(三)定时器TimerQueue的设计
- 对套接字简单的封装
- muduo库阅读(30)——Net部分:事件处理器Channel
- muduo网络库学习(八)事件驱动循环线程池EventLoopThreadPool
- Built-in shader variables 内置的着色器变量
- Codeforces Gym 101190 NEERC 2016 B. Binary Code
- 自定义组合控件textview
- 【1011】判素数
- sqlserver2008的安装
- muduo网络库学习(二)对套接字和监听事件的封装Channel
- angularJs 正 到 排序
- rest服务理解以及restful api
- 基于java的设计模式的代理模式
- thread相关——互斥锁
- 论文阅读:《Realtime Multi-Person 2D Pose Estimation using Part Affinity Fields》CVPR 2017
- Mob手机验证码android studio实现
- Linux Unit11--系统中的文件传输
- Construct2从入门到不放弃