天天看点

muduo源码分析之使用封装的Buffer读取数据

muduo是怎么对Buffer进行封装的介绍见:muduo源码分析之Buffer设计

使用Buffer处理数据的输入,而不是原始的

char*

,并且我们要求套接字描述符可读时获取消息到达的时间,因此需要重新添加poll返回的回调类型。

为回调添加时间戳参数

class Channel :boost::noncopyable
{
    public:
    //...
    //添加带时间戳参数的回调函数类型。
        typedef boost::function<void(Timestamp)>ReadEventCallback;
    //...
};
           

在Channel的handleEvent方法中就需要进行一些小小的修改:

void Channel::handleEvent(Timestamp receiveTime)
{

///...
  if (revents_ & (POLLIN | POLLPRI | POLLRDHUP)) {
    if (readCallback_) readCallback_(receiveTime);
  }
//...
}
           

测试上述功能,对s07/test3.cc进行修改,对回调函数添加时间戳参数:

void timeout(muduo::Timestamp receiveTime)
{
    //...
    //...
}
           

当计时器的timerfd超时时,描述符可读,将会调用用户回调,此时我们可以获取时间。

使用Buffer作为输入缓冲

使用封装的Buffer替代原始的char*。

对于一个TcpConnection,将Buffer作为其数据成员,读取接收缓冲的数据,作为输入缓冲,因此,在TcpConnection中添加成员

Buffer inputBuffer_;

class TcpConnection : boost::noncopyable,
                      public boost::enable_shared_from_this<TcpConnection>{
//....
  InetAddress peerAddr_;
  ConnectionCallback connectionCallback_;
  MessageCallback messageCallback_;
  CloseCallback closeCallback_;
  Buffer inputBuffer_;
};
           

在对TcpConnection中handleRead回调(该回调将传递给channel对象中的回调)进行修改。

主要两点:

1.添加了时间戳参数。

2.使用Buffer::readFd()读取数据。

@@ -1,12 +1,14 @@
-void TcpConnection::handleRead()
+void TcpConnection::handleRead(Timestamp receiveTime)
 {
-  char buf[65536];
-  ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
+  int savedErrno = 0;
+  ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
   if (n > 0) {
-    messageCallback_(shared_from_this(), buf, n);//
+    messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
   } else if (n == 0) {
     handleClose();
   } else {
+    errno = savedErrno;
+    LOG_SYSERR << "TcpConnection::handleRead";
     handleError();
   }
 }
           

做了如上改动之后,在

handleRead()

函数中调用

messageCallback_

使用

Buffer

Timestamp

作为参数,因此,在测试代码中,重写

onMessage

函数

void onMessage(const muduo::TcpConnectionPtr& conn,
               muduo::Buffer* buf,
               muduo::Timestamp receiveTime)
{
  printf("onMessage(): received %zd bytes from connection [%s] at %s\n",
         buf->readableBytes(),
         conn->name().c_str(),
         receiveTime.toFormattedString().c_str());

  printf("onMessage(): [%s]\n", buf->retrieveAsString().c_str());
}
           

至于

Buffer::readFd()

在muduo源码分析之Buffer设计已经说明,使用了一个栈上变量来处理大数据量的情形。

这样做的好处有:

1.使用scatter/gather IO并且一部分缓冲取自stack,这样输入缓冲足够大,通常一次

readv

调用就能取完全部数据。

2.采用电平触发,没有通过EAGAIN反复调用read,这样做高效的,因为每次读取数据只需要一次系统调用。

参考

Linux多线程服务端编程使用muduoC++网络库

继续阅读