这一节我们将继续完善 TcpServer 和 TCPConnection 类,并且解决上一节思考的问题【一定要理清思路】;
ConnectionCallback connectionCallback_; //连接到来和结束的回调函数
//可以自己设置,muduo 库是有默认的
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
void removeConnection(const TcpConnectionPtr& conn);
void removeConnectionInLoop(const TcpConnectionPtr& conn);
TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg,
Option option)
: loop_(CHECK_NOTNULL(loop)), //检查 loop 指针是否为 NULL
ipPort_(listenAddr.toIpPort()),
name_(nameArg),
acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),
------------------------------------------------------------------------
//threadPool_(new EventLoopThreadPool(loop, name_)),
//默认的连接回调函数,全局函数
//这个函数的作用就是输出客户端套接字的状态
connectionCallback_(defaultConnectionCallback),
------------------------------------------------------------------------
// messageCallback_(defaultMessageCallback),
nextConnId_(1)
{
//设置 newConnection 的回调函数,因为有两个参数,所以有两个占位符
//第一个参数是客户端套接字,第二个参数是客户端地址
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this, _1, _2));
}
//根据 conn 的状态判断现在是连接还是断开
void muduo::net::defaultConnectionCallback(const TcpConnectionPtr& conn)
{
LOG_TRACE << conn->localAddress().toIpPort() << " -> "
<< conn->peerAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
}
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
//断言在 IO 线程中
loop_->assertInLoopThread();
//EventLoop* ioLoop = threadPool_->getNextLoop();
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
//连接的名称是 服务器名称 + 服务器端口号 + 连接 ID
string connName = name_ + buf;
//输出客户端的信息
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd));
// 创建一个 TcpConnectionPtr 对象指向一个 TCPConnection
//不太理解的地方,传递的 peerAddr 是客户端的地址,localAddr 也是客户端的地址?
TcpConnectionPtr conn(new TcpConnection(loop,
connName,
sockfd,
localAddr,
peerAddr));
/*TcpConnectionPtr conn(new TcpConnection(ioLoop,
connName,
sockfd,
localAddr,
peerAddr));
*/
//记录客户端连接
connections_[connName] = conn;
--------------------------------------------------------------
//注册 TCPConnection 的 connectionCallback 回调函数
conn->setConnectionCallback(connectionCallback_);
----------------------------------------------------------------
//conn->setMessageCallback(messageCallback_);
//conn->setWriteCompleteCallback(writeCompleteCallback_);
------------------------------------------------------------------
//注册 TCPConnection 的 CloseCallback 回调函数
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, _1));
---------------------------------------------------------------------
//调用 TcpConnection::connectEstablished 函数
loop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
//ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}
//removeConnection 可跨线程使用
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
//只能在 IO 线程中使用
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
loop_->assertInLoopThread();
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
<< "] - connection " << conn->name();
//将对象从列表中移除,注意此时引用计数会减 1
//如果不使用 tie,则是引用计数为 0
//TcpConnection 对象被销毁,回国 TcpConnection 的 channel 对象就会被销毁,而此时我们处于 channel 对象的 handleEvent() 中,会造成 coredump
//使用 tie 后,调用 handleEvent() 时,weak_ptr 会被升级为 share_ptr,所以此时删除并不会销毁 TcpConnection
size_t n = connections_.erase(conn->name());
(void)n;
assert(n == 1);
//注意,目前这里的 ioLoop 就是上面的 loop_
EventLoop* ioLoop = conn->getLoop();
//这里调用 queueInLoop 而不是调用 runInLoop
//这也是 queueInLoop 里面有两个条件的原因
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
这里只是多给出了 TCPConnection 连接断开时触发的函数;
ConnectionCallback connectionCallback_; //连接回调函数,包含连接建立和连接断开
CloseCallback closeCallback_; //连接断开回调函数
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
void setCloseCallback(const CloseCallback& cb)
{ closeCallback_ = cb; }
void connectDestroyed();
void handleRead(Timestamp receiveTime);
void handleClose();
void handleError();
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting),
reading_(true),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
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));
//发生错误,回调 handleError
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this));
------------------------------------------------------------------
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this
<< " fd=" << sockfd;
socket_->setKeepAlive(true);
}
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting);
//设置状态为已连接
setState( kConnected);
//share_from_this 是根据裸指针创建一个 shared_ptr 对象
//这里使用了 Channel 的 tie,这是 Channel 中没有讨论的地方,这里会将 Channel 完善
channel_->tie(shared_from_this());
//关注 TcpConnect 的可读事件
channel_->enableReading();
---------------------------------------------------
//这里输出成功连接
connectionCallback_(shared_from_this());
---------------------------------------------------
}
//接收客户端的信息
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();
}
*/
//以下是模拟普通的接收过程
loop_->assertInLoopThread();
int saveErrno = 0;
char buf[65536];
ssize_t n = ::read(channel->fd(), buf, sizeof buf);
if (n > 0) {
messageCallback(shared_from_this(), buf, n);
}
//处理连接断开
else if (n == 0) {
handleClose();
}
//处理错误情况
else {
errno = saveErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
void TcpConnection::handleClose()
{
loop_->assertInLoopThread();
LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
assert(state_ == kConnected || state_ == kDisconnecting);
//这里将状态设置为已断开连接
setState(kDisconnected);
//这里并没有调用 channel_ 的 remove(),因为当前还处于 channel_ 的 handleEvent() 函数中
channel_->disableAll();
TcpConnectionPtr guardThis(shared_from_this());
//connectionCallback_ 函数在这里是显示连接断开
connectionCallback_(guardThis);
//这里调用的 TcpServer::removeConnection 函数
closeCallback_(guardThis);
}
//错误输出
void TcpConnection::handleError()
{
int err = sockets::getSocketError(channel_->fd());
LOG_ERROR << "TcpConnection::handleError [" << name_
<< "] - SO_ERROR = " << err << " " << strerror_tl(err);
}
//connectDestroyed 将 channel 从管道中移除
void TcpConnection::connectDestroyed()
{
loop_->assertInLoopThread();
//前面设置过状态为 kDisconnected,所以这里为 fasle
//前面可以不作处理,在这里进行处理
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll();
connectionCallback_(shared_from_this());
}
//此时 handleEvent() 已经处理完了,可以调用 remove() 函数了
channel_->remove();
}
其实在上面的备注中我已经说明了问什么要使用 tie,但是为了理清整体的思路,这边会详细介绍一下整体的过程【直接从客户端发起连接开始说起】;
//将 conn 传入到队列中,执行玩这个函数后,conn 会被释放,引用计数减 1,对象销毁
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
TcpServer 中还有多线程没有讨论,TcpConnection 中还有应用层缓冲和输入输出处理函数没有讨论,这是接下来讨论的重点;讨论完后,muduo 库的实现就差不多完成了!!!