muduo_net程式碼剖析之Channel
阿新 • • 發佈:2018-11-26
Channel簡介
- Channel是Reactor結構中的“事件”,它自始至終都屬於一個EventLoop,相當於libevent中的event_base,可以將event_base安插到EventLoop上,用EventLoop監視該Channel
- Channel負責一個檔案描述符的IO事件,它包含檔案描述符fd_,但實際上它不擁有fd_,不用負責將其關閉。
- 在Channel類中儲存這IO事件的型別以及對應的回撥函式,當IO事件發生時,最終會呼叫到Channel類中的回撥函式。Channel類一般不單獨使用,它常常包含在其他類中(Acceptor、Connector、EventLoop、TimerQueue、TcpConnection)使用。
Channel的使用步驟
Channel的使用步驟 |
---|
構造Channel物件:使用fd建立Channel物件,並給Channel關聯EventLoop |
給Channel物件註冊Read/Write回撥函式 |
呼叫enable*、disable*給Channel物件關注和取消讀寫事件 |
呼叫EventLoop的poll/epoll迴圈監控Channel物件可讀/可寫事件是否被觸發,一旦觸發,立即執行註冊的回撥函式 |
- Channel類有EventLoop的指標 loop_,通過這個指標可以向EventLoop中添加當前Channel事件。事件型別用events_表示,不同事件型別對應不同回撥函式
ReadEventCallback readCallback_;
EventCallback writeCallback_;
EventCallback closeCallback_;
EventCallback errorCallback_;
- 呼叫enable*、disable*關注和取消讀寫事件,都呼叫了update()函式,update()函式更新關心事件的時序圖:
Channel原始碼分析
Channel.h
class EventLoop;
class Channel : noncopyable
{
public:
typedef std::function<void()> EventCallback;//事件回撥處理
typedef std::function<void(Timestamp)> ReadEventCallback;//讀事件的回撥處理,傳一個時間戳
Channel(EventLoop* loop, int fd);//建構函式
~Channel();
//在EventLoop::loop()中呼叫了handleEvent()函式,用於處理被啟用的事件
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); }
... ...
//新增/刪除修改的事件,呼叫了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 const int kNoneEvent;
static const int kReadEvent;
static const int kWriteEvent;
EventLoop* loop_; //所屬EventLoop
const int fd_; //檔案描述符,但是不負責關閉該fd
int events_; //關注的事件
int revents_; //epoll/poll返回的事件
int index_; // used by Poller.表示在poll事件陣列中的序號
bool logHup_;// for POLLHUP
std::weak_ptr<void> tie_;
bool tied_;
bool eventHandling_; //是否處於處理事件中
bool addedToLoop_;
... ...
};
const int Channel::kNoneEvent = 0;
const int Channel::kReadEvent = POLLIN | POLLPRI;
const int Channel::kWriteEvent = POLLOUT;
... ...
void Channel::update() //更新事件型別
{
addedToLoop_ = true;
loop_->updateChannel(this); //呼叫loop的,loop再呼叫poll的註冊pollfd
}
... ...
void Channel::handleEvent(Timestamp receiveTime)
{
std::shared_ptr<void> guard;
if (tied_)
{
guard = tie_.lock();
if (guard)
{
handleEventWithGuard(receiveTime);
}
}
else
{
handleEventWithGuard(receiveTime);
}
}
//事件發生後,根據事件型別來呼叫回撥函式
void Channel::handleEventWithGuard(Timestamp receiveTime)
{
eventHandling_ = true;
LOG_TRACE << reventsToString();
if ((revents_ & POLLHUP) && !(revents_ & POLLIN)) //判斷返回事件型別
{
if (logHup_)//如果有POLLHUP事件,輸出警告資訊
{
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))//POLLRDHUP是對端關閉連線事件,如shutdown等
{
if (readCallback_) readCallback_(receiveTime);
}
if (revents_ & POLLOUT)
{
if (writeCallback_) writeCallback_();
}
eventHandling_ = false;//處理完了=false
}
... ...
handleEvent()函式的使用示例
示例程式碼1
在EventLoop類的成員函式EventLoop::loop()中,呼叫了handleEvent(),用於處理啟用的事件
//事件迴圈,該函式不能跨執行緒呼叫,只能在建立該物件的執行緒中呼叫
void EventLoop::loop()
{
... ...
while (!quit_)
{
... ...
//遍歷每一個啟用的通道currentActiveChannel_,並handleEvent
for (Channel* channel : activeChannels_)
{
currentActiveChannel_ = channel;
currentActiveChannel_->handleEvent(pollReturnTime_);
}
... ...
}
... ...
}