Socket
管理一个套接字(listen fd 或 connection fd),其生命周期和管理的套接字一样长。
class Socket : 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);
/// abort if address in use
void listen(); // 即设置这个套接字为 listen fd
/// 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); // 从该 listen fd 中获得一个 connection fd
void shutdownWrite();
void setTcpNoDelay(bool on);
void setReuseAddr(bool on);
void setReusePort(bool on);
void setKeepAlive(bool on);
private:
const int sockfd_;
};
析构的时候会::close
套接字
Socket::~Socket()
{
sockets::close(sockfd_);
}
void sockets::close(int sockfd)
{
if (::close(sockfd) < 0)
{
LOG_SYSERR << "sockets::close";
}
}
bindAddress & listen
::bind
void Socket::bindAddress(const InetAddress& addr)
{
sockets::bindOrDie(sockfd_, addr.getSockAddr());
}
void sockets::bindOrDie(int sockfd, const struct sockaddr* addr)
{
int ret = ::bind(sockfd, addr, static_cast<socklen_t>(sizeof(struct sockaddr_in6)));
if (ret < 0)
{
LOG_SYSFATAL << "sockets::bindOrDie";
}
}
::listen
void Socket::listen()
{
sockets::listenOrDie(sockfd_);
}
void sockets::listenOrDie(int sockfd)
{
int ret = ::listen(sockfd, SOMAXCONN);
if (ret < 0)
{
LOG_SYSFATAL << "sockets::listenOrDie";
}
}
accept
::accept4
int Socket::accept(InetAddress* peeraddr)
{
struct sockaddr_in6 addr;
memZero(&addr, sizeof addr);
int connfd = sockets::accept(sockfd_, &addr);
if (connfd >= 0)
{
peeraddr->setSockAddrInet6(addr);
}
return connfd;
}
int sockets::accept(int sockfd, struct sockaddr_in6* addr)
{
socklen_t addrlen = static_cast<socklen_t>(sizeof *addr);
int connfd = ::accept4(sockfd, sockaddr_cast(addr),
&addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
return connfd;
}
shutdownWrite
::shutdown
void Socket::shutdownWrite()
{
sockets::shutdownWrite(sockfd_);
}
void sockets::shutdownWrite(int sockfd)
{
if (::shutdown(sockfd, SHUT_WR) < 0)
{
LOG_SYSERR << "sockets::shutdownWrite";
}
}
Acceptor
Acceptor
用于 accept
新的 TCP 连接,当接受的新的连接之后,通过 NewConnectionCallback
回调通知调用者。它是内部 class 供 TcpServer
使用,生命周期由后者控制。
class Acceptor : noncopyable
{
public:
typedef std::function<void (int sockfd, const InetAddress&)> NewConnectionCallback;
Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport);
~Acceptor();
void setNewConnectionCallback(const NewConnectionCallback& cb)
{
newConnectionCallback_ = cb; }
void listen();
bool listening() const {
return listening_; }
private:
void handleRead();
EventLoop* loop_;
Socket acceptSocket_;
Channel acceptChannel_;
NewConnectionCallback newConnectionCallback_;
bool listening_;
int idleFd_;
};
Acceptor
构造函数接受一个 EventLoop*
,Channel acceptChannel_
就将跑在这个 loop 上
构造函数中会调用 sockets::createNonblockingOrDie
,来创建一个 非阻塞 的套接字(sockfd),用来初始化 Socket acceptSocket_
,然后调用 Socket::bindAddress
,来设置监听地址。
用刚刚创建的套接字构造 Channel acceptChannel_
,然后设置 ReadCallBack 为 Acceptor::handleRead
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
: loop_(loop),
acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
acceptChannel_(loop, acceptSocket_.fd()),
listening_(false),
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
assert(idleFd_ >= 0);
acceptSocket_.setReuseAddr(true);
acceptSocket_.setReusePort(reuseport);
acceptSocket_.bindAddress(listenAddr);
acceptChannel_.setReadCallback(
std::bind(&Acceptor::handleRead, this));
}
::socket
int sockets::createNonblockingOrDie(sa_family_t family)
{
int sockfd = ::socket(family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
if (sockfd < 0)
{
LOG_SYSFATAL << "sockets::createNonblockingOrDie";
}
return sockfd;
}
Acceptor::listen()
会调用 Socket::listen
开始监听套接字(即设置这个套接字为 listen fd),然后设置 acceptChannel_
关心可读事件,最终会注册在 Poller
中。这里直接调用 acceptChannel_.enableReading
,说明此时 loop_
还没有开始 loop。
void Acceptor::listen()
{
loop_->assertInLoopThread();
listening_ = true;
acceptSocket_.listen();
acceptChannel_.enableReading();
}
当套接字可读时会回调 Acceptor::handleRead
,其中会调用 ::accept4
,来生成一个非阻塞的 connection fd,并回调 newConnectionCallback_
若 ::accept4
时 发生文件符耗尽(EMFILE
),那么先 close
掉之前打开的 idleFd_
("/dev/null"
),这样就腾出来一个 fd,这时再 ::accept4
,然后再把刚 accpet 的 connection fd close
掉,最后再打开的 idleFd_
("/dev/null"
)。这样算是优雅的 close
掉了 handle 不了的 connection 了
void Acceptor::handleRead()
{
loop_->assertInLoopThread();
InetAddress peerAddr;
//FIXME loop until no more
int connfd = acceptSocket_.accept(&peerAddr);
if (connfd >= 0)
{
if (newConnectionCallback_)
{
newConnectionCallback_(connfd, peerAddr);
}
else
{
sockets::close(connfd);
}
}
else
{
if (errno == EMFILE)
{
::close(idleFd_);
idleFd_ = ::