muduo原始碼分析:Acceptor類
阿新 • • 發佈:2018-11-19
Acceptor用於接受(accept)客戶端的連線,通過設定回撥函式通知使用者。它只在muduo網路庫內部的TcpServer使用,由TcpServer控制它的生命期。
實際上,Acceptor只是對 Channel 的封裝,通過Channel關注listenfd的 readable可讀事件 ,並設定好回撥函式就可以了。因此理解了上一節的muduo:Reactor,那麼Acceptor也比較容易理解。
Acceptor.h
class Acceptor : boost::noncopyable { public: typedef boost::function<void (int sockfd, const InetAddress&)> NewConnectionCallback; Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport); ~Acceptor(); void setNewConnectionCallback(const NewConnectionCallback& cb) //設定新連線處理回撥 { newConnectionCallback_ = cb; } bool listenning() const { return listenning_; } void listen(); //啟動監聽套接字 private: void handleRead(); //處理新連線到來的函式 EventLoop* loop_; Socket acceptSocket_; //監聽套接字,Socket是個RAII型,析構時自動close檔案描述符 Channel acceptChannel_; //通過channel,設定監聽套接字的readable事件以及回撥函式 //NewConnectionCallback是:typedef NewConnectionCallback boost::function<void(int sockfd, const InetAddress&)> NewConnectionCallback newConnectionCallback_; bool listenning_; int idleFd_; };
Acceptor::Acceptor() 構造
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport) : loop_(loop), acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())), acceptChannel_(loop, acceptSocket_.fd()), listenning_(false), idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) { assert(idleFd_ >= 0); acceptSocket_.setReuseAddr(true); acceptSocket_.setReusePort(reuseport); acceptSocket_.bindAddress(listenAddr); acceptChannel_.setReadCallback( boost::bind(&Acceptor::handleRead, this)); //設定監聽套接字可讀回撥。 }
Acceptor::~Acceptor() 析構
Acceptor::~Acceptor()
{
acceptChannel_.disableAll(); // 移除註冊的事件
acceptChannel_.remove(); // Poller會持有Channel的裸指標,所以需要將該Channel從Poller中刪除,避免Channel析構後,Poller持有空懸指標。
::close(idleFd_);
}
Acceptor::listen()
void Acceptor::listen() { loop_->assertInLoopThread(); listenning_ = true; acceptSocket_.listen(); //listen acceptChannel_.enableReading(); //註冊監聽套接字channel可讀事件 }
注意此處註冊可讀事件,發生新連線時會呼叫Channel的 handleEvent 從而呼叫readCallback_ (即建構函式設定的handleRead())
Acceptor::handleRead()
在有新連線時被呼叫。
void Acceptor::handleRead() //監聽套接字可讀事件的處理函式,有新連線時被呼叫
{
loop_->assertInLoopThread();
InetAddress peerAddr;
int connfd = acceptSocket_.accept(&peerAddr); //接受新連線
if (connfd >= 0) //當新連線成功建立
{
if (newConnectionCallback_)
{
newConnectionCallback_(connfd, peerAddr); //執行使用者回撥
}
else
{
sockets::close(connfd);
}
}
else
{
LOG_SYSERR << "in Acceptor::handleRead";
if (errno == EMFILE)
{
::close(idleFd_);
idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
::close(idleFd_);
idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
}
}
}
使用示例:
void newConnection(int sockfd, struct sockaddr &in_addr, socklen_t in_len)
{
printf_address(sockfd, &in_addr, in_len);
::write(sockfd, "jinger\n", 8);
::close(sockfd);
}
int main()
{
muduo::net::EventLoop loop;
muduo::net::Acceptor acceptor(&loop, "8090");
acceptor.setNewConnectionCallback(newConnection);
acceptor.listen();
loop.loop();
return 0;
}
使用者只需要設定好回撥函式然後listen即可。socket->bind->listen->accept
這些步驟在底層都已經封裝好了。