客户端与服务器建立连接,就有了一个TcpConnection对象,该TcpConnection对象包含客户端套接字文件描述符,对应的channel,以及所属的EventLoop对象。上述三个TcpConnection对象非常重要的数据成员,他们标识了这个TcpConnection对象的身份。TcpConnection对象还拥有inputBuffer_和outputBuffer_两个重要数据成员,用于接受数据和发送数据。
当事件循环收到可读事件的到来时,会调用fd对应的channel的读事件的回调函数ReadCallback_,这个回调函数在TcpConnection对象建立时便绑定在TcpConnection::handleRead函数上(见TcpConnection类的构造函数),因此可读事件触发TcpConnection::handleRead函数,如下:
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
int savedErrno = 0;
//将读到的数据放入inputBuffer_中
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno); //接受数据
if (n > 0) //读取n字节数据
{
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0) //对端关闭连接
{
handleClose();
}
else
{
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
ssize_t Buffer::readFd(int fd, int* savedErrno)
{
// saved an ioctl()/FIONREAD call to tell how much to read
char extrabuf[65536]; //64k
struct iovec vec[2];
const size_t writable = writableBytes();
vec[0].iov_base = begin()+writerIndex_;
vec[0].iov_len = writable;
vec[1].iov_base = extrabuf;
vec[1].iov_len = sizeof extrabuf;
// when there is enough space in this buffer, don't read into extrabuf.
// when extrabuf is used, we read 128k-1 bytes at most.
const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;
const ssize_t n = sockets::readv(fd, vec, iovcnt);
if (n < 0)
{
*savedErrno = errno;
}
else if (implicit_cast<size_t>(n) <= writable) //还有剩余空间
{
writerIndex_ += n;
}
else //buffer中readable空间不够
{
writerIndex_ = buffer_.size();
append(extrabuf, n - writable);
}
// if (n == writable + sizeof extrabuf)
// {
// goto line_30;
// }
return n;
}
通过客户端fd读入数据,并将数据放入输入缓冲inputBuffer_中,当读取的字节n>0时,说明成功读取到了数据,因此调用用户回调函数来处理inputBuffer_中的数据。
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
发送数据是通过TcpConnection::send()函数和TcpConnection::handleWrite()函数进行。send函数如下:
void TcpConnection::send(const void* data, int len)
{
send(StringPiece(static_cast<const char*>(data), len));
}
void TcpConnection::send(const StringPiece& message)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread())
{
sendInLoop(message);
}
else
{
void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;
loop_->runInLoop(
std::bind(fp,
this, // FIXME
message.as_string()));
//std::forward(message)));
}
}
}
// FIXME efficiency!!!
void TcpConnection::send(Buffer* buf)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread())
{
sendInLoop(buf->peek(), buf->readableBytes());
buf->retrieveAll();
}
else
{
void (TcpConnection::*fp)(const StringPiece& message) = &TcpConnection::sendInLoop;
loop_->runInLoop(
std::bind(fp,
this, // FIXME
buf->retrieveAllAsString()));
//std::forward(message)));
}
}
}
void TcpConnection::sendInLoop(const StringPiece& message)
{
sendInLoop(message.data(), message.size());
}
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
//通道没有关注可写事件,并且发送缓冲区没有数据,直接write
if (!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
{
nwrote = sockets::write(channel_->fd(), data, len);
if (nwrote >= 0)
{
remaining = len - nwrote;
if (remaining == 0 && writeCompleteCallback_)
{
//写完了,回调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);
//没有错误,并且还有未写完的数据,(说明内核缓冲区已满要将未写完的数据添加到output buffer中)
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(static_cast<const char*>(data)+nwrote, remaining);
if (!channel_->isWriting())
{
channel_->enableWriting(); //关注POLLOUT事件
}
}
}
读上述代码可知,数据的发送都是通过sendInLoop函数在I/O线程中进行。发送数据的逻辑是:
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(); //停止关注POLLOUT事件,以免出现busy loop
if (writeCompleteCallback_) //回调writeCompleteCallback_
{
loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
}
if (state_ == kDisconnecting) //发送缓冲区已经清空,并且连接状态为kDisconnecting,要关闭连接
{
shutdownInLoop(); //关闭连接
}
}
}
else
{
LOG_SYSERR << "TcpConnection::handleWrite";
// if (state_ == kDisconnecting)
// {
// shutdownInLoop();
// }
}
}
else
{
LOG_TRACE << "Connection fd = " << channel_->fd()
<< " is down, no more writing";
}
}