muduo Buffer类的设计与使用——读书笔记

muduo Buffer类的设计与使用

muduo的IO模型

event loopnon-blocking网络编程的核心,non-blocking总是和IO multiplexing一起使用,原因如下

  • 不要使用轮询的方式来检查某个non-blockingIO操作是否完成,这样浪费CPU
  • IO multiplexing一般不能和blocking IO一起使用,因为blocking IOread()/write()/accept()/connect()都有可能阻塞当前线程,这样线程就没有办法处理其他socket上的IO事件了

为什么non-blocking网络编程中应用层buffer是必需的

non-blockingIO的核心思想是避免阻塞在read()write()或其他IO系统调用上,这样可以最大限度地复用thread-of-control,让一个线程能服务于多个socket连接IO线程只能阻塞在IO multiplexing函数上,如select/poll/epoll_wait,这样一来,应用层的缓冲是必须的,每个TCPsocket都要个statefulinput bufferoutput buffer

TcpConnection必须要有output buffer,考虑一个常见场景:程序想通过TCP连接发送100KB的数据,但是在write()调用中,操作系统只接受了80KB,这时肯定不想在原地等待,因为不知道等待多久,程序应该尽快交出控制权,返回event loop,这种情况下,剩余的20KB应该怎么办?

对于应用程序而言,它只管生成数据,它不应该关心到底数据是一次性发送还是分成几次发送,这些应该由网络库接管,程序只要调用TcpConnection::send()就可以了,网络库应该接管剩余的20KB,把它保存在该TCPconnectionoutput buffer里面,然后注册POLLOUT事件,一旦socket可写就立刻发送数据,当然,第二次write()也不一定能完全写入20KB,如果还有剩余,网络库应该继续关注POLLOUT事件,如果写完了20KB,网络库应该关闭POLLOUT

如果程序又写入了50KB,这时output buffer里面还有待发送的20KB数据,那么网络库不应该直接调用write(),而应该把这50KB数据append在20KB数据之后,等socket变得可写的时候再一并写入。

如果output buffer里面还有待发送的数据,而程序又想关闭连接,那么这时候网络库不能立刻关闭连接,而要等数据发送完毕。

综上,要让程序在write操作上不阻塞,网络库必需要给每个TCPconnection配置output buffer

TcpConnection必需要有input buffer,TCP是一个无边界的字节流协议,接收方必须要处理”收到的数据尚不构成一条完整的消息“和”一次收到两条消息的数据“等情况。一个常见的场景是,发送方send()了两条1KB的消息(共2KB),接收方收到数据的情况可能是:

  • 一次性收到2KB数据
  • 分两次收到,第一次600B,第二次1400B
  • 等等

网络库在处理”socket可读”事件的时候,必须一次性把socket里的数据读完(从操作系统buffer搬到应用层buffer),否则会反复触发POLLIN事件,造成busy-loop,那么网络库必然要应对“数据不完整”的情况,收到的数据先放到input buffer里面,等构成一条完整的消息再通知程序的业务逻辑。所以,在TCP网络编程中,网络库必须要给每个TCPconnection配置input buffer

muduo EventLoop采用的是epoll()level trigger,而不是edge trigger。一是为了与传统的poll()兼容,因为在文件描述符数目较少,活动文件描述符比例较高时,epoll不见得比poll高效,必要时可以在进程启动时切换Poller,二是level trigger编程更容易。

所有muduo中的IO都是带缓冲的IO(buffered IO),你不会自己去read()write()某个socket,只会操作TcpConnectioninput bufferoutput buffer。更确切地说,是在onMessage()回调里读取input buffer;调用TcpConnection::send()来间接操作output buffer,一般不会直接操作output buffer

7.4.3 Buffer的功能需求

muduo Buffer的设计要点:

  • 对外表现为一块连续的内存(char* p, int len),方便客户代码的编写。
  • size()可以自动增长,以适应不同大小的消息。
  • 内部以std::vector<char>来保存数据,并提供相应的访问函数。

TcpConnection会有两个Buffer成员,input bufferoutput buffer

  • input bufferTcpConnectionsocket读取数据,然后写入input buffer;客户代码从input buffer读取
  • output buffer,客户代码会把数据写入output bufferTcpConnectionoutput buffer读取数据并写入socket

具体做法,在栈上准备一个65536字节的extrabuf,然后利用readv()来读取数据,iovec有两块,第一块指向muduo Buffer中的writable字节,另一块指向栈上extrabuf。如果这样读入的数据不多,那么全部都读到了Buffer中去了;如果长度超过Bufferwritable字节数,就会读到栈上的

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值