说明
TcpServer类,直接给用户使用的类。如果下层的那些类看明白了,这个很简单,指挥各个类的运作即可。 主要工作如下:
1. 接受acceptor的新连接, 并创建TcpConnection对象,注册TcpConnection的可读事件。
2. 接受用户三个半事件的回调:
陈硕认为,TCP 网络编程的本质是处理三个半事件,即:
连接的建立
连接的断开:包括主动断开和被动断开
消息到达,文件描述符可读。
消息发送完毕。这个算半个事件。
成员变量
重点关注:
1. loop:主线程loop,和acceptor, io线程池是一样的loop;
2. IpPort: 监听的ip和端口
3.acceptor: 接受器,用于接受新连接。
4.threadPool_: IO线程池。
5. 四个回调,其中连接的建立和断开用同一个回调。使用tcpconnection的状态判断是连接还是断开。
6. nextId:为每个连接起一个不一样的名字。
8. connections_: 保存了所有TcpConnection的智能指针,保证连接断开前不被析构。
EventLoop* loop_; // the acceptor loop //主线程
const string ipPort_; //服务端口
const string name_;
/*
1、 unique_ptr去掉了拷贝构造函数和operator=赋值重载函数
禁止用户对unique_ptr进行显示的拷贝构造和赋值,防止智能指针浅拷贝问题的发生。
2、但是unique_ptr提供了带右值引用参数的拷贝构造和赋值
unique_ptr智能指针可以通过右值引用进行拷贝构造和赋值操作
或者在产生unique_ptr临时对象的地方,例如函数返回值为unique_ptr。
*/
std::unique_ptr<Acceptor> acceptor_; // avoid revealing Acceptor //提供监听套接字创建绑定功能,listen开始监听
std::shared_ptr<EventLoopThreadPool> threadPool_; //线程池指针
/*
陈硕认为,TCP 网络编程的本质是处理三个半事件,即:
连接的建立
连接的断开:包括主动断开和被动断开
消息到达,文件描述符可读。
消息发送完毕。这个算半个事件。
*/
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
WriteCompleteCallback writeCompleteCallback_;
ThreadInitCallback threadInitCallback_;
AtomicInt32 started_; //判断是否开始,一开始为0,在start()函数中判断
// always in loop t hread
int nextConnId_; //下一个连接ID
ConnectionMap connections_; //连接列表,实际是TcpConnection指针列表
方法
1. TcpServer:构造函数,初始化数据, 设置acceptor新连接到来的回调(这个回调是核心)
//需要传参EventLoop*,InetAddress&,服务名称字符串和枚举option
//各变量的初始化,新建Acceptor和EventLoopThreadPool,设置Acceptor的连接回调函数为newConnection
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));
}
2.newConnection: 该函数是acceptor新连接到来的回调(重要)
- 从IO线程池轮询获取一个loop;
- 给新连接一个名字;
- 创建新连接并在TcpServer连接容器中保存一份,防止智能指针引用计数为0会析构。
- 设置几个回调函数(这个其实是用户设置的)
- 最后的runInLoop,调用connectEstablished. 其实就是在loop中关注当前tcp连接的读事件
// 该函数作为acceptor_的新连接建立回调函数
// 新建一个TcpConnectionPtr连接对象,设置一些回调函数,最后运行TcpConnection::connectEstablished函数,这个函数就是把通道加入到Poller进行关注
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
EventLoop* ioLoop = threadPool_->getNextLoop(); //为新连接分配IO线程
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
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
/*
连接建立后,放到属于此tcpconnect的eventloop中执行TcpConnection::connectEstablished函数。
connectEstablished函数中,监听此tcp连接的可读事件。并通知用户的onConnection
*/
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
3. removeConnection:注册给每一个TcpConnection的连接关闭的回调。
- 删除TcpServer的连接容器中保存的当前连接。
- 在TcpConnection所属的IO线程调用connectDestroyed,取消所有关注事件,并在poller中删除当前TcpConnection的channel。
//连接的关闭回调函数,调用removeConnectionInLoop
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
// FIXME: unsafe
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
// 从连接列表中移除这个连接,获得该连接的loop事件.把TcpConnection的connectDestroyed()函数加入到loop事件中
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
loop_->assertInLoopThread();
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
<< "] - connection " << conn->name();
size_t n = connections_.erase(conn->name());
(void)n;
assert(n == 1);
EventLoop* ioLoop = conn->getLoop();
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
4. setThreadNum:设置IO线程数量
//调用threadPool_->setThreadNum()设置线程池的线程数量
void TcpServer::setThreadNum(int numThreads)
{
assert(0 <= numThreads);
threadPool_->setThreadNum(numThreads);
}
5. start
- 服务开启,主线程创建IO线程池中所有线程并收集所有线程的EventLoop*;
- listen: acceptor开始监听,并往事件循环注册可读事件
// 该函数可以多次调用,可以跨线程调用
// 线程池start(),开启acceptor_的listen()函数,get_pointer()是muduo的获得智能指针的裸指针的函数
void TcpServer::start()
{
if (started_.getAndSet(1) == 0)
{
//服务开启,主线程创建所有线程并收集所有线程的EventLoop*;
threadPool_->start(threadInitCallback_);
assert(!acceptor_->listening());
//主循环中监听socket开始监听。
loop_->runInLoop(
std::bind(&Acceptor::listen, get_pointer(acceptor_)));
}
}