Muduo网络库学习之Buffer读取与发送数据

时间:2024-05-18 17:46:21

Buffer 存在的意义

Buffer是Tcp网络编程中必不可少的东西。为什么必须要有要有Buffer。举个简单的例子,先说输出buffer,假如服务器要向客户端发送100KB数据,受到了某些因素的影响,一次性只能发送80KB,那剩余的20KB怎么办,总不可能把它丢掉吧。这时候Buffer的作用就出来了,它将缓存没发送完的20KB数据(把数据按时间顺序存好,有点像队列,先入先出),然后再次注册socket的可写事件,等到可写事件满足的时候再发送剩余的20KB数据。输入Buffer,TCP 是一个无边界的字节流协议,接收方必须要处理“收到的数据尚不构成一条完整的消息”和“一次收到两条消息的数据”等等情况。一个常见的场景是,发送方 send 了两条 10k 字节的消息(共 20k),接收方收到数据的情况可能是:

一次性收到 20k 数据
分两次收到,第一次 5k,第二次 15k
分两次收到,第一次 15k,第二次 5k
分两次收到,第一次 10k,第二次 10k
分三次收到,第一次 6k,第二次 8k,第三次 6k
其他任何可能
网络库在处理“socket 可读”事件的时候,必须一次性把 socket 里的数据读完(从操作系统 buffer 搬到应用层 buffer),否则会反复触发 POLLIN 事件,造成 busy-loop。
所以在TCP网络编程中,输入输出buffer是必须的。

Buffer的设计涉及到的东西太多,读者如果想去探究原理,可以去看陈硕大佬的 《linux 多线程 服务端编程》,这里只讲使用,不讲原理。

TCPconnection 类

上一节中提到了TcpConnetion类但是没有细讲。听名字,这是一个关于Tcp连接的类。不错,每一个TcpConnetion就代表一个链接,这就意味着有多少个服务器同时与客户端发起长连接就有多少个TcpConnetion对象。它是muduo里面最大的一个类,头文件和资源文件有450多行,muduo总共也就才3000多行。TcpConnetion是由隶属于TcpServer。TcpConnetion使用Channel来获得socket上的IO事件,它自己会处理可写事件,可读事件通过MessageCallback传给客户。它拥有TCP socket ,并且会在析构的时候close(fd);

Muduo网络库学习之Buffer读取与发送数据
当socket上有可读事件的时候会调用TcpConnetion::handleRead(),在此函数里面调用buffer的readFd()函数 然后把输入buffer传递给用户,用户可以实现自己的业务逻辑。
Muduo网络库学习之Buffer读取与发送数据
Buffer读取数据时兼顾了内存使用量和效率。实现如上。

TcoConnection 发送数据

发送数据和接收数据差不多,用户要发送数据的时候,回调用TcoConnection::send(),如果在非IO线程调用,会把数据复制一份到IO线程中来发送。
Muduo网络库学习之Buffer读取与发送数据
Muduo网络库学习之Buffer读取与发送数据
如果一次发送不完,会把剩余的数据缓存到缓冲区,然后监听IO可写事件,当可写时间满足的时候再通过Channel会调用TcoConnection::handleWrite(),一但发送完毕,应该立即停止观察writeable事件,因为muduo是level trigger(水平触发),如果一直观察会导致writeable事件一直活跃。
Muduo网络库学习之Buffer读取与发送数据

到此数据的接收发送就讲解完毕了