TCP长链接、短链接(心跳检测)

前言:

     上午发现一个神仙博客! 写的东西很深刻,通俗易懂,转载一下吧~ react

转载自:blog.csdn.net/qq_41453285…git

感谢~github

1、TCP链接的相关说明

  • ①使用TCP协议时,会在客户端和服务器之间创建一条虚拟的信道,这条虚拟信道就是指链接,而建议这条链接须要3次握手,拆毁这条链接须要4次挥手,可见,咱们创建这条链接是有成本的,这个成本就是效率成本,简单点说就是时间成 本,你要想发送一段数据,必须先3次握手(来往3个包),而后才能发送数据,发送完了,你须要4次挥手(来往4个包) 来断开这个链接
  • **②CPU资源成本,**三次握手和4次挥手和发送数据都是从网卡里发送出去和接收的,还有其他的设备,好比防火墙, 路由器等等,站在操做系统内核的角度来说,若是咱们是一个高并发系统的话,若是大量的数据包都经历过这么一个过 程,那是很耗CPU的。
  • **③每一个socket是须要耗费系统缓存的,**好比系统提供了一些接口设置socket缓存的,好比:
    • /proc/sys/net/ipv4/tcp_rmem
    • /proc/sys/net/ipv4/tcp_wmem
    • /proc/sys/net/ipv4/tcp_mem

  • 由于TCP的可靠传输,因此咱们有大量的应用程序使用TCP协议做为通讯,可是每一个应用由于产品功能的缘由,对TCP的使用是不同的,好比即时聊天系统(微信,钉钉,探探)

2、TCP长链接、TCP短链接

TCP短链接

  • **概念:**以下图所示,客户端与服务器创建链接开始通讯,一次/指定次数通讯结束以后就断开本次TCP链接,当下次再次通讯时,再次创建TCP的连接
  • **优势:**不长期占用服务器的内存,那么服务器能处理的链接数量是比较多的
  • 缺点:
    • 由于等到要发送数据或者获取资源时,才去请求创建链接发送数据,不然就是端开链接的,那么**若是服务器要想往客户端发送数据时怎么办?**凉伴,没有任何办法,或者要等到下一次要请求数据时,才发送,好比咱们采用轮询(30秒或者更长)拉取消息, 那么服务器与客户端通讯的实时性就丧失了
    • 客户端采用轮询来实时获取信息,或者说大量的客户端使用短链接的方式通讯,那么就浪费了大量的CPU和带宽资源用于创建连 接和释放链接,存在资源浪费,甚至是没法创建链接。好比经典的http长轮询(微信网页客户端端)

TCP长链接

  • **概念:**以下图所示,TCP与服务器创建链接以后一直处于链接状态,直到最后再也不须要服务的时候才断开链接
  • 优势:
    • 传输数据快
    • 服务器可以主动第一时间传输数据到客户端
  • 缺点:
    • 由于客户端与服务器一直保持这种链接,那么在高并发分布式集群系统中客户端数量会愈来愈多,占 用不少的系统资源
    • TCP自己是一种有状态的数据,在高并发分布式系统会致使后台设计比较难作

3、TCP的keepalive机制(保活机制)

  • “TCP保活机制”详情参阅blog.csdn.net/qq_41453285…
  • TCP实现了一种保活机制,主要的设计初衷是:
    • 客户端和服务器须要了解何时终止进程或者与对方断开链接
    • 而在另 一些状况下,虽然应用进程之间没有任何数据交换,但仍然须要经过链接保持一个最小的数据流,会很是消耗资源。保活机制可用来检测对方并在适时关闭链接
  • **主要的工做原理就是:**探测方会在本身一端设计一个计时器,当计时器被触发以后,向对方发送一个探测报文。若是对端给本身回送一个ACK,那么就表明对方仍存活;若是在指定的时间内没有给本身回送ACK,那么就确认对方已经断开链接,从而断开本次TCP链接

Linux的相关内核参数

  • **tcp_keepalive_time:**单位秒,表示发送探测报文以前的连接空闲时间,默认为7200
  • **tcp_keepalive_intvl:**单位秒,表示两次探测报文发送的时间间隔,默认为75
  • **tcp_keepalive_probes:**表示探测的次数,默认为9

Posix套接字选项

  • 上面介绍的三个内核参数,在Linux编程中有对应的socket选项可使用编程

  • 关于setsockopt()可参阅:blog.csdn.net/qq_41453285…缓存

    int keepalive_time = 30;setsockopt(incomingsock, IPPROTO_TCP,TCP_KEEPIDLE,(void*)(&keepalive_time),(socklen_t)sizeof(keepalive_time)); int keepalive_intvl = 3;setsockopt(incomingsock, IPPROTO_TCP,TCP_KEEPINTVL,(void*)(&keepalive_intvl),(socklen_t)sizeof(keepalive_intvl)); int keepalive_probes= 3;setsockopt(incomingsock, IPPROTO_TCP,TCP_KEEPCNT,(void*)(&keepalive_probes),(socklen_t)sizeof(keepalive_probes));服务器

4、TCP长链接设计

为何须要长链接

  • **服务器要主动发消息给客户端:**若是没有一个链接存在的话,服务器是永远不能主动地找到客户端的,那也就没有办法及时发送消息给客户端
  • **客户端和服务器间频繁地通讯:**若是是短链接,每次都须要创建链接才能发送消息,若是并发量稍高,可能就会出现大量的TIME_WAIT状态的socket出现,也即后续创建不了链接
  • **业务须要,好比客户端掉线时服务器须要作一些处理:**好比清空他的缓存或者其它资源或者其它业务含义,好比QQ头像显示离线

TCP长链接设计时须要考虑到的问题

  • **默认的tcp keep-alive超时时间太长:**默认是7200秒,也就是2个小时,固然是能够修改的
  • **socket proxy会让tcp keep-alive失效:**全部的proxy应用只能转发TCP的应用数据,作不到转发TCP协议内部的包
  • **移动网络须要信令保活:**对于手机等智能终端来说,他们使用移动网络来上网的,而运营商们为了节约信道资源,会尝试关闭超过60秒或者 45秒的没有发送数据的socket
  • 下面用心跳检测机制来解决这些问题

5、心跳检测

  • 心跳检测就是用户在应用层本身实现的一种机制,用来模仿TCP的keepalive机制,在指定的时间内会向对方放一个心跳检测包,若是在指定的时间内给本身回送了应答,那么就不关闭TCP的链接;若是在指定的时间内没有给本身回送应答,那么就作相应的处理(例如说的关闭本次TCP的连接)

Netyy中的实现

编码实现与主要原理

  • Github源码连接:github.com/dongyusheng…
  • 代码:
    • 代码就是用红黑树来管理定时器
    • 总体框架采用Reactor模式实现
    • 有相应的客户端与服务端

  • 大体思路为:
    • 咱们用一棵红黑树管理全部事件节点,其中key为该事件套接字的超时时间,value为套接字
    • 调用wpoll_wait()处理全部事件,其中其最后一个参数为超时时间,必须设置为整棵红黑树中超时时间最短的那个节点的值
    • 当epoll_wait()返回以后,将时间与节点的时间进行比较,若是超时了,那么就作相应的处理

代码解析

  • 下面主要介绍以“服务端”为第一视角,查看其如何对客户端进行心跳检测的
  • ngx_reactor.c的ngx_reactor_loop()函数:
    • Reactor的主要中心处理函数ngx_reactor_loop()中会在poll_wait()调用以前获取一下当前时间
    • 而后进行epoll_wait(),其最后一个参数为红黑树中超时时间最短的那个节点的时间值
    • epoll_wait()返回以后,先更新一下当前时间(全局变量ngx_current_msec)
    • 最后再次获取当前时间,而后与以前的旧的“当前时间”进行,相减获得差值,若是超时了,那么就调用ngx_event_expire_timers()函数

  • ngx_reactor.c的ngx_event_expire_timers()函数:
    • 该函数也就是该封装事件的what标志|上一个NGX_EVENT_TIMEOUT,将该事件置位超时了的
    • 而后会调用该事件的回调函数

  • 在上面的回调函数中,咱们为其绑定的回调函数为ngx_server.c中的client_handler()函数:
    • 该函数会判断该事件的what标志是否有NGX_EVENT_TIMEOUT标志,若是有就调用timeout_cb()处理函数
    • timeout_cb()函数或判断delta是否超过了KEEPALIVE_INTVAL_MSEC时间,而且超时了3次(KEEPALIVE_INTVAL_PROBES),若是是,那么就调用close()关闭客户端的套接字

  • 上面定义的宏以下所示

相关文章
相关标签/搜索