#ifndef MUDUO_NET_ACCEPTOR_H #define MUDUO_NET_ACCEPTOR_H #include <boost/function.hpp> #include <boost/noncopyable.hpp> #include <muduo/net/Channel.h> #include <muduo/net/Socket.h> namespace muduo { namespace net { class EventLoop; class InetAddress; /// /// Acceptor of incoming TCP connections. /// class Acceptor : boost::noncopyable { public: typedef boost::function<void (int sockfd, const InetAddress&)> NewConnectionCallback; Acceptor(EventLoop* loop, const InetAddress& listenAddr); ~Acceptor(); void setNewConnectionCallback(const NewConnectionCallback& cb) { newConnectionCallback_ = cb; } bool listenning() const { return listenning_; } void listen(); private: void handleRead(); EventLoop* loop_; // 对应的事件循环 Socket acceptSocket_; Channel acceptChannel_; // 通道观察socketfd的可读事件 NewConnectionCallback newConnectionCallback_; // 新连接到来的回调函数 bool listenning_; // 释放监听中 int idleFd_; }; } } #endif // MUDUO_NET_ACCEPTOR_H
#include <muduo/net/Acceptor.h> #include <muduo/net/EventLoop.h> #include <muduo/net/InetAddress.h> #include <muduo/net/SocketsOps.h> #include <boost/bind.hpp> #include <errno.h> #include <fcntl.h> //#include <sys/types.h> //#include <sys/stat.h> using namespace muduo; using namespace muduo::net; Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr) : loop_(loop), acceptSocket_(sockets::createNonblockingOrDie()), acceptChannel_(loop, acceptSocket_.fd()), listenning_(false), idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) { assert(idleFd_ >= 0); acceptSocket_.setReuseAddr(true); // 设置地址重复利用 acceptSocket_.bindAddress(listenAddr); acceptChannel_.setReadCallback( // 设置通道有读事件的时候的回调函数 boost::bind(&Acceptor::handleRead, this)); } Acceptor::~Acceptor() { acceptChannel_.disableAll(); acceptChannel_.remove(); ::close(idleFd_); } void Acceptor::listen() { loop_->assertInLoopThread(); listenning_ = true; acceptSocket_.listen(); acceptChannel_.enableReading(); } void Acceptor::handleRead() { loop_->assertInLoopThread(); InetAddress peerAddr(0); //FIXME loop until no more int connfd = acceptSocket_.accept(&peerAddr); // 调用socket的accept来等待客户端的连接 if (connfd >= 0) { // string hostport = peerAddr.toIpPort(); // LOG_TRACE << "Accepts of " << hostport; if (newConnectionCallback_) { newConnectionCallback_(connfd, peerAddr); // 连接到来调用新连接的回调函数来处理 } else { sockets::close(connfd); } } else { // By Marc Lehmann, author of livev. 优雅的关闭错误的fd 先准备一个空闲的fd来连接 if (errno == EMFILE) // EMFILE too many fd 处理太多的链接 { ::close(idleFd_); idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL); ::close(idleFd_); idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC); // 由于是电平触发这里准备一个空闲的文件描述符 } } }