(本文地址:LYanger的博客:http://blog.csdn.net/freeelinux/article/details/53574574)
TcpServer是muduo库很重要的一个类,它结合TcpConnection、Acceptor构成了一套完整的对I/O触发事件的处理机制。那么它们具体是怎么工作的呢?
先来看一个例子:
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <boost/bind.hpp>
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
class TestServer {
public:
TestServer(EventLoop* loop, const InetAddress& listenAddr, int numThreads)
: loop_(loop),
server_(loop, listenAddr, "TestServer"),
numThreads_(numThreads){
server_.setConnectionCallback(boost::bind(&TestServer::onConnection, this, _1));
server_.setThreadNum(numThreads);
}
void start(){
server_.start();
}
private:
void onConnection(const TcpConnectionPtr& conn){
if(conn->connected()){
printf("onConnection(): new connection [%s] from %s \n",
conn->name().c_str(),
conn->peerAddress().toIpPort().c_str());
}
else{
printf("onConnection() : connection [%s] is down\n",
conn->name().c_str());
}
}
EventLoop* loop_;
TcpServer server_;
int numThreads_;
};
int main()
{
printf("main() : pid = %d\n", getpid());
InetAddress listenAddr(8888);
EventLoop loop;
TestServer server(&loop, listenAddr, 4);
server.start();
loop.loop();
}
输出:
先不分析结构,我们来分析一下过程:
首先主函数中声明了一个InetAddress对象,构造参数是服务端的端口号,我们来看一下InetAddress的构造函数:
//仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0)
explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false, bool ipv6 = false);
具体实现截取ipv4部分是这样的:
InetAddress::InetAddress(uint16_t port, bool loopbackOnly, bool ipv6)
{
if (ipv6)
{
...
}
else
{
bzero(&addr_, sizeof addr_);
addr_.sin_family = AF_INET;
in_addr_t ip = loopbackOnly ? kInaddrLoopback : kInaddrAny; //INADDR_ANY 或者 INADDY_LOOPBACK
addr_.sin_addr.s_addr = sockets::hostToNetwork32(ip);
addr_.sin_port = sockets::hostToNetwork16(port);
}
}
它起始就是我们编最简单socket程序时sockaddr_in结构体填充的一个封装。
然后主函数中声明了一个loop对象,EventLoop类的功能就不用说了,Reactor循环处理事件的一个封装。给出它的成员函数:
bool looping_; /* atomic */
bool quit_; /* atomic and shared between threads, okay on x86, I guess. */
bool eventHandling_; /* atomic */
bool callingPendingFunctors_; /* atomic */
int64_t iteration_;
const pid_t threadId_; //当前所属对象线程id
Timestamp pollReturnTime_; //时间戳,poll返回的时间戳
boost::scoped_ptr<Poller> poller_; //poller对象
boost::scoped_ptr<TimerQueue> timerQueue_;
int wakeupFd_; //用于eventfd
// unlike in TimerQueue, which is an internal class,
// we don't expose Channel to client.
boost::scoped_ptr<Channel> wakeupChannel_; //wakeupfd所对应的通道,该通道会纳入到poller来管理
boost::any context_;
// scratch variables
ChannelList activeChannels_; //Poller返回的活动通道,vector<channel*>类型
Channel* currentActiveChannel_; //当前正在处理的活动通道
MutexLock mutex_;
std::vector<Functor> pendingFunctors_; // @GuardedBy mutex_
我们来分析一下打开的描述符,接下来本文打开的描述符都用绿色标注。
1.首先poller_对象会打开文件描述符值为3的pollfd,一般是是epollfd。
2.打开timerfd,这和接下来的eventfd是muduo使用的较新的系统调用之一。
3.wakeupFd_,I/O线程自己和自己通信的eventfd。
4.server肯定会调用listen,所以将来肯定会打开Acceptor类的