封裝了字節序轉換函數(全局函數,位於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
封裝了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; }
用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
具體實現服務器
// 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 }
網際地址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
具體實現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用於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判斷,就會一直觸發 } }