muduo库阅读(39)——Net部分:接收者Accpetor

来源:互联网 发布:长绸扇子淘宝 编辑:程序博客网 时间:2024/04/30 14:09
Accpetor的作用如下:
1、创建监听(接收者)套接字
2、设置套接字选项
3、创建监听套接字的事件处理器,主要用于处理监听套接字的读事件(出现读事件表示有新的连接到来)
4、绑定地址
5、开始监听

6、等待事件到来


写服务器应用程序必须要考虑到服务器资源不足的情况,其中常见的一个是打开的文件数量(文件描述符数量)不能超过系统限制,当接受的连接太多时就会到达系统的限制,即表示打开的套接字文件描述符太多,从而导致accpet失败,返回EMFILE错误,但此时连接已经在系统内核建立好了,所以占用了系统的资源,我们不能让接受不了的连接继续占用系统资源,如果不处理这种错误就会有越来越多的内核连接建立,系统资源被占用也会越来越多,直到系统崩溃。一个常见的处理方式就是,先打开一个文件,预留一个文件描述符,出现EMFILE错误的时候,把打开的文件关闭,此时就会空出一个可用的文件描述符,再次调用accept就会成功,接受到客户连接之后,我们马上把它关闭,这样这个连接在系统中占用的资源就会被释放。关闭之后又会有一个文件描述符空闲,我们再次打开一个文件,占用文件描述符,等待下一次的EMFILE错误。


/* * 接收者Accpetor */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, bool reuseport);~Acceptor();// 设置新链接到来的回调函数void setNewConnectionCallback(const NewConnectionCallback& cb){ newConnectionCallback_ = cb; }// 是否正在监听bool listenning() const { return listenning_; }// 监听void listen();private:// 处理读事件,acceptChannel_的读事件回调函数void handleRead();// 所属的ReactorEventLoop* loop_;// 监听/接收者套接字Socket acceptSocket_;// 监听套接字的事件处理器(主要处理读事件,即新链接到来的事件)Channel acceptChannel_;// 新链接到来的回调函数NewConnectionCallback newConnectionCallback_;// 是否正在监听bool listenning_;// 预留的空闲文件描述符,用于备用int idleFd_;};}}

/* * 构造函数,创建套接字,并设置套接字选项,然后绑定地址 * 设置事件处理的读事件回调函数 */Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport): 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_.setReusePort(reuseport);// 绑定地址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;// 接受一个链接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);}}}


0 0
原创粉丝点击