TcpServer
仔细想了想自己之前在写server端的时候,发现从listenfd的初始化直到收到新的连接之前,一直都是重复的工作。因此可以抽象出一个Acceptor出来。
没有Acceptor
先看没有Acceptor的情况。
我们在TcpServer中完成listenfd的初始化,一直到给Handler设置回调函数。
大致的伪代码如下
TcpServer{
int lfd = initListenFd();
lHandler = new lHandler(lfd);
lHandler->setReadCallback(bind(newConnection));
reactor->update(lHandler); // 完成将listenHandler注册到Reactor中去。
TcpServer::newConnection() // 当listenHandler就绪的时候,代表有连接到来。
{
int connfd = accept(...);
newConnection = new TcpConnection(connfd);
...
}
};
有Acceptor
- 完成listenfd的初始化。即完成listen状态。期间要注意设置成为非阻塞状态。
- 将listenfd包装成Handler,注册到Reactor中,二者是使用关系。Acceptor的构造函数中传递一个Reactor指针。
- 给Handler设置回调函数。这个回调函数的任务将会是处理新的connfd到达。我们要将其accept,然后抽象成TcpConnection。但这些任务不是我们要在Acceptor中处理的。那么就提供一个接口给TcpServer,允许TcpServer设置这样的回调函数到Acceptor中。然后在Acceptor::handleCallback中直接就是去调用TcpServer设置好的Callback。(如果没看过muduo库源码的可能不知道我在说什么,请耐心继续往下读)
大致的伪代码如下
Acceptor{
int lfd = initListenFd();
lHandler = new lHandler(lfd);
lHandler->setReadCallback(bind(handleCallback));
reactor->update(lHandler); // 完成将listenHandler注册到Reactor中去。
Acceptor::handleCallback()
{
int connfd = accept(...);
callback_(connfd);
}
Acceptor::setCallback(cb) // 这是提供给server去调用的
{
callback_ = cb;
}
};
剩余工作
剩余工作其实是非常简单的,就是一个start。实际上就是开启reactor->loop()。当前我们不去考虑程序结束时Reactor怎么退出。因为服务器程序安全退出基本是不太需要的。我目前见到过的都是while循环一直运行到死的。
TcpServer的成员
现在我们来思考下TcpServer的成员需要哪些?这是一个我认为非常好的习惯。因为之前都是按照C语言的方式去写,面向过程,很少去抽象一个东西出来。现在需要自己抽象一些成员,并且完成一些独立性的功能。
- mainReactor。这个Reactor主要负责的是IO处理。就是处理到达的新连接该怎么办。
- ReactorThreadPool。这个Pool里面全部都是subReactor。当我们收到一个新连接时,需要将新连接交给subReactor中去。即每个subReactor对应多个连接。
- TcpConnectionList。主要目的是用来帮助我们建立fd——TcpConnection对应关系。当客户端对开的时候。用来删除连接。这个目前先不考虑。到TcpConnection的时候讨论。
- 记录TcpServer的ip和port的一个地址类——InetAddress。这个没什么好讲的。