muduo源码剖析之Acceptor监听类

简介

Acceptor类用于创建套接字,设置套接字选项,调用socket()->bind()->listen()->accept()函数,接受连接,然后调用TcpServer设置的connect事件的回调。

listen()//在TcpServer::start中调用

封装了一个listen fd相关的操作,用于mainLoop

成员及属性解析

Acceptor - 逻辑上的内部类

接受器封装,实质上就是对Channel的多一层封装

主要接口

listen

监听连接

当新连接进入时,调用Socket::accept创建套接字,触发TcpServer的回调

setNewConnectionCallback

TcpServer通过该接口设置回调,当新连接套接字创建后,创建TcpConnection对象

核心实现:

通过socket::accept接受新连接,获得套接字fd

这个fd作为参数调用TcpServer注册的回调

主要成员

  • loop
  • channel
  • idlefd
    非常巧妙的设计,在服务器压力过大,无法新建文件描述符时,通过这个idlefd拒绝连接
    来自libevent的设计

源码剖析

Acceptor.h

cpp 复制代码
#ifndef MUDUO_NET_ACCEPTOR_H
#define MUDUO_NET_ACCEPTOR_H

#include <functional>

#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 : noncopyable
{
 public:
  typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;

  Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);
  ~Acceptor();

  void setNewConnectionCallback(const NewConnectionCallback& cb)
  { newConnectionCallback_ = cb; }

  void listen();

  bool listening() const { return listening_; }

  // Deprecated, use the correct spelling one above.
  // Leave the wrong spelling here in case one needs to grep it for error messages.
  // bool listenning() const { return listening(); }

 private:
  void handleRead();

  EventLoop* loop_;
  Socket acceptSocket_;
  Channel acceptChannel_;
  NewConnectionCallback newConnectionCallback_;
  bool listening_;
  int idleFd_;
};

}  // namespace net
}  // namespace muduo

#endif  // MUDUO_NET_ACCEPTOR_H

Acceptor.cc

cpp 复制代码
// Copyright 2010, Shuo Chen.  All rights reserved.
// http://code.google.com/p/muduo/
//
// Use of this source code is governed by a BSD-style license
// that can be found in the License file.

// Author: Shuo Chen (chenshuo at chenshuo dot com)

#include "muduo/net/Acceptor.h"

#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/SocketsOps.h"

#include <errno.h>
#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
  : loop_(loop),
    acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),//创建一个非阻塞的socket fd
    acceptChannel_(loop, acceptSocket_.fd()),//创建socket fd的channel
    listening_(false),//是否处于监听状态
    idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
  assert(idleFd_ >= 0);
  acceptSocket_.setReuseAddr(true);			//设置ip地址复用
  acceptSocket_.setReusePort(reuseport);	//设置端口复用
  acceptSocket_.bindAddress(listenAddr);	//bind()函数封装,绑定ip和端口
  acceptChannel_.setReadCallback(
      std::bind(&Acceptor::handleRead, this));//设置accept的回调函数
}

Acceptor::~Acceptor()
{
  acceptChannel_.disableAll();//将mainloop poller监听集合中移除,取消所有事件的监听
  acceptChannel_.remove();//在events_删除channel
  ::close(idleFd_);//关闭文件
}

void Acceptor::listen()//开始监听fd
{
  loop_->assertInLoopThread();//判断是不是和创建时的io线程处于同一个线程
  listening_ = true;//是否监听
  acceptSocket_.listen();//真正的监听函数
  acceptChannel_.enableReading();//设置监听读事件
}

void Acceptor::handleRead()//当有client connnect时,则会调用
{
  loop_->assertInLoopThread();
  InetAddress peerAddr;
  //FIXME loop until no more
  int connfd = acceptSocket_.accept(&peerAddr);//接收client connect,返回值==accept()返回值
  if (connfd >= 0)
  {
    // string hostport = peerAddr.toIpPort();
    // LOG_TRACE << "Accepts of " << hostport;
    if (newConnectionCallback_)//如果设置了connect cb,则调用,否则则关闭这个连接
    {
      newConnectionCallback_(connfd, peerAddr);
    }
    else
    {
      sockets::close(connfd);
    }
  }
  else
  {
    LOG_SYSERR << "in Acceptor::handleRead";
    // Read the section named "The special problem of
    // accept()ing when you can't" in libev's doc.
    // By Marc Lehmann, author of libev.
    if (errno == EMFILE)
    {
      ::close(idleFd_);
      idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
      ::close(idleFd_);
      idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
    }
  }
}
相关推荐
热爱嵌入式的小许2 小时前
Linux基础项目开发1:量产工具——显示系统
linux·运维·服务器·韦东山量产工具
小堃学编程2 小时前
计算机网络(十) —— IP协议详解,理解运营商和全球网络
网络·tcp/ip·计算机网络
2401_857622663 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
代码雕刻家3 小时前
数据结构-3.9.栈在递归中的应用
c语言·数据结构·算法
2402_857589363 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没5 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
IPFoxy6665 小时前
探索路由器静态IP的获取方式
网络·智能路由器
Kalika0-05 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
代码雕刻家5 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构