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判断,就会一直触发 } }