muduo库的socket操作封装与Acceptor类剖析

 

Endian.h

封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)

// 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)
//
// This is a public header file, it must only include public header files.
/*字节序转换函数封装*/
#ifndef MUDUO_NET_ENDIAN_H
#define MUDUO_NET_ENDIAN_H

#include <stdint.h>
#include <endian.h>

namespace muduo {
    namespace net {
        namespace sockets {

// the inline assembler code makes type blur,
// so we disable warnings for a while.
#if __GNUC_MINOR__ >= 6
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wold-style-cast"

            /*32位用来转换ip地址,16位用来转换端口号*/
            inline uint64_t hostToNetwork64(uint64_t host64) {
                return htobe64(host64);//64位的主机字节向大端字节转换
            }

            inline uint32_t hostToNetwork32(uint32_t host32) {
                return htobe32(host32);//32位的主机字节向大端字节转换
            }

            inline uint16_t hostToNetwork16(uint16_t host16) {
                return htobe16(host16);//16位的主机字节向大端字节转换
            }

            inline uint64_t networkToHost64(uint64_t net64) {
                return be64toh(net64);//64位的大端字节向主机字节转换
            }

            inline uint32_t networkToHost32(uint32_t net32) {
                return be32toh(net32);//32位的大端字节向主机字节转换
            }

            inline uint16_t networkToHost16(uint16_t net16) {
                return be16toh(net16);//16位的大端字节向主机字节转换
            }

#if __GNUC_MINOR__ >= 6
#pragma GCC diagnostic pop
#else
#pragma GCC diagnostic error "-Wconversion"
#pragma GCC diagnostic error "-Wold-style-cast"
#endif


        }
    }
}

#endif  // MUDUO_NET_ENDIAN_H

SocketsOps.h/ SocketsOps.cc

封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)。

// 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/SocketsOps.h>

#include <muduo/base/Logging.h>
#include <muduo/base/Types.h>
#include <muduo/net/Endian.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>  // snprintf
#include <strings.h>  // bzero
#include <sys/socket.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

namespace
{

typedef struct sockaddr SA;

const SA* sockaddr_cast(const struct sockaddr_in* addr)//将const sockaddr_in*转换成const sockaddr*
{
  return static_cast<const SA*>(implicit_cast<const void*>(addr));//implicit_cast是自定义的转换符,派生类转换成基类
}

SA* sockaddr_cast(struct sockaddr_in* addr)//将sockaddr_in*转换成sockaddr*
{
  return static_cast<SA*>(implicit_cast<void*>(addr));
}

void setNonBlockAndCloseOnExec(int sockfd)//设置非阻塞和close-on-exec形式的文件描述符,就是在执行execve()函数时,该文件描述符会被关闭
{
  // non-block
  int flags = ::fcntl(sockfd, F_GETFL, 0);
  flags |= O_NONBLOCK;
  int ret = ::fcntl(sockfd, F_SETFL, flags);
  // FIXME check

  // close-on-exec
  flags = ::fcntl(sockfd, F_GETFD, 0);
  flags |= FD_CLOEXEC;
  ret = ::fcntl(sockfd, F_SETFD, flags);
  // FIXME check

  (void)ret;
}

}

int sockets::createNonblockingOrDie()//创建一个套接字,socket函数封装
{
  // socket
#if VALGRIND
  int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sockfd < 0)
  {
    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
  }

  setNonBlockAndCloseOnExec(sockfd);
#else
  // Linux 2.6.27以上的直接在内核支持SOCK_NONBLOCK与SOCK_CLOEXEC
  int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
  //创建一个支持IPv4,并且支持http协议的,非阻塞的,close-on-exec形式的套接字,TCP传输形式的套接字
  if (sockfd < 0)
  {
    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
  }
#endif
  return sockfd;
}

void sockets::bindOrDie(int sockfd, const struct sockaddr_in& addr)//bind函数封装
{
  int ret = ::bind(sockfd, sockaddr_cast(&addr), sizeof addr);
  if (ret < 0)
  {
    LOG_SYSFATAL << "sockets::bindOrDie";
  }
}

void sockets::listenOrDie(int sockfd)//listen函数封装
{
  int ret = ::listen(sockfd, SOMAXCONN);
  if (ret < 0)
  {
    LOG_SYSFATAL << "sockets::listenOrDie";
  }
}

int sockets::accept(int sockfd, struct sockaddr_in* addr)//accept函数封装,返回连接的描述符,以及客户端的sockaddr_in
{
  socklen_t addrlen = sizeof *addr;
#if VALGRIND
  int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);
  setNonBlockAndCloseOnExec(connfd);
#else
  // Linux 2.6.27以上使用这个,设置成
  int connfd = ::accept4(sockfd, sockaddr_cast(addr),
                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endif
  if (connfd < 0)//错误原因分析
  {
    int savedErrno = errno;
    LOG_SYSERR << "Socket::accept";
    switch (savedErrno)
    {
      case EAGAIN:
      case ECONNABORTED:
      case EINTR:
      case EPROTO: // ???
      case EPERM:
      case EMFILE: // per-process lmit of open file desctiptor ???
        // expected errors
        errno = savedErrno;//上述错误不致命,保存起来即可
        break;
      case EBADF:
      case EFAULT:
      case EINVAL:
      case ENFILE:
      case ENOBUFS:
      case ENOMEM:
      case ENOTSOCK:
      case EOPNOTSUPP:
        // unexpected errors//致命错误,直接FATAL
        LOG_FATAL << "unexpected error of ::accept " << savedErrno;
        break;
      default://不知名错误也FATAL
        LOG_FATAL << "unknown error of ::accept " << savedErrno;
        break;
    }
  }
  return connfd;
}

int sockets::connect(int sockfd, const struct sockaddr_in& addr)//封装connect
{
  return ::connect(sockfd, sockaddr_cast(&addr), sizeof addr);
}

ssize_t sockets::read(int sockfd, void *buf, size_t count)//封装read函数
{
  return ::read(sockfd, buf, count);
}

// readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
// 这里的iov是一个struct iovec的数组指针,可以将一系列分散的缓冲区中的值只通过一次系统调用全部读出来,如果要用read,就需要很多次
ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)//封装readv函数
{
  return ::readv(sockfd, iov, iovcnt);
}

ssize_t sockets::write(int sockfd, const void *buf, size_t count)//封装write函数
{
  return ::write(sockfd, buf, count);
}

void sockets::close(int sockfd)//封装close函数
{
  if (::close(sockfd) < 0)
  {
    LOG_SYSERR << "sockets::close";
  }
}

// 只关闭写端,还可以继续接受数据
void sockets::shutdownWrite(int sockfd)
{
  if (::shutdown(sockfd, SHUT_WR) < 0)
  {
    LOG_SYSERR << "sockets::shutdownWrite";
  }
}

void sockets::toIpPort(char* buf, size_t size,
                       const struct sockaddr_in& addr)//根据sockaddr_in结构体得到IP:port的字符串并返回
{
  char host[INET_ADDRSTRLEN] = "INVALID";
  toIp(host, sizeof host, addr);
  uint16_t port = sockets::networkToHost16(addr.sin_port);
  snprintf(buf, size, "%s:%u", host, port);
}

void sockets::toIp(char* buf, size_t size,
                   const struct sockaddr_in& addr)//将32位整型转换为16位的点分十进制
{
  assert(size >= INET_ADDRSTRLEN);
  ::inet_ntop(AF_INET, &addr.sin_addr, buf, static_cast<socklen_t>(size));
}

void sockets::fromIpPort(const char* ip, uint16_t port,//根据ip和端口填充sockaddr_in结构体
                           struct sockaddr_in* addr)
{
  addr->sin_family = AF_INET;
  addr->sin_port = hostToNetwork16(port);
  if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)//将ip的点分十进制转换成为32位整型
  {
    LOG_SYSERR << "sockets::fromIpPort";
  }
}

int sockets::getSocketError(int sockfd)//获取套接字的错误状态并清除
{
  int optval;
  socklen_t optlen = sizeof optval;

  if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)//获取套接字的错误状态并清除
  {
    return errno;
  }
  else
  {
    return optval;
  }
}

struct sockaddr_in sockets::getLocalAddr(int sockfd)//获取已经被绑定的sockfd的sockaddr_in结构体
{
  struct sockaddr_in localaddr;
  bzero(&localaddr, sizeof localaddr);
  socklen_t addrlen = sizeof(localaddr);
  if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)
  {
    LOG_SYSERR << "sockets::getLocalAddr";
  }
  return localaddr;
}

struct sockaddr_in sockets::getPeerAddr(int sockfd)//在accept以后,获取对端的sockaddr_in结构体
{
  struct sockaddr_in peeraddr;
  bzero(&peeraddr, sizeof peeraddr);
  socklen_t addrlen = sizeof(peeraddr);
  if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)
  {
    LOG_SYSERR << "sockets::getPeerAddr";
  }
  return peeraddr;
}

// 自连接是指(sourceIP, sourcePort) = (destIP, destPort)
// 自连接发生的原因:
// 客户端在发起connect的时候,没有bind(2)
// 客户端与服务器端在同一台机器,即sourceIP = destIP,
// 服务器尚未开启,即服务器还没有在destPort端口上处于监听
// 就有可能出现自连接,这样,服务器也无法启动了

bool sockets::isSelfConnect(int sockfd)
{
  struct sockaddr_in localaddr = getLocalAddr(sockfd);
  struct sockaddr_in peeraddr = getPeerAddr(sockfd);
  return localaddr.sin_port == peeraddr.sin_port
      && localaddr.sin_addr.s_addr == peeraddr.sin_addr.s_addr;
}

Socket.h

用RAII方法封装socket file descriptor,包含操作:listen、bind、accept这些操作将调用上述封装的内容。

// 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)
//
// This is an internal header file, you should not include this.
/*封装了一个类来操作bind函数*/
#ifndef MUDUO_NET_SOCKET_H
#define MUDUO_NET_SOCKET_H

#include <boost/noncopyable.hpp>

namespace muduo {
///
/// TCP networking.
///
    namespace net {

        class InetAddress;

///
/// Wrapper of socket file descriptor.
///
/// It closes the sockfd when desctructs.
/// It's thread safe, all operations are delagated to OS.
        class Socket : boost::noncopyable {
        public:
            explicit Socket(int sockfd)//初始化套接字
                    : sockfd_(sockfd) {}

            // Socket(Socket&&) // move constructor in C++11
            ~Socket();//关闭套接字

            int fd() const { return sockfd_; }//返回套接字

            /// abort if address in use
            void bindAddress(const InetAddress &localaddr);//bind函数
            /// abort if address in use
            void listen();//listen函数

            /// On success, returns a non-negative integer that is
            /// a descriptor for the accepted socket, which has been
            /// set to non-blocking and close-on-exec. *peeraddr is assigned.
            /// On error, -1 is returned, and *peeraddr is untouched.
            int accept(InetAddress *peeraddr);//accept函数,得到对端的InetAddress类

            void shutdownWrite();//关闭写端

            ///
            /// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm).
            ///
            // Nagle算法可以一定程度上避免网络拥塞
            // Nagle算法就是积攒一定数量的数据以后,再一起发出去,这样势必导致,如果只有一小块数据块,就不能直接发送,需要等待攒到足够数据块
            // TCP_NODELAY选项可以禁用Nagle算法
            // 禁用Nagle算法,可以避免连续发包出现延迟,这对于编写低延迟的网络服务很重要
            void setTcpNoDelay(bool on);

            ///
            /// Enable/disable SO_REUSEADDR
            ///
            /// 允许重用本地地址
            void setReuseAddr(bool on);

            ///
            /// Enable/disable SO_KEEPALIVE
            ///
            // TCP keepalive是指定期探测连接是否存在,如果应用层有心跳的话,这个选项不是必需要设置的
            void setKeepAlive(bool on);

        private:
            const int sockfd_;//const成员变量只可以在初始化列表中初始化
        };

    }
}
#endif  // MUDUO_NET_SOCKET_H

Socket.cc

具体实现

// 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/Socket.h>

#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>

#include <netinet/in.h>
#include <netinet/tcp.h>
#include <strings.h>  // bzero

using namespace muduo;
using namespace muduo::net;

Socket::~Socket() {
    sockets::close(sockfd_);
}

void Socket::bindAddress(const InetAddress &addr) {
    sockets::bindOrDie(sockfd_, addr.getSockAddrInet());
}

void Socket::listen() {
    sockets::listenOrDie(sockfd_);
}

int Socket::accept(InetAddress *peeraddr) {
    struct sockaddr_in addr;
    bzero(&addr, sizeof addr);
    int connfd = sockets::accept(sockfd_, &addr);
    if (connfd >= 0) {
        peeraddr->setSockAddrInet(addr);
    }
    return connfd;
}

void Socket::shutdownWrite() {
    sockets::shutdownWrite(sockfd_);
}

void Socket::setTcpNoDelay(bool on) {
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY,
                 &optval, sizeof optval);
    // FIXME CHECK
}

void Socket::setReuseAddr(bool on) {
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,
                 &optval, sizeof optval);
    // FIXME CHECK
}

void Socket::setKeepAlive(bool on) {
    int optval = on ? 1 : 0;
    ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,
                 &optval, sizeof optval);
    // FIXME CHECK
}

InetAddress.h

网际地址sockaddr_in封装

// 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)
//
// This is a public header file, it must only include public header files.
/*封装了一个类来初始化sockaddr_in结构体,以及由sockaddr_in结构体得到IP地址和端口的函数*/
#ifndef MUDUO_NET_INETADDRESS_H
#define MUDUO_NET_INETADDRESS_H

#include <muduo/base/copyable.h>
#include <muduo/base/StringPiece.h>

#include <netinet/in.h>

namespace muduo
{
namespace net
{

///
/// Wrapper of sockaddr_in.
///
/// This is an POD interface class.
class InetAddress : public muduo::copyable
{
 public:
  /*三个构造函数都是初始化sockaddr_in结构体的
   *第一个构造函数只要端口号
   *第二个构造函数需要ip和端口号
   *第三个构造函数是将一个初始化好的sockaddr_in结构体输入进去,所以第三个构造函数不需要做任何操作
   **/
  /// Constructs an endpoint with given port number.
  /// Mostly used in TcpServer listening.
  // 仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0)
  explicit InetAddress(uint16_t port);

  /// Constructs an endpoint with given ip and port.
  /// @c ip should be "1.2.3.4"
  InetAddress(const StringPiece& ip, uint16_t port);

  /// Constructs an endpoint with given struct @c sockaddr_in
  /// Mostly used when accepting new connections
  InetAddress(const struct sockaddr_in& addr)
    : addr_(addr)
  { }

  string toIp() const;
  string toIpPort() const;

  // __attribute__ ((deprecated)) 表示该函数是过时的,被淘汰的
  // 这样使用该函数,在编译的时候,会发出警告
  string toHostPort() const __attribute__ ((deprecated))
  { return toIpPort(); }

  // default copy/assignment are Okay

  const struct sockaddr_in& getSockAddrInet() const { return addr_; }//返回sockaddr_in结构体
  void setSockAddrInet(const struct sockaddr_in& addr) { addr_ = addr; }//设置sockaddr_in结构体

  uint32_t ipNetEndian() const { return addr_.sin_addr.s_addr; }//返回32位整型的IP地址
  uint16_t portNetEndian() const { return addr_.sin_port; }//返回端口量

 private:
  struct sockaddr_in addr_;
};

}
}

#endif  // MUDUO_NET_INETADDRESS_H

InetAddress.cc

具体实现

// 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/InetAddress.h>

#include <muduo/net/Endian.h>
#include <muduo/net/SocketsOps.h>

#include <strings.h>  // bzero
#include <netinet/in.h>

#include <boost/static_assert.hpp>

// INADDR_ANY use (type)value casting.
#pragma GCC diagnostic ignored "-Wold-style-cast"
static const in_addr_t kInaddrAny = INADDR_ANY;
#pragma GCC diagnostic error "-Wold-style-cast"

//     /* Structure describing an Internet socket address.  */
//     struct sockaddr_in {
//         sa_family_t    sin_family; /* address family: AF_INET */
//         uint16_t       sin_port;   /* port in network byte order */
//         struct in_addr sin_addr;   /* internet address */
//     };

//     /* Internet address. */
//     typedef uint32_t in_addr_t;
//     struct in_addr {
//         in_addr_t       s_addr;     /* address in network byte order */
//     };

using namespace muduo;
using namespace muduo::net;

BOOST_STATIC_ASSERT(sizeof(InetAddress) == sizeof(struct sockaddr_in));
//判断这两个结构体是否大小一样,按照man文档的说明应该是一样的

InetAddress::InetAddress(uint16_t port)
{
  bzero(&addr_, sizeof addr_);//void bzero(void *s, int n)就是把s指针指向的前n个字节置零
  addr_.sin_family = AF_INET;//设置IPv4协议
  addr_.sin_addr.s_addr = sockets::hostToNetwork32(kInaddrAny);//设置为所有主机IP都可以被连接,也就是0.0.0.0
  addr_.sin_port = sockets::hostToNetwork16(port);//将端口转换成网络字节序
}

InetAddress::InetAddress(const StringPiece& ip, uint16_t port)
{
  bzero(&addr_, sizeof addr_);
  sockets::fromIpPort(ip.data(), port, &addr_);//根据ip和端口填充sockaddr_in结构体
}

string InetAddress::toIpPort() const//由成员变量sockaddr_in addr_得到"ip:端口"字符串
{
  char buf[32];
  sockets::toIpPort(buf, sizeof buf, addr_);
  return buf;
}

string InetAddress::toIp() const//由成员变量sockaddr_in addr_只得到IP地址
{
  char buf[32];
  sockets::toIp(buf, sizeof buf, addr_);
  return buf;
}

Acceptor

Acceptor用于accept(2)接受TCP连接。

Acceptor的数据成员包括Socket、Channel。

Acceptor的socket是listening socket(即server socket)。

Channel用于观察此socket的readable事件,并Acceptor::handleRead(),后者调用accept(2)来接受连接,并回调用户callback。

不过,Acceptor类在上层应用程序中我们不直接使用,而是把它封装作为TcpServer的成员。

Acceptor.h源码分析(注释很详细)

// 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)
//
// This is an internal header file, you should not include this.
/*就是用一个Acceptor类专门用一个channel来创建套接字,绑定,监听等操作*/
#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();
            //newConnectionCallback_是在Acceptor::handleRead里面执行的,也就是在acceptChannel_的读事件发生的时候会被调用
            void setNewConnectionCallback(const NewConnectionCallback &cb) { newConnectionCallback_ = cb; }

            bool listenning() const { return listenning_; }

            void listen();

        private:
            void handleRead();//可读回调函数,绑定在acceptChannel_的读函数上

            EventLoop *loop_;//所属的EventLoop对象
            Socket acceptSocket_;//监听套接字
            Channel acceptChannel_;//和监听套接字绑定的通道 acceptChannel_和监听套接字acceptSocket_绑定
            NewConnectionCallback newConnectionCallback_;//一旦有新连接发生,执行的回调函数
            bool listenning_;//acceptChannel所处的eventloop是否处于监听状态
            int idleFd_;//用来解决文件描述符过多,引起电平触发不断触发的问题,详见handleRead函数的最后
        };

    }
}

#endif  // MUDUO_NET_ACCEPTOR_H

Acceptor.cc源码分析(注释很详细)

// 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/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))//这个描述符打开一个linux系统的空文件,所有写入的内容都会被丢弃
{
    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();//将socket套接字挂到eventloop的epoll上,并开启读监听
}

void Acceptor::handleRead()//读的回调函数,一旦socket套接字监听到连接,epoll就会立刻调用回调函数
{
    loop_->assertInLoopThread();
    InetAddress peerAddr(0);//对端的
    //FIXME loop until no more
    int connfd = acceptSocket_.accept(&peerAddr);
    if (connfd >= 0) {
        // string hostport = peerAddr.toIpPort();
        // LOG_TRACE << "Accepts of " << hostport;
        if (newConnectionCallback_) {
            newConnectionCallback_(connfd, peerAddr);
        } else {
            sockets::close(connfd);
        }
    } else {
        // Read the section named "The special problem of
        // accept()ing when you can't" in libev's doc.
        // By Marc Lehmann, author of livev.
        //在监听套接字可读事件触发时,我们会调用accept接受连接。如果此时注册过回调函数,就执行它。如果没有就直接关闭!
        //另一方面,如果已用文件描述符过多,accept会返回-1,我们构造函数中注册的idleFd_就派上用场了。
        // 当前文件描述符过多,无法接收新的连接。但是由于我们采用LT模式,如果无法接收,可读事件会一直触发。
        // 那么在这个地方的处理机制就是,关掉之前创建的空心啊idleFd_,然后去accept让这个事件不会一直触发,
        // 然后再关掉该文件描述符,重新将它设置为空文件描述符。
        //这种机制可以让网络库在处理连接过多,文件描述符不够用时,不至于因为LT模式一直触发而产生坏的影响。
        if (errno == EMFILE)//当accept函数出错时,是因为文件描述符太多了
        {
            ::close(idleFd_);//就关闭一个空闲描述符,相当于现在就有一个空的文件描述符位置了
            idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);//然后把刚才没有接受的描述符接受进来
            ::close(idleFd_);//把这个描述符给关闭,相当于忽略这个请求连接了
            idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);//重新开启这个空闲描述符
        }//之所以这样,是因为poll使用的是水平触发,如果没有这个if判断,就会一直触发
    }
}
原文地址:https://www.cnblogs.com/qldabiaoge/p/12701796.html