chargen简介
chargen(Character Generator Protocol)是指在TCP连接建立后,服务器不断传送任意的字符到客户端,直到客户端关闭连接。
它生成数据的逻辑如下:
for (int i = 33; i < 127; ++i)
{
line.push_back(char(i));
}
line += line;
for (size_t i = 0; i < 127-33; ++i)
{
message_ += line.substr(i, 72) + '\n';
}
其输出数据格式类似下图,每行有72个字符,完整的一组输出数据有95行:
在介绍muduo chargen服务示例前,我们需要了解muduo::TcpConnection是如何发送数据的。
TcpConnection发送数据
一、代码的改动
之前几篇博客介绍了TcpConnection关于连接建立和连接断开的处理,在此基础上,TcpConnection的接口中新增了两个函数send()和shutdown(),这两个函数都可以跨线程调用。其内部实现增加了sendInLoop()和shutdownInLoop()两个成员函数,并使用Buffer(muduo对应用层缓冲区的封装)作为输出缓冲区。
TcpConnection的状态也增加到了四个:
// 连接断开,连接中,已连接,断开连接中
enum StateE { kDisconnected, kConnecting, kConnected, kDisconnecting };
TcpConnection接口的变化:
// send()有多种重载
void send(const void* message, size_t len);
void send(const StringPiece& message);
void shutdown();
......
void sendInLoop(const StringPiece& message);
void sendInLoop(const void* message, size_t len);
void shutdownInLoop();
Buffer outputBuffer_; // 应用层发送缓冲区
发送数据会用到Channel的WriteCallback,且由于muduo采用Level Trigger(水平触发),因此我们只在需要的时候才关注可写事件,否则会造成busy loop。
Channel.h的改动如下:
void enableReading() { events_ |= kReadEvent; update(); }
void enableWriting() { events_ |= kWriteEvent; update(); }
void disableWriting() { events_ &= ~kWriteEvent; update(); }
void disableAll() { events_ = kNoneEvent; update(); }
bool isWriting() const { return events_ & kWriteEvent; }
二、源码分析
我们首先来看发送数据的TcpConnection::send()函数,它是线程安全的,可以跨线程调用。如果在非IO线程中调用,它会把message复制一份,传给IO线程中的sendInLoop()来发送。
代码片段1:TcpConnection::send()
文件名:TcpConnection.cc
void TcpConnection::send(const std::string& message)
{
if (state_ == kConnected)
{
if (loop_->isInLoopThread())
{
sendInLoop(message);
}
else
{
loop_->runInLoop(
boost::bind(