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()關閉客戶端的套接字

  • 上面定義的宏以下所示

相關文章
相關標籤/搜索