muduo源码分析之Acceptor
Posted 零十
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了muduo源码分析之Acceptor相关的知识,希望对你有一定的参考价值。
相关文件
muduo/net/Acceptor.h
muduo/net/Acceptor.cc
//用RAII方法封装socket file descriptor
muduo/net/Socket.h
muduo/net/Socket.cc
//封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)
muduo/net/SocketsOps.h
muduo/net/SocketsOps.cc
//封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)
muduo/net/Endian.h
作用
Acceptor用于accept接收TCP连接。
Acceptor的数据成员包括Socket、Channel,Acceptor的Socket是listening socket(即server socket)。
Channel 用于观察此Socket的readable事件,并回调Acceptor::handleRead(),handleRead()调用accept()来接收新连接,,并回调用户callback函数。
使用
Acceptor是内部类,共TcpServer使用。
下面是一个使用示例
如果有client连接,回调函数向client发送“how are you?”字符串。
#include <muduo/net/Acceptor.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
void newConnection(int sockfd, const InetAddress& peerAddr)
{
printf("newConnection(): accepted a new connection from %s\\n",
peerAddr.toIpPort().c_str());
::write(sockfd, "How are you?\\n", 13);
sockets::close(sockfd);
}
int main()
{
printf("main(): pid = %d\\n", getpid());
InetAddress listenAddr(8888);//端口8888
EventLoop loop;
Acceptor acceptor(&loop, listenAddr);
acceptor.setNewConnectionCallback(newConnection);//设置回调函数
acceptor.listen();
loop.loop();
}
Acceptor源码分析
Acceptor类
/// Acceptor of incoming TCP connections.
///
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_; }
// Deprecated, use the correct spelling one above.
// Leave the wrong spelling here in case one needs to grep it for error messages.
// bool listenning() const { return listening(); }
private:
void handleRead(); //通道Channel的回调函数
EventLoop* loop_; //所在EventLoop
Socket acceptSocket_; //socket类
Channel acceptChannel_; //通道
NewConnectionCallback newConnectionCallback_; //在handleRead()中调用
bool listening_; //是否在listen
int idleFd_;
};
构造函数
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)); //通道回调函数
}
通道回调函数
void Acceptor::handleRead()
{
loop_->assertInLoopThread();
InetAddress peerAddr;
//FIXME loop until no more
int connfd = acceptSocket_.accept(&peerAddr); //accept 新连接
if (connfd >= 0)
{
// string hostport = peerAddr.toIpPort();
// LOG_TRACE << "Accepts of " << hostport;
if (newConnectionCallback_)
{
newConnectionCallback_(connfd, peerAddr);//调用用户回调函数
}
else
{
sockets::close(connfd);
}
}
else
{
LOG_SYSERR << "in Acceptor::handleRead";
// Read the section named "The special problem of
// accept()ing when you can\'t" in libev\'s doc.
// By Marc Lehmann, author of libev.
if (errno == EMFILE)
{
::close(idleFd_);
idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
::close(idleFd_);
idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
}
}
}
监听
//Accepter.cc
void Acceptor::listen()
{
loop_->assertInLoopThread();
listening_ = true;
acceptSocket_.listen();
acceptChannel_.enableReading(); //注册到poller监听事件
}
//Socket.cc
void Socket::listen()
{
sockets::listenOrDie(sockfd_);
}
//SocketsOps.cc
void sockets::listenOrDie(int sockfd)
{
int ret = ::listen(sockfd, SOMAXCONN);
if (ret < 0)
{
LOG_SYSFATAL << "sockets::listenOrDie";
}
}
以上是关于muduo源码分析之Acceptor的主要内容,如果未能解决你的问题,请参考以下文章
(10)muduo_base库源码分析:Timestamp.cc和Timestamp.h
Muduo网络库源码分析 EventLoop事件循环(Poller和Channel)
muduo源代码分析--Reactor模式在muduo中的使用