本文主要介绍的是muduo网络库对于tcp通信的相关处理及源码分析,关于tcp相关知识,可以参考:https://blog.csdn.net/www_dong/category_10706734.html
模型
tcp相关类说明:
TcpConnection: TCP连接管理
Acceptor: 接收连接
Connector: 发起连接
TcpServer: 管理Acceptor获取的连接
实现流程:
1)TcpServer构造时会创建Acceptor对象并设置新连接回调;
2)Acceptor接收Channel类的handleRead回调,在handleRead函数中调用socket的accept的接口接收新连接,接收成功后回调至TcpServer;
3)TcpServer成功接收回调后创建TcpConnection对象(通过std::shared_d智能指针管理),创建读、写、关闭等回调,并将连接加入到EventLoop中;
4)TcpConnection类做连接成功处理(设置连接状态、Channel绑定当前连接、读使能等);
源码分析
TcpServer
TcpServer类管理TcpConnection,供用户直接使用,生命周期由用户控制。用户只需要设置好callback,然后调用start()即可。
构造函数
主要完成以下几项任务:
1)创建Acceptor对象,用于接收新连接;
2)创建线程池EventLoopThreadPool的实例;
3)接收并保存回调,当有消息时进行回调;
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));
}
析构函数
移除所有连接。
TcpServer::~TcpServer()
{
loop_->assertInLoopThread();
LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";
for (auto& item : connections_)
{
TcpConnectionPtr conn(item.second);
item.second.reset();
conn->getLoop()->runInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
}
newConnection
1)接收Acceptor回调;
2)创建TcpConnection;
3)将当前连接加入事件循环;
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
TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr)); // 创建TcpConnection对象
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)); // 将连接加入事件循环
}
start(供用户调用)
1)启动线程池;
2)启动监听;
void TcpServer::start()
{
if (started_.getAndSet(1) == 0)
{
threadPool_->start(threadInitCallback_); // 线程池启动
assert(!acceptor_->listening());
loop_->runInLoop(
std::bind(&Acceptor::listen, get_pointer(acceptor_))); // 启动监听
}
}
get_pointer
用于获取智能指针维护的原始指针。
template<typename T>
inline T* get_pointer(const std::shared_ptr<T>& ptr)
{
return ptr.get();
}
Acceptor
主要功能如下:
1)启动监听;
2)处理Channel连接回调;
构造函数
1)初始化socket;
2)设置socket监听;
3)设置Channel类的handleRead回调,处理新连接;
Acceptor::Acceptor(EventLoop* loop, const InetAddress& listenAddr, bool reuseport)
: loop_(loop),
acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
acceptChannel_(loop, acceptSocket_.fd()),
listening_(false),
idleFd_(::open("/dev/null", O_RDONLY | O_CLOEXEC)) // 空闲fd指向文件"/dev/null",用来解决服务器fd资源耗尽问题
{
assert(idleFd_ >= 0);
acceptSocket_.setReuseAddr(true);
acceptSocket_.setReusePort(reuseport);
acceptSocket_.bindAddress(listenAddr);
acceptChannel_.setReadCallback(
std::bind(&Acceptor::handleRead, this));
}
析构函数
Acceptor::~Acceptor()
{
acceptChannel_.disableAll();
acceptChannel_.remove();
::close(idleFd_);
}
idleFd_
Acceptor用了这样一种技术:
先申请一个空闲的fd(idleFd_),等到发生由于fd资源不够用时,就把这个备用fd暂时用于accept接收连接,然后再马上关闭,以防止不断产生可读事件(连接请求),从而回调相同的失败代码。及早建立连接后并关闭连接,让程序不会频繁响应同一个连接请求。
# 相关代码如下
if (errno == EMFILE)
{
::close(idleFd_);
idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
::close(idleFd_);
idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
}
listen
监听本地sock fd, 使能监听Channel读事件。
void Acceptor::listen()
{
loop_->assertInLoopThread();
listening_ = true;
acceptSocket_.listen(); // 开始监听
acceptChannel_.enableReading(); // Channel使能读事件
}
TcpConnection
TcpConnection使用Channel获得socket上IO事件,它会自己处理writable事件,把可读事件通过MessageCallback传递给客户。
构造函数
主要功能如下:
1)创建Channel对象;
2)设置Channel读、写、关闭和错误事件回调;
3)调用Socket类创建socket_对象,sockfd由TcpServer传入;
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting), // 设置连接状态为kConnecting
reading_(true),
socket_(new Socket(sockfd)), // 创建socket
channel_(new Channel(loop, sockfd)), // 创建channel_
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);
}
消息发送
sendInLoop是loop线程排队发送消息的核心函数,主要是向对端发送一次数据,如果发送完一次,就进行一次回调;如果待发送数据超高水位,就进行高水位回调;如果发生错误,就进行错误回调。
void TcpConnection::sendInLoop(const void* data, size_t len)
{
loop_->assertInLoopThread();
ssize_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
if (state_ == kDisconnected)
{
LOG_WARN << "disconnected, give up writing";
return;
}
// if no thing in output queue, try writing directly
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) // 如果通道没有使能监听写事件, 并且outputBuffer 没有待发送数据, 就直接通过socket写
{
nwrote = sockets::write(channel_->fd(), data, len);
if (nwrote >= 0)
{
remaining = len - nwrote;
if (remaining == 0 && writeCompleteCallback_) // 数据已写完
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
}
else // nwrote < 0
{
nwrote = 0;
if (errno != EWOULDBLOCK)
{
LOG_SYSERR << "TcpConnection::sendInLoop";
if (errno == EPIPE || errno == ECONNRESET) // FIXME: any others?
{
faultError = true;
}
}
}
}
// 处理剩余待发送数据
assert(remaining <= len);
if (!faultError && remaining > 0) //没有故障, 并且还有待发送数据, 可能是发送太快, 对方来不及接收
{
size_t oldLen = outputBuffer_.readableBytes();
if (oldLen + remaining >= highWaterMark_ // // Buffer及当前要发送的数据量之和 超 高水位(highWaterMark)
&& oldLen < highWaterMark_ // 单独的Buffer中待发送数据量 未超 高水位
&& highWaterMarkCallback_)
{
loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
}
outputBuffer_.append(static_cast<const char*>(data)+nwrote, remaining);
if (!channel_->isWriting())
{
channel_->enableWriting();
}
}
}
handleRead
inputBuffer_中读取到数据,则执行上层消息回调。
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0)
{
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0)
{
handleClose();
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
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();
}
}
}
else
{
LOG_SYSERR << "TcpConnection::handleWrite";
// if (state_ == kDisconnecting)
// {
// shutdownInLoop();
// }
}
}
else
{
LOG_TRACE << "Connection fd = " << channel_->fd()
<< " is down, no more writing";
}
}
handleClose
主要处理以下几项任务:
1)设置状态为未连接;
2)通道不使能;
3)关闭回调上层;
void TcpConnection::handleClose()
{
loop_->assertInLoopThread();
LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
assert(state_ == kConnected || state_ == kDisconnecting);
// we don't close fd, leave it to dtor, so we can find leaks easily.
setState(kDisconnected);
channel_->disableAll();
TcpConnectionPtr guardThis(shared_from_this());
connectionCallback_(guardThis);
// must be the last line
closeCallback_(guardThis);
}
Channel断开连接
Channel断开连接流程:
1)Poller::poll()检测到Channel事件就绪;
2)EventLoop检测事件;
3)Channel::handleEvent()处理事件;
4)Channel::closeCallback_回调给TcpConnection事件;
5)TcpConnection::handleClose()处理事件关闭;
connectDestroyed(资源销毁)
连接断开的调用流程如下:
TcpServer::removeConnection <--- TcpServer::removeConnectionInLoop <--- TcpConnection::connectDestroyed
TcpServer中调用removeConnection移除连接。
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
// FIXME: unsafe
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
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)); // 确保是当前循环, 执行TcpConnection中的connectDestroyed
}
connectDestroyed处理如下:
注意:只有状态为kConnected的连接时,才可以采取断开连接动作。
void TcpConnection::connectDestroyed()
{
loop_->assertInLoopThread();
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll();
connectionCallback_(shared_from_this());
}
channel_->remove();
}