muduo accept连接、断开连接、发送数据的实现

目录

一、TcpServer接受新连接

1、TcpServer

2、TcpConnection

二、TcpConnection断开连接

三、TcpConnection发送数据

四、如何限制服务器的最大并发连接数

五、如何踢掉空闲连接

六、发送数据时的流量控制

七、为什么TcpConnection对象是用shared_ptr来管理对象

八、TcpConnection的id如何存放,当从client connection收到数据,如何得知其id 


muduo是陈硕个人使用C++开发的一款网络库,代码写的很有学习价值,总结的内容来自书籍《Linux 多线程服务器端编程》,也是由陈硕编写,可以配合github代码一起使用。

muduo github网址:https://github.com/chenshuo/muduo

一、TcpServer接受新连接

1、TcpServer

TcpServer新建连接的相关函数调用顺序见下图

 

Channel::handleEvent()的触发条件是listening socket可读,表明有新连接到达,TcpServer会为新连接创建对应的TcpConnection对象。在新连接到达时,Acceptor会回调newConnection(),后者会创建TcpConnection对象conn,把它加入connectionMap,设置好callback,再调用conn->connectEstablished(),其中会调用用户提供的ConnectionCallback。

2、TcpConnection

是muduo最核心也是最复杂的类,TcpConnection使用Channel获得socket上的IO事件,它会自己处理writable事件,而把readable事件通过MessageCallback传送给用户,TcpConnection拥有Tcp socket,析构函数会close(fd)。

二、TcpConnection断开连接

muduo可用被动关闭,即对方先关闭连接,本地read()返回0,触发关闭逻辑。也可以调用TcpConnection的forceClose()成员函数,用于主动关闭连接,后者会调用handleClose()函数,函数调用的流程如下图所示:

 

 

TcpServer会向TcpConnection注册CloseCallback,用于接收连接断开的消息。handleClose()的主要功能是调用closeCallback_,这个回调绑定到TcpServer::removeConnection(),后者会把conn从ConnectionMap里删除。

TcpConnection并没有提供close()函数,而是使用shutdown()函数,muduo把主动关闭连接这件事分成了两步:如果要主动关闭连接,先关闭本地的“写”端,等到对方关闭连接后,再关闭本地“读”端,这种half-close主要是为了防止关闭连接时,对方还在发送数据。

三、TcpConnection发送数据

用户通过调用TcpConnection::send()来发送数据,由于消息的读写都必须在EventLoop的同一个线程,因此必须确保线程安全性,因此在send的时候,会先检测是否在当前的IO线程,如果是就直接调用sendInLoop(),如果不在一个线程的话,会把messsage复制一份,传给IO线程中的sendInLoop()来发送。

sendInLoop()会尝试直接发送数据,如果一次发送完,就不会启用WriteCallback,如果只发送了部分数据,则把剩余的数据放入outputBuffer_,并开始关注writable事件,然后在handleWrite()中发送剩余的数据。

四、如何限制服务器的最大并发连接数

准备一个空闲的文件描述符,当本进程使用的文件描述符已经达到上限时(Acceptor的handleRead()执行accept,返回errno为EMFILE),先关闭这个空闲的文件描述符,获得一个文件描述符的名额,再accept()拿到新socket连接的描述符,随后立即close()掉,这样就优雅的断开了客户端连接,最后在重新打开一个空闲文件描述符,把“坑”占住,以备再次出现这种情况使用。

五、如何踢掉空闲连接

使用timeing wheel,大致的数据结构为:8个桶(hash set)组成的循环队列(超时时间8s)。第一个桶放1秒之后将要超时的连接,第二个桶放2秒之后将要超时的连接以此类推,每个连接一收到数据就把自己放入第8个桶,然后在每秒的timer里把第一个桶里的连接断开,把这个空桶挪到队尾。这样不用每次检查所有的连接。

连接超时被踢掉的过程:

 

连接刷新:

具体实现时,桶里放的不是连接,而是Entry struct,每个Entry包含TcpConnection的weak_ptr,当Entry的引用计数递减到0,说明它不存在于任何一个盒子里,就会连接超时,Entry的析构函数就会断开连接。

六、发送数据时的流量控制

muduo设置了WriteCompleteCallback()和HighWaterCallback()作为“低水位回调”和“高水位回调”,当发送缓冲区清空时调用WriteCompleteCallback(),当输出缓冲区长度超过用户规定大小,就调用HighWaterCallback()。

七、为什么TcpConnection对象是用shared_ptr来管理对象

因为从TCP连接收到一个request开始,程序处理这个连接可能要花费一段时间,为了确认socket连接在处理request期间是否已经关闭了,需要持有TcpConnection对象的引用计数。

八、TcpConnection的id如何存放,当从client connection收到数据,如何得知其id 

TcpConnection的id使用std::map<int, TcpConnectionPtr> clientConns_保存id到client connection的映射,muduo的TcpConnection还用context功能,每个TcpConnection都有一个boost::any成员,可以用于保存与connection绑定的任意数据,比如连接id,连接最后数据到达时间,连接所代表的用户名等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值