zoukankan      html  css  js  c++  java
  • 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判断,就会一直触发
        }
    }
  • 相关阅读:
    ubuntu16.04系统安装
    SQL注入之Sqli-labs系列第二十六关(过滤空格、注释符、逻辑运算符注入)和第二十六A
    提权心法(2)提权基本流程
    布尔盲注
    提权心法(1)信息搜集很重要
    POST注入-双注入
    POST型注入-报错注入
    字符型注入
    Web中间件常见安全漏洞总结
    SSRF 从入门到批量找漏洞
  • 原文地址:https://www.cnblogs.com/qldabiaoge/p/12701796.html
Copyright © 2011-2022 走看看