9. TCPconnection
约 5588 字大约 19 分钟
2026-04-19
TcpConnection 简介
TcpConnection 类可以理解为 muduo 中对一条 TCP 连接的完整封装。
如果说:
Acceptor负责“接进来一个新连接”Channel负责“监听某个 fd 上发生了什么事件”EventLoop/Poller负责“把事件分发出去”
那么 TcpConnection 就是把这些东西串起来的 连接实体。它负责一条连接从建立、读写、半关闭、关闭到销毁的整个生命周期。
它内部不仅保存了:
- 连接对应的
Socket - 连接对应的
Channel - 连接的输入输出缓冲区
还保存了用户注册的各种回调函数,比如:
- 连接建立回调
- 消息到达回调
- 写完成回调
- 高水位回调
- 关闭回调
可以把它理解为:
- Socket:真正和内核通信的 fd 封装
- Channel:负责监听这个 fd 的事件
- Buffer:负责缓存收发的数据
- TcpConnection:负责管理这条连接的完整生命周期
thread(main)
└── TcpServer
├── mainLoop
│ ├── Poller
│ │ ├── wakeupChannel
│ │ └── acceptChannel
│ └── Acceptor
│ └── Socket(listenfd)
│
├── EventLoopThreadPool
│ ├── subThread1 -> subLoop1 -> Poller -> Channel(connfd...)
│ ├── subThread2 -> subLoop2 -> Poller -> Channel(connfd...)
│ └── subThread3 -> subLoop3 -> Poller -> Channel(connfd...)
│
└── connections_
├── TcpConnection1
├── TcpConnection2
└── TcpConnection3TcpConnection 类重要的成员变量
1. 重要成员
EventLoop *loop_:这个 TcpConnection 所属的事件循环。
在多 Reactor 模型下,它通常指向subLoop;在单 Reactor 模型下,可能指向baseLoop。
这个loop_决定了该连接的事件和回调应该在哪个线程中执行。const std::string name_:连接的名字。
通常由服务器为每条连接生成唯一标识,便于日志打印和调试。std::atomic_int state_:连接当前的状态。
它是一个原子状态机,用来表示连接处于:kDisconnectedkConnectingkConnectedkDisconnecting
bool reading_:表示当前连接是否正在监听读事件。
这个成员在当前代码里主要用于标记读状态。std::unique_ptr<Socket> socket_:连接对应的 socket 封装。
负责执行底层的shutdownWrite()、setKeepAlive()等操作。std::unique_ptr<Channel> channel_:连接对应的 Channel。
负责把这个连接的 fd 注册到 Poller,并监听读写关闭错误等事件。const InetAddress localAddr_:本地地址。
也就是这个连接所在 socket 的本机 IP 和端口。const InetAddress peerAddr_:对端地址。
也就是客户端的 IP 和端口。ConnectionCallback connectionCallback_:连接建立/状态变化时的回调。MessageCallback messageCallback_:有消息到达时的回调。WriteCompleteCallback writeCompleteCallback_:消息全部发送完成后的回调。HighWaterMarkCallback highWaterMarkCallback_:输出缓冲区达到高水位时的回调。CloseCallback closeCallback_:连接关闭时的回调。size_t highWaterMark_:高水位阈值。
当输出缓冲区积压的数据超过这个值时,会触发高水位回调,提醒上层发送压力太大。Buffer inputBuffer_:输入缓冲区。
用于保存从对端读到的数据。Buffer outputBuffer_:输出缓冲区。
用于保存暂时还没发完的数据。
EventLoop *loop_; // 这里是baseloop还是subloop由TcpServer中创建的线程数决定 若为多Reactor 该loop_指向subloop 若为单Reactor 该loop_指向baseloop
const std::string name_;
std::atomic_int state_;
bool reading_;//连接是否在监听读事件
// Socket Channel 这里和Acceptor类似 Acceptor => mainloop TcpConnection => subloop
std::unique_ptr<Socket> socket_;
std::unique_ptr<Channel> channel_;
const InetAddress localAddr_;
const InetAddress peerAddr_;
// 这些回调TcpServer也有 用户通过写入TcpServer注册 TcpServer再将注册的回调传递给TcpConnection TcpConnection再将回调注册到Channel中
ConnectionCallback connectionCallback_; // 有新连接时的回调
MessageCallback messageCallback_; // 有读写消息时的回调
WriteCompleteCallback writeCompleteCallback_; // 消息发送完成以后的回调
HighWaterMarkCallback highWaterMarkCallback_; // 高水位回调
CloseCallback closeCallback_; // 关闭连接的回调
size_t highWaterMark_; // 高水位阈值
// 数据缓冲区
Buffer inputBuffer_; // 接收数据的缓冲区
Buffer outputBuffer_; // 发送数据的缓冲区 用户send向outputBuffer_发连接状态(StateE)
enum StateE
{
kDisconnected, // 已经断开连接
kConnecting, // 正在连接
kConnected, // 已连接
kDisconnecting // 正在断开连接
};状态含义
kConnecting:连接刚创建,还处于建立阶段。kConnected:连接已经建立,可以正常收发数据。kDisconnecting:上层已经调用了shutdown(),正在等待发送缓冲区的数据发送完毕后再优雅关闭。kDisconnected:连接已经断开,不能再发送数据。
为什么需要状态机?
因为 TCP 连接不是“连上/断开”这么简单。 比如:
- 对方关闭了连接
- 本地想优雅关闭写端
- 发送缓冲区还有未发完的数据
- 连接已经销毁,不能再访问
状态机可以保证这些流程不会乱掉。
TcpConnection 类重要的成员方法
1. 构造 / 析构
TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr);
~TcpConnection();TcpConnection::TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr)
: loop_(CheckLoopNotNull(loop))
, name_(nameArg)
, state_(kConnecting)
, reading_(true)
, socket_(new Socket(sockfd))
, channel_(new Channel(loop, sockfd))
, localAddr_(localAddr)
, peerAddr_(peerAddr)
, highWaterMark_(64 * 1024 * 1024) // 64M
{
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
channel_->setWriteCallback(
std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(
std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this));
LOG_INFO("TcpConnection::ctor[%s] at fd=%d\n", name_.c_str(), sockfd);
socket_->setKeepAlive(true);
}
TcpConnection::~TcpConnection()
{
LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d\n", name_.c_str(), channel_->fd(), (int)state_);
}构造函数主要做了几件事:
- 检查
loop是否为空; - 保存连接名字;
- 初始化连接状态为
kConnecting; - 创建
Socket和Channel; - 保存本地和对端地址;
- 设置默认高水位阈值;
- 给
Channel注册读、写、关闭、错误回调; - 打开
SO_KEEPALIVE。
它把“一个 fd + 一个事件循环 + 一组缓冲区 + 一组回调”组合成了一条完整连接。
2. getLoop() / name() / 地址获取 / connected()
EventLoop *getLoop() const { return loop_; }
const std::string &name() const { return name_; }
const InetAddress &localAddress() const { return localAddr_; }
const InetAddress &peerAddress() const { return peerAddr_; }
bool connected() const { return state_ == kConnected; }这些函数主要是查询连接的基本信息。
getLoop():拿到连接所属的 EventLoop。name():拿到连接名称。localAddress()/peerAddress():拿到本地和对端地址。connected():判断当前连接是否处于已连接状态。
3. send(const std::string &buf)
void send(const std::string &buf);void TcpConnection::send(const std::string &buf)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread())
{
sendInLoop(buf.c_str(), buf.size());
}
else
{
loop_->runInLoop(
std::bind(&TcpConnection::sendInLoop, this, buf.c_str(), buf.size()));
}
}
}这个函数用于发送数据。
如果当前就在连接所属的事件循环线程中,就直接发送;
如果不在,就把发送任务投递到所属线程中执行。
提示
为什么要这样?
因为 TcpConnection 和它的 Channel 必须在正确的 EventLoop 线程中操作,不能随便跨线程直接改状态。
4. sendInLoop(const void *data, size_t len)
void sendInLoop(const void *data, size_t len);void TcpConnection::sendInLoop(const void *data, size_t len)
{
ssize_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
if (state_ == kDisconnected)
{
LOG_ERROR("disconnected, give up writing");
}
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
nwrote = ::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;
if (errno != EWOULDBLOCK)
{
LOG_ERROR("TcpConnection::sendInLoop");
if (errno == EPIPE || errno == ECONNRESET)
{
faultError = true;
}
}
}
}
if (!faultError && remaining > 0)
{
size_t oldLen = outputBuffer_.readableBytes();
if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_)
{
loop_->queueInLoop(
std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
}
outputBuffer_.append((char *)data + nwrote, remaining);
if (!channel_->isWriting())
{
channel_->enableWriting();
}
}
}这个函数是真正执行发送逻辑的地方。
- 先尝试直接写到 socket;
- 如果没写完,把剩余数据放到
outputBuffer_; - 需要时注册写事件
EPOLLOUT,等待后续继续发送; - 如果全部写完,触发写完成回调;
- 如果输出缓冲区积压过多,触发高水位回调。
提示
为什么要有输出缓冲区?
因为应用层发送速度通常比内核发送速度快。 如果一次写不完,就必须把未发送完的数据缓存起来,等 socket 可写时再继续发。
5. sendFile(int fileDescriptor, off_t offset, size_t count)
void sendFile(int fileDescriptor, off_t offset, size_t count);void TcpConnection::sendFile(int fileDescriptor, off_t offset, size_t count)
{
if (connected())
{
if (loop_->isInLoopThread())
{
sendFileInLoop(fileDescriptor, offset, count);
}
else
{
loop_->runInLoop(
std::bind(&TcpConnection::sendFileInLoop, shared_from_this(), fileDescriptor, offset, count));
}
}
else
{
LOG_ERROR("TcpConnection::sendFile - not connected");
}
}这个函数用于零拷贝发送文件数据。
如果连接处于已连接状态,就把 sendfile() 相关任务放到正确的事件循环线程中执行。
提示
为什么有这个接口?
它可以更高效地把文件内容直接发送到 socket,减少用户态拷贝。
6. sendFileInLoop(int fileDescriptor, off_t offset, size_t count)
void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);void TcpConnection::sendFileInLoop(int fileDescriptor, off_t offset, size_t count)
{
ssize_t bytesSent = 0;
size_t remaining = count;
bool faultError = false;
if (state_ == kDisconnecting)
{
LOG_ERROR("disconnected, give up writing");
return;
}
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
bytesSent = sendfile(socket_->fd(), fileDescriptor, &offset, remaining);
if (bytesSent >= 0)
{
remaining -= bytesSent;
if (remaining == 0 && writeCompleteCallback_)
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
}
else
{
if (errno != EWOULDBLOCK)
{
LOG_ERROR("TcpConnection::sendFileInLoop");
}
if (errno == EPIPE || errno == ECONNRESET)
{
faultError = true;
}
}
}
if (!faultError && remaining > 0)
{
loop_->queueInLoop(
std::bind(&TcpConnection::sendFileInLoop, shared_from_this(), fileDescriptor, offset, remaining));
}
}这个函数是真正执行 sendfile() 的地方。
它的工作方式
- 先检查连接状态;
- 如果当前没有待发送缓冲数据,就尝试直接用
sendfile(); - 如果没有一次发完,就把剩余部分继续投递到事件循环中;
- 如果全部发送完,触发写完成回调。
7. shutdown()
void shutdown();void TcpConnection::shutdown()
{
if (state_ == kConnected)
{
setState(kDisconnecting);
loop_->runInLoop(
std::bind(&TcpConnection::shutdownInLoop, this));
}
}这个函数用于关闭写方向,也就是 TCP 的半关闭。
如果当前处于已连接状态,就把状态切成 kDisconnecting,然后在所属线程中执行 shutdownInLoop()。
提示
为什么不是直接 close?
因为这里想要的是 优雅关闭: 先把输出缓冲区剩余数据发送完,再关闭写端。
8. shutdownInLoop()
void shutdownInLoop();void TcpConnection::shutdownInLoop()
{
if (!channel_->isWriting())
{
socket_->shutdownWrite();
}
}这个函数真正执行写端关闭。
如果当前 Channel 已经不再监听写事件,说明输出缓冲区的数据已经发完了, 这时候就可以调用 socket_->shutdownWrite(),关闭写方向。
9. connectEstablished()
void connectEstablished();void TcpConnection::connectEstablished()
{
setState(kConnected);
channel_->tie(shared_from_this());
channel_->enableReading();
connectionCallback_(shared_from_this());
}这个函数表示连接已经建立完成。
- 把状态改成
kConnected; - 通过
tie()绑定自身生命周期,防止回调执行时对象被销毁; - 注册读事件;
- 触发连接建立回调。
连接建立后,Channel 才真正开始监听这个 socket 的读事件,后续数据才能被收到。
10. connectDestroyed()
void connectDestroyed();void TcpConnection::connectDestroyed()
{
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll();
connectionCallback_(shared_from_this());
}
channel_->remove();
}这个函数表示连接即将销毁。
- 如果连接还处于
kConnected,先改成kDisconnected; - 取消所有事件监听;
- 触发连接回调,通知上层连接状态变化;
- 最后把 Channel 从 Poller 中移除。
提示
为什么最后要 remove()?
因为连接彻底结束后,Channel 就不应该再被 Poller 管理了。
11. handleRead(Timestamp receiveTime)
void handleRead(Timestamp receiveTime);void TcpConnection::handleRead(Timestamp receiveTime)
{
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_ERROR("TcpConnection::handleRead");
handleError();
}
}这个函数是读事件的回调。
- 从 socket 读数据到输入缓冲区;
- 如果读到了数据,就触发消息回调;
- 如果读到 0,说明对端关闭了连接;
- 如果出错,就处理错误。
提示
为什么要先读进 inputBuffer_?
因为 TCP 是字节流,应用层收到的往往不是一个完整“消息”,而是连续的数据流。 先放进缓冲区,再由上层按协议解析更安全。
12. handleWrite()
void handleWrite();void TcpConnection::handleWrite()
{
if (channel_->isWriting())
{
int savedErrno = 0;
ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);
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_ERROR("TcpConnection::handleWrite");
}
}
else
{
LOG_ERROR("TcpConnection fd=%d is down, no more writing", channel_->fd());
}
}这个函数是写事件的回调。
- 检查当前是否真的在监听写事件;
- 把输出缓冲区的数据写到 socket;
- 如果写完了,就取消写事件监听;
- 触发写完成回调;
- 如果之前进入了
kDisconnecting,现在可以真正关闭写端了。
它负责把 outputBuffer_ 里的数据尽量写出去,并在写完后恢复正常状态。
13. handleClose()
void handleClose();void TcpConnection::handleClose()
{
LOG_INFO("TcpConnection::handleClose fd=%d state=%d\n", channel_->fd(), (int)state_);
setState(kDisconnected);
channel_->disableAll();
TcpConnectionPtr connPtr(shared_from_this());
connectionCallback_(connPtr);
closeCallback_(connPtr); // must be the last line
}这个函数用于处理连接关闭事件。
- 打日志;
- 状态改成
kDisconnected; - 取消所有事件监听;
- 触发连接回调;
- 调用关闭回调,把销毁请求交给上层。
提示
为什么 closeCallback_ 必须放在最后?
因为它通常会触发连接移除和销毁逻辑。 一旦执行了关闭回调,这个对象后续就可能被销毁,所以它必须放在最后一行。
14. handleError()
void handleError();void TcpConnection::handleError()
{
int optval;
socklen_t optlen = sizeof optval;
int err = 0;
if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
{
err = errno;
}
else
{
err = optval;
}
LOG_ERROR("TcpConnection::handleError name:%s - SO_ERROR:%d\n", name_.c_str(), err);
}这个函数用于处理 socket 错误。
通过 getsockopt(SO_ERROR) 查询 socket 的具体错误原因,然后打印日志。
TcpConnection 和 Channel、Socket、Buffer 的关系
可以这样理解:
Socket:负责底层系统调用,比如关闭写端、设置 keepaliveChannel:负责监听 fd 的读写关闭错误事件Buffer:负责缓存输入输出数据TcpConnection:把这三者拼成一条完整连接,并管理连接生命周期
TcpConnection 的整体工作流程
1. 创建连接
TcpServer::newConnection() 创建 TcpConnection,初始化其 Socket、Channel 和缓冲区。
2. 建立连接
调用 connectEstablished():
- 把状态设为
kConnected - 绑定
tie - 开始监听读事件
- 执行连接建立回调
3. 收消息
当 socket 可读时:
Channel触发handleRead()TcpConnection把数据读到inputBuffer_- 执行
messageCallback_
4. 发消息
上层调用 send():
- 如果能直接写,就直接写
- 写不完就放进
outputBuffer_ - 注册写事件,等待后续继续发送
5. 关闭连接
上层调用 shutdown() 或对端关闭连接:
- 状态机转移
- 停止监听事件
- 执行关闭流程
- 最终调用
connectDestroyed()
TcpConnection 就像一条“完整的会话管道”:
- 它知道这条连接是谁和谁之间的
- 它知道什么时候读、什么时候写
- 它知道数据先放哪儿
- 它知道什么时候关闭
- 它还知道该通知谁
总结
TcpConnection 的核心职责
- 封装一条 TCP 连接的生命周期
- 管理对应的 Socket 和 Channel
- 维护输入输出缓冲区
- 处理读、写、关闭、错误事件
- 提供连接、消息、写完成、高水位、关闭等回调
- 保证所有操作在正确的 EventLoop 线程中执行
TcpConnection 是 muduo 中对一条 TCP 连接的完整封装,它把 Socket、Channel、Buffer 和各种回调组合起来,负责连接从建立到销毁的整个生命周期。
TcpConnection 源码
#pragma once
#include <memory>
#include <string>
#include <atomic>
#include "noncopyable.h"
#include "InetAddress.h"
#include "Callbacks.h"
#include "Buffer.h"
#include "Timestamp.h"
class Channel;
class EventLoop;
class Socket;
/**
* TcpServer => Acceptor => 有一个新用户连接,通过accept函数拿到connfd
* => TcpConnection设置回调 => 设置到Channel => Poller => Channel回调
**/
class TcpConnection : noncopyable, public std::enable_shared_from_this<TcpConnection>
{
public:
TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr);
~TcpConnection();
EventLoop *getLoop() const { return loop_; }
const std::string &name() const { return name_; }
const InetAddress &localAddress() const { return localAddr_; }
const InetAddress &peerAddress() const { return peerAddr_; }
bool connected() const { return state_ == kConnected; }
// 发送数据
void send(const std::string &buf);
void sendFile(int fileDescriptor, off_t offset, size_t count);
// 关闭半连接
void shutdown();
void setConnectionCallback(const ConnectionCallback &cb)
{ connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback &cb)
{ messageCallback_ = cb; }
void setWriteCompleteCallback(const WriteCompleteCallback &cb)
{ writeCompleteCallback_ = cb; }
void setCloseCallback(const CloseCallback &cb)
{ closeCallback_ = cb; }
void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, size_t highWaterMark)
{ highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }
// 连接建立
void connectEstablished();
// 连接销毁
void connectDestroyed();
private:
enum StateE
{
kDisconnected, // 已经断开连接
kConnecting, // 正在连接
kConnected, // 已连接
kDisconnecting // 正在断开连接
};
void setState(StateE state) { state_ = state; }
void handleRead(Timestamp receiveTime);
void handleWrite();//处理写事件
void handleClose();
void handleError();
void sendInLoop(const void *data, size_t len);
void shutdownInLoop();
void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);
EventLoop *loop_; // 这里是baseloop还是subloop由TcpServer中创建的线程数决定 若为多Reactor 该loop_指向subloop 若为单Reactor 该loop_指向baseloop
const std::string name_;
std::atomic_int state_;
bool reading_;//连接是否在监听读事件
// Socket Channel 这里和Acceptor类似 Acceptor => mainloop TcpConnection => subloop
std::unique_ptr<Socket> socket_;
std::unique_ptr<Channel> channel_;
const InetAddress localAddr_;
const InetAddress peerAddr_;
// 这些回调TcpServer也有 用户通过写入TcpServer注册 TcpServer再将注册的回调传递给TcpConnection TcpConnection再将回调注册到Channel中
ConnectionCallback connectionCallback_; // 有新连接时的回调
MessageCallback messageCallback_; // 有读写消息时的回调
WriteCompleteCallback writeCompleteCallback_; // 消息发送完成以后的回调
HighWaterMarkCallback highWaterMarkCallback_; // 高水位回调
CloseCallback closeCallback_; // 关闭连接的回调
size_t highWaterMark_; // 高水位阈值
// 数据缓冲区
Buffer inputBuffer_; // 接收数据的缓冲区
Buffer outputBuffer_; // 发送数据的缓冲区 用户send向outputBuffer_发
};#include <functional>
#include <string>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/tcp.h>
#include <sys/sendfile.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include "TcpConnection.h"
#include "Logger.h"
#include "Socket.h"
#include "Channel.h"
#include "EventLoop.h"
static EventLoop *CheckLoopNotNull(EventLoop *loop)
{
if (loop == nullptr)
{
LOG_FATAL("%s:%s:%d mainLoop is null!\n", __FILE__, __FUNCTION__, __LINE__);
}
return loop;
}
TcpConnection::TcpConnection(EventLoop *loop,
const std::string &nameArg,
int sockfd,
const InetAddress &localAddr,
const InetAddress &peerAddr)
: loop_(CheckLoopNotNull(loop))
, name_(nameArg)
, state_(kConnecting)
, reading_(true)
, socket_(new Socket(sockfd))
, channel_(new Channel(loop, sockfd))
, localAddr_(localAddr)
, peerAddr_(peerAddr)
, highWaterMark_(64 * 1024 * 1024) // 64M
{
// 下面给channel设置相应的回调函数 poller给channel通知感兴趣的事件发生了 channel会回调相应的回调函数
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
channel_->setWriteCallback(
std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(
std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this));
LOG_INFO("TcpConnection::ctor[%s] at fd=%d\n", name_.c_str(), sockfd);
socket_->setKeepAlive(true);
}
TcpConnection::~TcpConnection()
{
LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d\n", name_.c_str(), channel_->fd(), (int)state_);
}
void TcpConnection::send(const std::string &buf)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread()) // 这种是对于单个reactor的情况 用户调用conn->send时 loop_即为当前线程
{
sendInLoop(buf.c_str(), buf.size());
}
else
{
loop_->runInLoop(
std::bind(&TcpConnection::sendInLoop, this, buf.c_str(), buf.size()));
}
}
}
/**
* 发送数据 应用写的快 而内核发送数据慢 需要把待发送数据写入缓冲区,而且设置了水位回调
**/
void TcpConnection::sendInLoop(const void *data, size_t len)
{
ssize_t nwrote = 0;
size_t remaining = len;
bool faultError = false;
if (state_ == kDisconnected) // 之前调用过该connection的shutdown 不能再进行发送了
{
LOG_ERROR("disconnected, give up writing");
}
// 表示channel_第一次开始写数据或者缓冲区没有待发送数据
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
nwrote = ::write(channel_->fd(), data, len);
if (nwrote >= 0)
{
remaining = len - nwrote;
if (remaining == 0 && writeCompleteCallback_)
{
// 既然在这里数据全部发送完成,就不用再给channel设置epollout事件了
loop_->queueInLoop(
std::bind(writeCompleteCallback_, shared_from_this()));
}
}
else // nwrote < 0
{
nwrote = 0;
if (errno != EWOULDBLOCK) // EWOULDBLOCK表示非阻塞情况下没有数据后的正常返回 等同于EAGAIN
{
LOG_ERROR("TcpConnection::sendInLoop");
if (errno == EPIPE || errno == ECONNRESET) // SIGPIPE RESET
{
faultError = true;
}
}
}
}
/**
* 说明当前这一次write并没有把数据全部发送出去 剩余的数据需要保存到缓冲区当中
* 然后给channel注册EPOLLOUT事件,Poller发现tcp的发送缓冲区有空间后会通知
* 相应的sock->channel,调用channel对应注册的writeCallback_回调方法,
* channel的writeCallback_实际上就是TcpConnection设置的handleWrite回调,
* 把发送缓冲区outputBuffer_的内容全部发送完成
**/
if (!faultError && remaining > 0)
{
// 目前发送缓冲区剩余的待发送的数据的长度
size_t oldLen = outputBuffer_.readableBytes();
if (oldLen + remaining >= highWaterMark_ && oldLen < highWaterMark_ && highWaterMarkCallback_)
{
loop_->queueInLoop(
std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
}
outputBuffer_.append((char *)data + nwrote, remaining);
if (!channel_->isWriting())
{
channel_->enableWriting(); // 这里一定要注册channel的写事件 否则poller不会给channel通知epollout
}
}
}
void TcpConnection::shutdown()
{
if (state_ == kConnected)
{
setState(kDisconnecting);
loop_->runInLoop(
std::bind(&TcpConnection::shutdownInLoop, this));
}
}
void TcpConnection::shutdownInLoop()
{
if (!channel_->isWriting()) // 说明当前outputBuffer_的数据全部向外发送完成
{
socket_->shutdownWrite();
}
}
// 连接建立
void TcpConnection::connectEstablished()
{
setState(kConnected);
channel_->tie(shared_from_this());
channel_->enableReading(); // 向poller注册channel的EPOLLIN读事件
// 新连接建立 执行回调
connectionCallback_(shared_from_this());
}
// 连接销毁
void TcpConnection::connectDestroyed()
{
if (state_ == kConnected)
{
setState(kDisconnected);
channel_->disableAll(); // 把channel的所有感兴趣的事件从poller中删除掉
connectionCallback_(shared_from_this());
}
channel_->remove(); // 把channel从poller中删除掉
}
// 读是相对服务器而言的 当对端客户端有数据到达 服务器端检测到EPOLLIN 就会触发该fd上的回调 handleRead取读走对端发来的数据
void TcpConnection::handleRead(Timestamp receiveTime)
{
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0) // 有数据到达
{
// 已建立连接的用户有可读事件发生了 调用用户传入的回调操作onMessage shared_from_this就是获取了TcpConnection的智能指针
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0) // 客户端断开
{
handleClose();
}
else // 出错了
{
errno = savedErrno;
LOG_ERROR("TcpConnection::handleRead");
handleError();
}
}
void TcpConnection::handleWrite()
{
if (channel_->isWriting())
{
int savedErrno = 0;
ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);
if (n > 0)
{
outputBuffer_.retrieve(n);//从缓冲区读取reable区域的数据移动readindex下标
if (outputBuffer_.readableBytes() == 0)
{
channel_->disableWriting();
if (writeCompleteCallback_)
{
// TcpConnection对象在其所在的subloop中 向pendingFunctors_中加入回调
loop_->queueInLoop(
std::bind(writeCompleteCallback_, shared_from_this()));
}
if (state_ == kDisconnecting)
{
shutdownInLoop(); // 在当前所属的loop中把TcpConnection删除掉
}
}
}
else
{
LOG_ERROR("TcpConnection::handleWrite");
}
}
else
{
LOG_ERROR("TcpConnection fd=%d is down, no more writing", channel_->fd());
}
}
void TcpConnection::handleClose()
{
LOG_INFO("TcpConnection::handleClose fd=%d state=%d\n", channel_->fd(), (int)state_);
setState(kDisconnected);
channel_->disableAll();
TcpConnectionPtr connPtr(shared_from_this());
connectionCallback_(connPtr); // 连接回调
closeCallback_(connPtr); // 执行关闭连接的回调 执行的是TcpServer::removeConnection回调方法 // must be the last line
}
void TcpConnection::handleError()
{
int optval;
socklen_t optlen = sizeof optval;
int err = 0;
if (::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
{
err = errno;
}
else
{
err = optval;
}
LOG_ERROR("TcpConnection::handleError name:%s - SO_ERROR:%d\n", name_.c_str(), err);
}
// 新增的零拷贝发送函数
void TcpConnection::sendFile(int fileDescriptor, off_t offset, size_t count) {
if (connected()) {
if (loop_->isInLoopThread()) { // 判断当前线程是否是loop循环的线程
sendFileInLoop(fileDescriptor, offset, count);
}else{ // 如果不是,则唤醒运行这个TcpConnection的线程执行Loop循环
loop_->runInLoop(
std::bind(&TcpConnection::sendFileInLoop, shared_from_this(), fileDescriptor, offset, count));
}
} else {
LOG_ERROR("TcpConnection::sendFile - not connected");
}
}
// 在事件循环中执行sendfile
void TcpConnection::sendFileInLoop(int fileDescriptor, off_t offset, size_t count) {
ssize_t bytesSent = 0; // 发送了多少字节数
size_t remaining = count; // 还要多少数据要发送
bool faultError = false; // 错误的标志位
if (state_ == kDisconnecting) { // 表示此时连接已经断开就不需要发送数据了
LOG_ERROR("disconnected, give up writing");
return;
}
// 表示Channel第一次开始写数据或者outputBuffer缓冲区中没有数据
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0) {
bytesSent = sendfile(socket_->fd(), fileDescriptor, &offset, remaining);
if (bytesSent >= 0) {
remaining -= bytesSent;
if (remaining == 0 && writeCompleteCallback_) {
// remaining为0意味着数据正好全部发送完,就不需要给其设置写事件的监听。
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
} else { // bytesSent < 0
if (errno != EWOULDBLOCK) { // 如果是非阻塞没有数据返回错误这个是正常显现等同于EAGAIN,否则就异常情况
LOG_ERROR("TcpConnection::sendFileInLoop");
}
if (errno == EPIPE || errno == ECONNRESET) {
faultError = true;
}
}
}
// 处理剩余数据
if (!faultError && remaining > 0) {
// 继续发送剩余数据
loop_->queueInLoop(
std::bind(&TcpConnection::sendFileInLoop, shared_from_this(), fileDescriptor, offset, remaining));
}
}