muduo庫的socket操做封裝與Acceptor類剖析

 

Endian.h

封裝了字節序轉換函數(全局函數,位於muduo::net::sockets名稱空間中)linux

 

// 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

具體實現app

 

// 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鏈接。socket

Acceptor的數據成員包括Socket、Channel。tcp

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

Channel用於觀察此socket的readable事件,並Acceptor::handleRead(),後者調用accept(2)來接受鏈接,並回調用戶callback。oop

不過,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判斷,就會一直觸發
    }
}
相關文章
相關標籤/搜索