TCP的定時器系列 — 保活定時器 Keepalive
主要內容:保活定時器的實現,TCP_USER_TIMEOUT選項的實現。
核心版本:3.15.2
原理
HTTP有Keepalive功能,TCP也有Keepalive功能,雖然都叫Keepalive,但是它們的目的卻是不一樣的。
為了說明這一點,先來看下長連線和短連線的定義。
連線的“長短”是什麼?
短連線:建立一條連線,傳輸一個請求,馬上關閉連線。
長連線:建立一條連線,傳輸一個請求,過會兒,又傳輸若干個請求,最後再關閉連線。
長連線的好處是顯而易見的,多個請求可以複用一條連線,省去連線建立和釋放的時間開銷和系統呼叫,
但也意味著伺服器的一部分資源會被長時間佔用著。
HTTP的Keepalive,顧名思義,目的在於延長連線的時間,以便在同一條連線中傳輸多個HTTP請求。
HTTP伺服器一般會提供Keepalive Timeout引數,用來決定連線保持多久,什麼時候關閉連線。
當連線使用了Keepalive功能時,對於客戶端傳送過來的一個請求,伺服器端會發送一個響應,然後開始計時,
如果經過Timeout時間後,客戶端沒有再發送請求過來,伺服器端就把連線關了,不再保持連線了。
TCP的Keepalive,是掛羊頭賣狗肉的,目的在於看看對方有沒有發生異常,如果有異常就及時關閉連線。
當傳輸雙方不主動關閉連線時,就算雙方沒有交換任何資料,連線也是一直有效的。
如果這個時候對端、中間網路出現異常而導致連線不可用,本端如何得知這一資訊呢?
答案就是保活定時器。它每隔一段時間會超時,超時後會檢查連線是否空閒太久了,如果空閒的時間超過
了設定時間,就會發送探測報文。然後通過對端是否響應、響應是否符合預期,來判斷對端是否正常,
如果不正常,就主動關閉連線,而不用等待HTTP層的關閉了。
當伺服器傳送探測報文時,客戶端可能處於4種不同的情況:仍然正常執行、已經崩潰、已經崩潰並重啟了、
由於中間鏈路問題不可達。在不同的情況下,伺服器會得到不一樣的反饋。
(1) 客戶主機依然正常執行,並且從伺服器端可達
客戶端的TCP響應正常,從而伺服器端知道對方是正常的。保活定時器會在兩小時以後繼續觸發。
(2) 客戶主機已經崩潰,並且關閉或者正在重新啟動
客戶端的TCP沒有響應,伺服器沒有收到對探測包的響應,此後每隔75s傳送探測報文,一共傳送9次。
socket函式會返回-1,errno設定為ETIMEDOUT,表示連線超時。
(3) 客戶主機已經崩潰,並且重新啟動了
客戶端的TCP傳送RST,伺服器端收到後關閉此連線。
socket函式會返回-1,errno設定為ECONNRESET,表示連線被對端復位了。
(4) 客戶主機依然正常執行,但是從伺服器不可達
雙方的反應和第二種是一樣的,因為伺服器不能區分對端異常與中間鏈路異常。
socket函式會返回-1,errno設定為EHOSTUNREACH,表示對端不可達。
選項
核心預設並不使用TCP Keepalive功能,除非使用者設定了SO_KEEPALIVE選項。
有兩種方式可以自行調整保活定時器的引數:一種是修改TCP引數,一種是使用TCP層選項。
(1) TCP引數
tcp_keepalive_time
最後一次資料交換到TCP傳送第一個保活探測報文的時間,即允許連線空閒的時間,預設為7200s。
tcp_keepalive_intvl
保活探測報文的重傳時間,預設為75s。
tcp_keepalive_probes
保活探測報文的傳送次數,預設為9次。
Q:一次完整的保活探測需要花費多長時間?
A:tcp_keepalive_time + tcp_keepalive_intvl * tcp_keepalive_probes,預設值為7875s。
如果覺得兩個多小時太長了,可以自行調整上述引數。
(2) TCP層選項
TCP_KEEPIDLE:含義同tcp_keepalive_time。
TCP_KEEPINTVL:含義同tcp_keepalive_intvl。
TCP_KEEPCNT:含義同tcp_keepalive_probes。
Q:既然有了TCP引數可供調整,為什麼還增加了上述的TCP層選項?
A:TCP引數是面向本機的所有TCP連線,一旦調整了,對所有的連線都有效。
而TCP層選項是面向一條連線的,一旦調整了,只對本條連線有效。
啟用
在連線建立後,可以通過設定SO_KEEPALIVE選項,來啟用保活定時器。
int keepalive = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
- int sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval,
- unsigned int optlen)
- {
- ...
- case SO_KEEPALIVE:
- #ifdef CONFIG_INET
- if (sk->sk_protocol == IPPROTO_TCP && sk->sk_type == SOCK_STREAM)
- tcp_set_keepalive(sk, valbool); /* 啟用或刪除保活定時器 */
- #endif
- sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool); /* 設定或取消SOCK_KEEPOPEN標誌位 */
- break;
- ...
- }
- static inline void sock_valbool_flag (struct sock *sk, int bit, int valbool)
- {
- if (valbool)
- sock_set_flag(sk, bit);
- else
- sock_reset_flag(sk, bit);
- }
- void tcp_set_keepalive(struct sock *sk, int val)
- {
- /* 不在以下兩個狀態設定保活定時器:
- * TCP_CLOSE:sk_timer用作FIN_WAIT2定時器
- * TCP_LISTEN:sk_timer用作SYNACK重傳定時器
- */
- if ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))
- return;
- /* 如果SO_KEEPALIVE選項值為1,且此前沒有設定SOCK_KEEPOPEN標誌,
- * 則啟用sk_timer,用作保活定時器。
- */
- if (val && !sock_flag(sk, SOCK_KEEPOPEN))
- inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tcp_sk(sk)));
- elseif (!val)
- /* 如果SO_KEEPALIVE選項值為0,則刪除保活定時器 */
- inet_csk_delete_keepalive_timer(sk);
- }
- /* 保活定時器的超時時間 */
- static inline int keepalive_time_when(const struct tcp_sock *tp)
- {
- return tp->keepalive_time ? : sysctl_tcp_keepalive_time;
- }
- void inet_csk_reset_keepalive_timer (struc sock *sk, unsigned long len)
- {
- sk_reset_timer(sk, &sk->sk_timer, jiffies + len);
- }
可以使用TCP層選項來動態調整保活定時器的引數。
int keepidle = 600;
int keepintvl = 10;
int keepcnt = 6;
setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
- struct tcp_sock {
- ...
- /* 最後一次接收到ACK的時間 */
- u32 rcv_tstamp; /* timestamp of last received ACK (for keepalives) */
- ...
- /* time before keep alive takes place, 空閒多久後才傳送探測報文 */
- unsigned int keepalive_time;
- /* time iterval between keep alive probes */
- unsigned int keepalive_intvl; /* 探測報文之間的時間間隔 */
- /* num of allowed keep alive probes */
- u8 keepalive_probes; /* 探測報文的傳送次數 */
- ...
- struct {
- ...
- /* 最後一次接收到帶負荷的報文的時間 */
- __u32 lrcvtime; /* timestamp of last received data packet */
- ...
- } icsk_ack;
- ...
- };
- #define TCP_KEEPIDLE 4/* Start Keepalives after this period */
- #define TCP_KEEPINTVL 5/* Interval between keepalives */
- #define TCP_KEEPCNT 6/* Number of keepalives before death */
- #define MAX_TCP_KEEPIDLE 32767
- #define MAX_TCP_KEEPINTVL 32767
- #define MAX_TCP_KEEPCNT 127
- staticint do_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
- unsigned int optlen)
- {
- ...
- case TCP_KEEPIDLE:
- if (val < 1 || val > MAX_TCP_KEEPIDLE)
- err = -EINVAL;
- else {
- tp->keepalive_time = val * HZ; /* 設定新的空閒時間 */
- /* 如果有使用SO_KEEPALIVE選項,連線處於非監聽非結束的狀態。
- * 這個時候保活定時器已經在計時了,這裡設定新的超時時間。
- */
- if (sock_flag(sk, SOCK_KEEPOPEN) &&
- !((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
- u32 elapsed = keepalive_time_elapsed(tp); /* 連線已經經歷的空閒時間 */
- if (tp->keepalive_time > elapsed)
- elapsed = tp->keepalive_time - elapsed; /* 接著等待的時間,然後超時 */
-
相關推薦
TCP的定時器系列 — 保活定時器 Keepalive
主要內容:保活定時器的實現,TCP_USER_TIMEOUT選項的實現。 核心版本:3.15.2 原理 HTTP有Keepalive功能,TCP也有Keepalive功能,雖然都叫Keepalive,但是它們的目的卻是不一樣的。 為了說
TCP的定時器系列 — 保活定時器
主要內容:保活定時器的實現,TCP_USER_TIMEOUT選項的實現。 核心版本:3.15.2 原理 HTTP有Keepalive功能,TCP也有Keepalive功能,雖然都叫Keepalive,但是它們的目的卻是不一樣的。 為了說明這一點,先來看下長連線和短連線
TCP/IP詳解--幾類定時器的作用(重傳 保活定時器)
與資料鏈路層的ARQ協議相類似,TCP使用超時重發的重傳機制。即:TCP每傳送一個報文段,就對此報文段設定一個超時重傳計時器。此計時器設定的超時重傳時間RTO(Retransmission Time-Out)應當略大於TCP報文段的平均往返時延RTT,一般可取RTO=2R
TCP堅持定時器,TCP保活定時器
TCP一共有四個主要的定時器,前面已經講到了超時定時器,2MSL定時器,(MSL是指任何報文段被丟棄前在網路內的最長存活時間)另外的兩個是: 堅持定時器 1, 堅持定時器的意義; 在TCP連線雙方,均有一個接收快取,當接收快取滿時,接收端會回覆傳送端一個視窗大小為
TCP/IP學習筆記(13)-TCP堅持定時器,TCP保活定時器
TCP一共有四個主要的定時器,前面已經講到了一個--超時定時器--是TCP裡面最複雜的一個,另外的三個是: 1. 堅持定時器 2. 保活定時器 3. 2MSL定時器 其中堅持定時器用於防止通告視窗為0以後雙方
TCP的定時器系列 — 零視窗探測定時器
主要內容:零視窗探測定時器的實現。 核心版本:3.15.2 出現以下情況時,TCP接收方的接收緩衝區將被塞滿資料: 傳送方的傳送速度大於接收方的接收速度。 接收方的應用程式未能及時從接收緩衝區中讀取資料。 當接收方的接收緩衝區滿了以後,會把響應報文中的通告視窗欄位置為
C# 定時器保活機制引起的記憶體洩露問題
C# 中有三種定時器,System.Windows.Forms 中的定時器和 System.Timers.Timer 的工作方式是完全一樣的,所以,這裡我們僅討論 System.Timers.Timer 和 System.Threading.Timer 1、定時器保活 先來看一個例子: class Progr
補習系列(9)-springboot 定時器,你用對了嗎
empty cront apps 任務並發 轉發 gis execute 大小 定義 目錄 簡介 一、應用啟動任務 二、JDK 自帶調度線程池 三、@Scheduled 定制 @Scheduled 線程池 四、@Async 定制 @Async 線程池 小結 簡介 大
深入理解定時器系列第一篇——理解setTimeout和setInterval
前面的話 很長時間以來,定時器一直是javascript動畫的核心技術。但是,關於定時器,人們通常只瞭解如何使用setTimeout()和setInterval(),對它們的內在執行機制並不理解,對於與預想不同的實際執行狀況也無法解決。本文將詳細介紹定時器的相關內容 setTimeout()
深入理解定時器系列第三篇——定時器應用(時鐘、倒計時、秒錶和鬧鐘)
前面的話 本文屬於定時器的應用部分,分別用於實現與時間相關的四個應用,包括時鐘、倒計時、秒錶和鬧鐘。與時間相關需要用到時間和日期物件Date,詳細情況移步至此 時鐘 最簡單的時鐘製作辦法是通過正則表示式的exec()方法,將時間物件的字串中的時間部分截取出來,使用定時器重新整理即可 &
深入理解定時器系列第二篇——被譽為神器的requestAnimationFrame
前面的話 與setTimeout和setInterval不同,requestAnimationFrame不需要設定時間間隔。這有什麼好處呢?為什麼requestAnimationFrame被稱為神器呢?本文將詳細介紹HTML5新增的定時器requestAnimationFrame 引入 計時
定時器系列-被譽為神器的requestAnimationFrame
大多數電腦顯示器的重新整理頻率是60Hz,大概相當於每秒鐘重繪60次。大多數瀏覽器都會對重繪操作加以限制,不超過顯示器的重繪頻率,因為即使超過那個頻率使用者體驗也不會有提升。因此,最平滑動畫的最佳迴圈間隔是1000ms/60,約等於16.6ms
Python系列之迴圈定時器
近期在學習並使用Python開發一些小工具,在這裡記錄方便回憶,也與各位開始走上這條路的朋友共勉,如有不正確希望指正,謝謝! 開始使用定時器時,度娘了下有沒好的例子,本人比較懶,希望能直接使用。確實找到了一些,但是大多隻是很直白的程式碼,自己打算整理一下。 我選用了thr
定時器/計數器0之定時器
.com 函數調用 wid 延時 mod main images .cn cnblogs /* 效果說明: 定時器中斷:通過單片機計數使程序執行 一秒中斷一次,中斷發生時高四位亮一秒,中斷發生後又回到主程序 */ #include <
C# System.Timers.Timer定時器的使用和定時自動清理內存應用
for process work proc program 指定時間 handle 清理 interval 項目比較大有時候會比較卡,雖然有GC自動清理機制,但是還是有不盡人意的地方。所以嘗試在項目啟動文件中,手動寫了一個定時器,定時清理內存,加快項目運行速度。 pub
Windows服務器上使用bat定時執行php
保存 掛載 color website window 服務 註意 一個 con windows上和linux上有一個類似的cmd和bat文件,bat文件類似於shell文件,執行這個bat文件,就相當於依次執行裏面的命令(當然,還可以通過邏輯來實現編程),所以,我們可以
runloop 和 CFRunLoop - 定時器 - NSTimer 和 GCD定時器
決定 etc ont tro mode .com int schedule lin 1. 2、 1 #import "ViewController.h" 2 3 @interface ViewController () 4 @property (no
STM32同時開啟兩個定時器,其中一個定時器不能設定斷點的原因
最近在編寫程式的時候發現stm32微控制器的定時器不同的型別其配置是不一樣的。 在程式設計的過程中開了兩個定時器,結果在除錯程式的時候發現TIM6中斷程式不能設定斷點,就說明這段程式可能沒有被執行,後來我又換了TIM1也是一樣不行。 檢視資料手冊如下: 從stm3
MySQL 事件排程器(Event Scheduler)建立定時任務
事件排程器(Event Scheduler)是在MySQLv5.1.6中新增的一個功能,它相當於一個定時器,可以在指定的時間點執行一條SQL語句或一個語句塊,也可以用於在固定間隔重複執行。事件排程器相當於作業系統中的定時任務(如:Linux中的cron、Window中的計劃任務),但MySql的事
使用Spring的定時器@Scheduled註解實現定時任務
在很多時候我們在專案中需要週期性地執行一些操作,並且這些操作不能通過簡單的for迴圈和while迴圈來實現,因此我們需要有一個可以實現定時操作的方法,在spring中就有這麼一個非常方便的方法,下面就簡單記錄下@Scheduled註解的使用方法。 1、首先我們需要一個spring專案,這個是前