引言
muduo网络库的文章到这里就结束,本篇文章给muduo网络库收一个尾,这个TcpServer也就是我们对外给用户提供的接口。
TcpServer
TcpServer是对外提供的接口,控制着整个muduo网络库,所以这个类必须要有一个开启的入口,设置subLoop的个数,然后把其他的接口都隐藏在私有类,比如对于连接的新增,移除,还有对连接的存储通过map,并且我们的TcpServer所属的线程就是baseLoop,也就是说如果没有subLoop,那么就只有这个线程运行所有的channel。然后还有一些回调的函数,这个回调的函数并不是交给channel执行的,而是放在eventLoop的queue里面,也就是doPendingFunctors()里面执行。
cpp
class TcpServer : noncopyable{
public:
using ThreadInitCallback = std::function<void(EventLoop*)>; // 对应EventThreadLoop
enum Option {
KNoReusePort,
KReusePort,
};
TcpServer(EventLoop* loop, const InetAddress& listenAddr, const std::string& nameArg, Option option = KNoReusePort);
~TcpServer();
void setThreadInitCallback(const ThreadInitCallback& cb) { m_threadInitCallback = cb; }
void setConnectionCallback(const ConnectionCallback& cb) { m_connectionCallback = cb; }
void setMessageCallback(const MessageCallback& cb) { m_messageCallback = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback& cb) { m_writeCompleteCallback = cb; }
// 设置底层subLoop的个数
void setThreadNum(int numThreads);
// 开启服务的监听
void start();
private:
void newConnection(int sockfd, const InetAddress& peerAddr);
void removeConnection(const TcpConnectionPtr& conn);
void removeConnectionInLoop(const TcpConnectionPtr& conn);
using connectionMap = std::unordered_map<std::string, TcpConnectionPtr>;
EventLoop* m_loop; // baseLoop 用户定义的loop
const std::string m_ipPort; // 服务器相关的ip地址和端口号
const std::string m_name;
std::unique_ptr<Acceptor> m_acceptor; // 运行在mainLoop 监听新连接的事件
std::shared_ptr<EventLoopThreadPool> m_threadPool;
ConnectionCallback m_connectionCallback; // 有新连接时的回调
MessageCallback m_messageCallback; // 有读写消息时的回调
WriteCompleteCallback m_writeCompleteCallback; // 消息发送 完成之后的回调
ThreadInitCallback m_threadInitCallback; // loop线程初始化的回调
std::atomic<int> m_started;
int m_nextConnId;
connectionMap m_connectionMap;
};
}
对于TcpServer的构造函数,里面要设置baseLoop的绑定,本服务器的地址端口,acceptor的创造,线程池的创建,最后给acceptor设置了一个对于新连接的处理函数,也就是接受加分配给subLoop。我们再来回顾一下,设置了这个函数之后,其实就是给acceptor里面的那个channel的readbackcall回调函数设置了操作,其实目的就是接受到有新的fd就接触阻塞,然后执行回调。
然后对于析构函数来说,我们需要学习一个点,我们要删除指针,但是又要利用这个指针调用其回调函数,所以我们搞了一个展示的指针,正好一出这个作用域这个指针也消除了。
cpp
TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr, const std::string& nameArg, Option option)
: m_loop(CheckLoopNotNull(loop))
, m_ipPort(listenAddr.toIpPort())
, m_name(nameArg)
, m_acceptor(new Acceptor(loop, listenAddr, option == KReusePort))
, m_threadPool(std::make_shared<EventLoopThreadPool>(loop, m_name))
, m_connectionCallback()
, m_messageCallback()
, m_nextConnId(1)
, m_started(0)
{
m_acceptor->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}
TcpServer::~TcpServer()
{
for (auto& item : m_connectionMap) {
TcpConnectionPtr conn(item.second); // 建立一个临时的指针
item.second.reset();
// 销毁连接
conn->getLoop()->runInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
}
这个就是两个对外开放的接口,目的是开启线程池加mainLoop的循环,也就是开启了监听。
当然还有一些设置的接口,我们在之前的代码里面可能已经遇到了很多回调函数,我们用if判断的,这个是因为是由用户设置的,很可能如果用户没有设置,那么就没有那些回调函数。
cpp
void TcpServer::setThreadNum(int numThreads)
{
m_threadPool->setThreadNum(numThreads);
}
// 开启服务器监听 loop.loop()
void TcpServer::start()
{
if (m_started++ == 0) { // 防止一个TcpServer对象被start多次
m_threadPool->start(m_threadInitCallback);
m_loop->runInLoop(std::bind(&Acceptor::listen, m_acceptor.get())); // 开启监听
}
}
对于一个新连接的到来,我们需要用轮询算法分配一个subLoop,然后通过getsockname来获得服务器的地址,之所以我们可以获得,因为我们传入的是acceptor的sockfd(也就是新创建的fd),其实就是本机的,然后客户端的地址已经被传入进去了,所以这个样子就可以形成一个新连接,有了这个连接之后先存储这个连接,然后对这个连接设置多种回调函数,注意:这个messagecallback是用户来设置的,我们一般把这个函数叫做onMessage。最后我们绑定了关闭连接的回调函数,这个我们马上来说。最后把建立这个连接的函数绑定并放入ioLoop(也就是对应的loop)里面,这个建立连接的函数就是要把channel绑定给对应的loop,并开启可读的事件。
cpp
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
EventLoop* ioLoop = m_threadPool->getNextLoop();
char buf[64] = {0};
snprintf(buf, sizeof(buf), "-%s#%d", m_ipPort.c_str(), m_nextConnId);
m_nextConnId++;
std::string connName = m_name + buf;
LOG_INFO("TcpServer::Connection [%s] - new connection [%s] from %s \n", m_name.c_str(),
connName.c_str(), peerAddr.toIpPort().c_str());
// 通过sockfd获取其绑定的本机的ip地址和端口号信息
sockaddr_in local;
::bzero(&local, sizeof(local));
socklen_t addrlen = sizeof(local);
if (::getsockname(sockfd, (sockaddr*)(&local), &addrlen) < 0) {
LOG_ERROR("sockets::getLocalAddr");
}
InetAddress localAddr(local);
// 根据连接成功的sockfd,创建TcpConnection连接对象
TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
m_connectionMap[connName] = conn;
// 下面的回调都是用户设置给TcpServer =》TcpConnection =》Channel =》Poller =》notify =》channel回调
conn->setConnectionCallback(m_connectionCallback);
conn->setMessageCallback(m_messageCallback);
conn->setWriteCompleteCallback(m_writeCompleteCallback);
// 设置了如何关闭连接的回调 conn->shutdown
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, std::placeholders::_1));
// 直接调用TcpConnection::connectEstablished
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
这个就是销毁连接的函数
cpp
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
m_loop->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
LOG_INFO("TcpServer::removeConnnectionInLoop [%s] - connection %s \n", m_name.c_str(), conn->name().c_str());
m_connectionMap.erase(conn->name());
EventLoop* ioloop = conn->getLoop();
ioloop->queueInLoop(std::bind(&TcpConnection::connectDestroyed, conn));
}
总结
本篇文章到这里就结束了!!!muduo网络库也到这里结束了~~~我会在不久的将来重新写一遍网络库的代码,到时候会有一篇文章是关于重写网络库的心得和注意事项~~~希望可以帮助大家理解~~~