功能 :这是一个接口类,拥有一个管理监听套接字的类accptor,拥有一张具有多个管理连接套接字的TcpConnection类的映射表。
它只是对这两个类进行管理,会设置它们的一些回调函数,监听端口,等,负责acceptor和TcpConnection两个类与用户交互的接口,而具体的调用实现还是由那两个具体的类去实现。比如连接到来时的调用函数,用户设置为onConnection,则接口TcpServer会将其设置为,TcpConnection类的connectioncallback函数,然后在建立函数的newconnection中调用。而将建立新的TcpConnection的TcpServer::NewConnection函数设置为acceptor的
类图:
先上一段测试代码
int main()
{
printf("main(): pid = %d\n", getpid());
muduo::InetAddress listenAddr(9981);
muduo::EventLoop loop;
muduo::TcpServer server(&loop, listenAddr);
server.setConnectionCallback(onConnection);
server.setMessageCallback(onMessage);
server.start();
loop.loop();
}
这段测试代码中,我们构造了一个TcpServer对象,然后调用了两个设置回调函数的成员函数,然后调用一个start函数。也就是说,我们使用TcpServer的时候,只要提供监听的端口号,注册连接时的回调函数,注册消息到来时的回调函数,即可。
我们为 InetAddress 提供了端口号,那么muduo为我们做了什么呢?
explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false, bool ipv6 = false);
InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6)
{
memZero(&addr_, sizeof addr_);
addr_.sin_family = AF_INET;
in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny;
addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);
addr_.sin_port = sockets::hostToNetwork16(port);
}
这里对构造函数做了些删减,只留下了比较主干的部分,
在 InetAddress 的构造函数中,会创建一个 sockaddr_in_ 结构体对象,
假如我们只提供端口号,那么muduo会默认为ipv4,然后默认监听本机所有ip,初始化addr_成员,在初始化的时候,还用了sockets的一些转换函数,这些工作由muduo为我们做,可以为我们节省大量的时间和重复的工作量。
这样我们就完成了要监听的ip地址和端口号。
接着关注,TcpServer对象,
在TcpServer的构造函数:
TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option)
: loop_(CHECK_NOTNULL(loop)),//检查不是空指针
ipPort_(listenAddr.toIpPort()),
name_(nameArg),
acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),
threadPool_(new EventLoopThreadPool(loop, name_)),
connectionCallback_(defaultConnectionCallback),
messageCallback_(defaultMessageCallback),
nextConnId_(1)
{
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, _1, _2));//把newconnection设为了acceptor的回调函数
}
可以看到,该TcpServer有着所属的eventloop,监听的端口号,自己的名字,这都是一对一的属性。
同时这里创建了一个accptor_对象,这个对象会为我们完成一部分重要而规范化的工作。
功能 :调用accept建立新的TCP连接。每个accptor负责一个监听套接字,一旦有连接请求到来,会调用accept函数建立一个TcpConnection类,管理连接套接字。
类图 :
Acceptor的构造函数,调用socket完成了套接字的创建,调用bind绑定了套接字与服务器addr_结构体,也就是在TcpServer中创建的对象。
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
: loop_(loop),
acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
acceptChannel_(loop, acceptSocket_.fd()),//初始化channel对应的文件描述符
listenning_(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));//设置channel的读事件回调函数
}
//创建socket的函数 SocketOps.cc
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;
}
// 绑定监听地址 在
acceptSocket_.bindAddress(listenAddr);
//调用listen,转为监听状态:在TcpServer::start()中调用
void Acceptor::listen()
{
loop_->assertInLoopThread();
listenning_ = true;
acceptSocket_.listen();
acceptChannel_.enableReading();
}
因为我们的eventloop机制是通过channel来管理事件的,一个channel对应一个fd,这里的监听socketfd会对应一个channel,这里把该channel的读处理事件回调函数绑定为Acceptor::handleRead。也就是监听套接字可读,那就是发生了TCP连接事件,因此,我们应该想到这个时候是该调用accept来处理连接事件了。
Acceptor::handleRead就是通过调用accept(2)来建立了新的TCP连接
//channel读事件发生时会调用这个函数
void Acceptor::handleRead()
{
loop_->assertInLoopThread();
InetAddress peerAddr;
//FIXME loop until no more
int connfd = acceptSocket_.accept(&peerAddr);//调用accept建立连接,得到客户端fd
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);
}
}
}
建立连接以后,调用 newConnectionCallback_ 这个函数在TcpServer的构造函数中,被传入的是TcpServer::newConnection也就是后续创建Tcpconnection 的函数
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, _1, _2));
现在用accept得到了连接套接字,连接套接字得到以后,我们就需要建立一个TcpConnection来维持两者的通信。
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
EventLoop* ioLoop = threadPool_->getNextLoop();
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd));
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
//创建了一个Tcpconnection对象
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
先看一下构造函数代码:
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting),
reading_(true),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),
peerAddr_(peerAddr),
highWaterMark_(64*1024*1024)
{
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, _1));
channel_->setWriteCallback(
std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(
std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this));
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this
<< " fd=" << sockfd;
socket_->setKeepAlive(true);
}
构造函数:
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
创建了一个socket作为连接套接字,创建一个channel负责该连接套接字
然后设置一些channel对应的处理回调函数
先分析两个相对重要的回调函数
第一个是处理读取的函数
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno); //调用inputBuffer_的read函数
if (n > 0)
{
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);//调用我们注册的消息到来时的函数
}
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
这个回调函数主要是两件事:调用InputBuffer的读函数,调用我们注册的消息到来时的函数onMessage
处理回调写函数
handlewrite
void TcpConnection::handleWrite()
{
loop_->assertInLoopThread();
if (channel_->isWriting())
{
ssize_t n = sockets::write(channel_->fd(),
outputBuffer_.peek(),
outputBuffer_.readableBytes());
if (n > 0)
{
outputBuffer_.retrieve(n);
if (outputBuffer_.readableBytes() == 0)
{
channel_->disableWriting();
if (writeCompleteCallback_)
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
if (state_ == kDisconnecting)
{
shutdownInLoop();
}
}
}