記錄 - 5.2
mysql
round函數(shù): 四舍五入,保留小數(shù)。
TCP 延遲確認(rèn)與Nagle算法
Nagle 算法 :? ?滿(mǎn)足任一條件
條件一 : 要等到窗口大小 ?>=
MSS
并且數(shù)據(jù)大小 >=MSS
條件二:收到之前發(fā)送數(shù)據(jù)的
ack
回包
延遲確認(rèn):
當(dāng)有響應(yīng)數(shù)據(jù)要發(fā)送時(shí),ACK 會(huì)隨著響應(yīng)數(shù)據(jù)一起立刻發(fā)送給對(duì)方
當(dāng)沒(méi)有響應(yīng)數(shù)據(jù)要發(fā)送時(shí),ACK 將會(huì)延遲一段時(shí)間,以等待是否有響應(yīng)數(shù)據(jù)可以一起發(fā)送
如果在延遲等待發(fā)送 ACK 期間,對(duì)方的第二個(gè)數(shù)據(jù)報(bào)文又到達(dá)了,這時(shí)就會(huì)立刻發(fā)送 ACK
tcp_retries1 和tcp_retries2
兩者都是在TCP 三次握手之后的場(chǎng)景
當(dāng)重傳次數(shù)超過(guò)tcp_retries1就會(huì)指示IP 層進(jìn)行MTU 探測(cè)、刷新路由等過(guò)程,并不會(huì)斷開(kāi)TCP 連接,當(dāng)重傳次數(shù)超過(guò) tcp_retries2 才會(huì)斷開(kāi)TCP 流。
tcp_retries1 和tcp_retries2 兩個(gè)重傳次數(shù)都是受一個(gè)timeout 值限制的,當(dāng)重傳時(shí)間超過(guò)timeout,就不會(huì)重傳了。
查看全連接隊(duì)列
在listen狀態(tài)時(shí):
Recv-Q:當(dāng)前全連接隊(duì)列的大小,也就是當(dāng)前已完成三次握手并等待服務(wù)端 accept() 的 TCP 連接;
Send-Q:當(dāng)前全連接最大隊(duì)列長(zhǎng)度,上面的輸出結(jié)果說(shuō)明監(jiān)聽(tīng) 8088 端口的 TCP 服務(wù),最大全連接長(zhǎng)度為 128
在非listen狀態(tài)時(shí):
Recv-Q:已收到但未被應(yīng)用進(jìn)程讀取的字節(jié)數(shù);
Send-Q:已發(fā)送但未收到確認(rèn)的字節(jié)數(shù);
當(dāng)TCP ?全連接隊(duì)列滿(mǎn)了會(huì)使用什么策略來(lái)回應(yīng)客戶(hù)端?
默認(rèn)丟棄,也可以回復(fù)Rst復(fù)位報(bào)文。 通過(guò) ipv4.tcp_abort_on_overflow
參數(shù)控制。0: 丟棄。 1:發(fā)送RST
如何增大TCP 全連接隊(duì)列
TCP 全連接隊(duì)列的最大值取決于 somaxconn 和 backlog 之間的最小值,也就是 min(somaxconn, backlog)
半連接隊(duì)列的大小
半連接隊(duì)列的最大值是 max_qlen_log 變量,半連接隊(duì)列最大值不是單單由 max_syn_backlog 決定,還跟 somaxconn 和 backlog 有關(guān)系。

三次握手性能的提升
SYN_SENT、SYN_REV:
可以調(diào)整重傳次數(shù)。如在內(nèi)網(wǎng)中通訊時(shí),適當(dāng)調(diào)低重傳次數(shù),盡快把錯(cuò)誤暴露給應(yīng)用程序。

四次揮手的性能提升
關(guān)閉的方式:RST 報(bào)文和FIN 報(bào)文
如果進(jìn)程收到RST 報(bào)文,直接關(guān)閉連接,這是一個(gè)暴力關(guān)閉連接的方式
close函數(shù):關(guān)閉雙向。
shutdown函數(shù):優(yōu)雅關(guān)閉,可以只關(guān)閉一個(gè)方向
FIN_WAIT1 狀態(tài)的優(yōu)化
重發(fā)由 tcp_orphan_retries
參數(shù)控制:orphan 雖然是孤兒的意思,該參數(shù)卻不只對(duì)孤兒連接有效,事實(shí)上,它對(duì)所有 FIN_WAIT1 狀態(tài)下的連接都有效,默認(rèn)值是 0。 0,特指 8 次。
對(duì)于普遍正常情況時(shí),調(diào)低 tcp_orphan_retries 就已經(jīng)可以了。如果遇到惡意攻擊,F(xiàn)IN 報(bào)文根本無(wú)法發(fā)送出去,這由 TCP 兩個(gè)特性導(dǎo)致的:
首先,TCP 必須保證報(bào)文是有序發(fā)送的,F(xiàn)IN 報(bào)文也不例外,當(dāng)發(fā)送緩沖區(qū)還有數(shù)據(jù)沒(méi)有發(fā)送時(shí),F(xiàn)IN 報(bào)文也不能提前發(fā)送。
其次,TCP 有流量控制功能,當(dāng)接收方接收窗口為 0 時(shí),發(fā)送方就不能再發(fā)送數(shù)據(jù)。所以,當(dāng)攻擊者下載大文件時(shí),就可以通過(guò)接收窗口設(shè)為 0 ,這就會(huì)使得 FIN 報(bào)文都無(wú)法發(fā)送出去,那么連接會(huì)一直處于 FIN_WAIT1 狀態(tài)。
解決方法:調(diào)整 tcp_max_orphans 參數(shù),它定義了「孤兒連接」的最大數(shù)量。超出數(shù)量的將會(huì)直接發(fā)送RST報(bào)文強(qiáng)制關(guān)閉。
FIN_WAIT2 狀態(tài)的優(yōu)化
close關(guān)閉的連接,不會(huì)在FIN_WAIT2持續(xù)太久,tcp_fin_timeout 控制此時(shí)長(zhǎng),默認(rèn)是60s,這與TIME_WAIT的時(shí)間是一樣的。
但shutdown可以在這個(gè)狀態(tài)持續(xù),因?yàn)榭赡苓€可以發(fā)送或接收數(shù)據(jù)。
TIME_WAIT 狀態(tài)的優(yōu)化
當(dāng)TIME_WAIT 連接超過(guò)一定量(tcp_max_tw_buckets)時(shí),新關(guān)閉的連接就不再經(jīng)歷TIME_WAIT 而直接關(guān)閉。
連接復(fù)用。打開(kāi)tcp_tw_reuse 參數(shù)和打開(kāi)時(shí)間戳功能。此參數(shù)只適用于連接發(fā)起方。
如何連接雙方同時(shí)關(guān)閉連接,會(huì)怎么樣?

雙方在等待 ACK 報(bào)文的過(guò)程中,都等來(lái)了 FIN 報(bào)文。這是一種新情況,所以連接會(huì)進(jìn)入一種叫做 CLOSING 的新?tīng)顟B(tài),它替代了 FIN_WAIT2 狀態(tài)。接著,雙方內(nèi)核回復(fù) ACK 確認(rèn)對(duì)方發(fā)送通道的關(guān)閉后,進(jìn)入 TIME_WAIT 狀態(tài),等待 2MSL 的時(shí)間后,連接自動(dòng)關(guān)閉。
開(kāi)啟時(shí)間戳的好處
2MSL的問(wèn)題不存在在了,因?yàn)橹貜?fù)的數(shù)據(jù)包會(huì)因?yàn)闀r(shí)間戳過(guò)期被自然丟棄
可以防止序列號(hào)繞回
便于精確計(jì)算RTT
傳輸性能的提升
如何確定最大的傳輸速度?
帶寬時(shí)延積 BDP = RTT * 帶寬
如果飛行報(bào)文超過(guò)了1MB,就會(huì)導(dǎo)致網(wǎng)絡(luò)過(guò)載容易丟包。
怎樣調(diào)整緩沖區(qū)大小
Linux 會(huì)根據(jù)設(shè)置的參數(shù)進(jìn)行動(dòng)態(tài)調(diào)節(jié)。
發(fā)送緩沖區(qū)是自行調(diào)節(jié)的,接收緩沖區(qū)可以根據(jù)系統(tǒng)空閑內(nèi)存的大小來(lái)調(diào)節(jié)接收窗口。
發(fā)送緩沖區(qū)的調(diào)節(jié)功能是自動(dòng)發(fā)開(kāi)啟的,而接收緩沖區(qū)則需要手動(dòng)來(lái)開(kāi)啟調(diào)節(jié)功能。
小結(jié)

TCP 可靠性是通過(guò) ACK 確認(rèn)報(bào)文實(shí)現(xiàn)的,又依賴(lài)滑動(dòng)窗口提升了發(fā)送速度也兼顧了接收方的處理能力。
內(nèi)核緩沖區(qū)決定了滑動(dòng)窗口的上限,緩沖區(qū)可分為:發(fā)送緩沖區(qū) tcp_wmem 和接收緩沖區(qū) tcp_rmem。
Linux 會(huì)對(duì)緩沖區(qū)動(dòng)態(tài)調(diào)節(jié),我們應(yīng)該把緩沖區(qū)的上限設(shè)置為帶寬時(shí)延積。發(fā)送緩沖區(qū)的調(diào)節(jié)功能是自動(dòng)打開(kāi)的,而接收緩沖區(qū)需要把 tcp_moderate_rcvbuf 設(shè)置為 1 來(lái)開(kāi)啟。其中,調(diào)節(jié)的依據(jù)是 TCP 內(nèi)存范圍 tcp_mem。
如何理解TCP 是面向字節(jié)流的協(xié)議
UDP: 操作系統(tǒng)不會(huì)對(duì)消息進(jìn)行拆分,每個(gè)報(bào)文就是一個(gè)用戶(hù)消息的邊界。
TCP:不能認(rèn)為一個(gè)用戶(hù)消息對(duì)應(yīng)一個(gè)TCP 報(bào)文,因此,TCP是面向字節(jié)流的協(xié)議。
為什么TCP 每次建立連接時(shí),初始化序列號(hào)都要不一樣呢?
防止歷史數(shù)據(jù)被下一個(gè)相同的四元組錯(cuò)誤的接收。
四次揮手的TIME_WAIT 不是會(huì)持續(xù)2MSL時(shí)長(zhǎng),歷史報(bào)文不是早就在網(wǎng)絡(luò)中消失了嗎?
并不是所有連接都會(huì)通過(guò)四次揮手來(lái)正常關(guān)閉連接。
序列號(hào)回繞問(wèn)題:
開(kāi)啟TCP時(shí)間戳。防回繞算法:要求雙方維護(hù)最近一次收到數(shù)據(jù)包的時(shí)間戳,每收到一個(gè)新數(shù)據(jù)包都會(huì)跟上一個(gè)時(shí)間戳比較,如果發(fā)現(xiàn)時(shí)間戳不是遞增的,則該數(shù)據(jù)包是過(guò)期的,直接丟棄該數(shù)據(jù)包。
如果時(shí)間戳也回繞了怎么辦?
Linux 在 PAWS 檢查做了一個(gè)特殊處理,如果一個(gè) TCP 連接連續(xù) 24 天不收發(fā)數(shù)據(jù)則在接收第一個(gè)包時(shí)基于時(shí)間戳的 PAWS 會(huì)失效,也就是可以 PAWS 函數(shù)會(huì)放過(guò)這個(gè)特殊的情況,認(rèn)為是合法的,可以接收該數(shù)據(jù)包。
SYN 報(bào)文什么情況下會(huì)被丟棄
開(kāi)啟 tcp_tw_recycle 參數(shù),并且在 NAT 環(huán)境下,造成 SYN 報(bào)文被丟棄
TCP 兩個(gè)隊(duì)列滿(mǎn)了(半連接隊(duì)列和全連接隊(duì)列),造成 SYN 報(bào)文被丟棄
已建立連接的TCP ,收到SYN會(huì)發(fā)生什么
客戶(hù)端的SYN 報(bào)文里的端口號(hào)與歷史連接的不相同:
服務(wù)端會(huì)認(rèn)為是新的連接要建立,于是就會(huì)通過(guò)三次握手來(lái)建立新的連接。
舊連接處于 Established 狀態(tài)的服務(wù)端最后會(huì)怎樣呢?
如果服務(wù)端發(fā)送了數(shù)據(jù)包給客戶(hù)端,由于客戶(hù)端的連接已經(jīng)被關(guān)閉了,此時(shí)客戶(hù)的內(nèi)核就會(huì)回 RST 報(bào)文,服務(wù)端收到后就會(huì)釋放連接。
如果服務(wù)端一直沒(méi)有發(fā)送數(shù)據(jù)包給客戶(hù)端,在超過(guò)一段時(shí)間后,TCP 保活機(jī)制就會(huì)啟動(dòng),檢測(cè)到客戶(hù)端沒(méi)有存活后,接著服務(wù)端就會(huì)釋放掉該連接。
客戶(hù)端的 SYN 報(bào)文里的端口號(hào)與歷史連接相同
處于Established 狀態(tài)的服務(wù)端,如果收到了客戶(hù)端的SYN 報(bào)文(SYN報(bào)文的初始化序列號(hào)是一個(gè)隨機(jī)數(shù),對(duì)于服務(wù)端就是一個(gè)亂序的序列號(hào)),會(huì)回復(fù)一個(gè)攜帶了正確序列號(hào)和確認(rèn)號(hào)的ACK 報(bào)文,這個(gè)ACK 又稱(chēng)為 Challenage ACK ;
接著,客戶(hù)端收到這個(gè)Challenage ACK ,發(fā)現(xiàn)確認(rèn)號(hào)(ack ?num)并不是自己期望收到的,于是會(huì)回復(fù)RST 報(bào)文,服務(wù)端收到后,就會(huì)釋放該連接。
如何關(guān)閉一個(gè)TCP 連接
殺掉進(jìn)程。
killcx 工具
它會(huì)主動(dòng)發(fā)送 SYN 包獲取 SEQ/ACK 號(hào),然后利用 SEQ/ACK 號(hào)偽造兩個(gè) RST 報(bào)文分別發(fā)給客戶(hù)端和服務(wù)端,這樣雙方的 TCP 連接都會(huì)被釋放,這種方式活躍和非活躍的 TCP 連接都可以殺掉。
tcpkill 工具
在雙方進(jìn)行 TCP 通信時(shí),拿到對(duì)方下一次期望收到的序列號(hào),然后將序列號(hào)填充到偽造的 RST 報(bào)文,并將其發(fā)送給對(duì)方,達(dá)到關(guān)閉 TCP 連接的效果。
總結(jié)
tcpkill 工具只能用來(lái)關(guān)閉活躍的 TCP 連接,無(wú)法關(guān)閉非活躍的 TCP 連接,因?yàn)?tcpkill 工具是等雙方進(jìn)行 TCP 通信后,才去獲取正確的序列號(hào),如果這條 TCP 連接一直沒(méi)有任何數(shù)據(jù)傳輸,則就永遠(yuǎn)獲取不到正確的序列號(hào)。
killcx 工具可以用來(lái)關(guān)閉活躍和非活躍的 TCP 連接,因?yàn)?killcx 工具是主動(dòng)發(fā)送 SYN 報(bào)文,這時(shí)對(duì)方就會(huì)回復(fù) Challenge ACK ,然后 killcx 工具就能從這個(gè) ACK 獲取到正確的序列號(hào)。
在 FIN_WAIT_2 狀態(tài)下,是如何處理收到的亂序到 FIN 報(bào)文,然后TCP連接又是什么時(shí)候才進(jìn)入到TIME_WAIT 狀態(tài)?
在FIN_WAIT_2狀態(tài)時(shí),如果收到亂序的FIN 報(bào)文,那么就會(huì)加入到亂序隊(duì)列
,并不會(huì)進(jìn)入到TIME_WAIT 狀態(tài)。
等收到前面被網(wǎng)絡(luò)延遲的數(shù)據(jù)包時(shí),會(huì)判斷亂序隊(duì)列有沒(méi)有數(shù)據(jù),然后會(huì)檢測(cè)亂序隊(duì)列中是否有可用的數(shù)據(jù),如果能在亂序隊(duì)列中找到與當(dāng)前報(bào)文的序列號(hào)保持順序的報(bào)文,就會(huì)看該報(bào)文是否有FIN 標(biāo)志,如果發(fā)現(xiàn)有FIN 標(biāo)志,這時(shí)才進(jìn)入TIME_WAIT 狀態(tài)。
在TIME_WAIT 狀態(tài)的TCP 連接,收到SYN 后會(huì)發(fā)生什么?
合法 SYN:
客戶(hù)端的 SYN 的「序列號(hào)」比服務(wù)端「期望下一個(gè)收到的序列號(hào)」要大,并且 SYN 的「時(shí)間戳」比服務(wù)端「最后收到的報(bào)文的時(shí)間戳」要大。
結(jié)果:重用此四元組連接,跳過(guò)2MSL 而轉(zhuǎn)變?yōu)镾YN_RECV狀態(tài)。
非法 SYN:
客戶(hù)端的 SYN 的「序列號(hào)」比服務(wù)端「期望下一個(gè)收到的序列號(hào)」要小,或者 SYN 的「時(shí)間戳」比服務(wù)端「最后收到的報(bào)文的時(shí)間戳」要小。
結(jié)果:會(huì)再回復(fù)一個(gè)第四次揮手的 ACK 報(bào)文,客戶(hù)端收到后,發(fā)現(xiàn)并不是自己期望收到確認(rèn)號(hào)(ack num),就回 RST 報(bào)文給服務(wù)端。
在 TIME_WAIT 狀態(tài),收到 RST 會(huì)斷開(kāi)連接嗎?
如果 net.ipv4.tcp_rfc1337 參數(shù)為 0,則提前結(jié)束 TIME_WAIT 狀態(tài),釋放連接。
如果 net.ipv4.tcp_rfc1337 參數(shù)為 1,則會(huì)丟掉該 RST 報(bào)文。
進(jìn)程崩潰和主機(jī)宕機(jī)
客戶(hù)端主機(jī)宕機(jī),又迅速重啟
客戶(hù)端沒(méi)重啟完:不會(huì)響應(yīng)報(bào)文;重啟完后,收到之前的TCP報(bào)文,都會(huì)回復(fù)RST報(bào)文。是否有進(jìn)程綁定該TCP目標(biāo)端口號(hào),TCP維護(hù)的結(jié)構(gòu)信息都已經(jīng)丟失了,只能重新建立連接。
如果「客戶(hù)端進(jìn)程崩潰」,客戶(hù)端的進(jìn)程在發(fā)生崩潰的時(shí)候,內(nèi)核會(huì)發(fā)送 FIN 報(bào)文,與服務(wù)端進(jìn)行四次揮手。
但是,「客戶(hù)端主機(jī)宕機(jī)」,那么是不會(huì)發(fā)生四次揮手的,具體后續(xù)會(huì)發(fā)生什么?還要看服務(wù)端會(huì)不會(huì)發(fā)送數(shù)據(jù)?
如果服務(wù)端會(huì)發(fā)送數(shù)據(jù),由于客戶(hù)端已經(jīng)不存在,收不到數(shù)據(jù)報(bào)文的響應(yīng)報(bào)文,服務(wù)端的數(shù)據(jù)報(bào)文會(huì)超時(shí)重傳,當(dāng)重傳總間隔時(shí)長(zhǎng)達(dá)到一定閾值(內(nèi)核會(huì)根據(jù) tcp_retries2 設(shè)置的值計(jì)算出一個(gè)閾值)后,會(huì)斷開(kāi) TCP 連接;
如果服務(wù)端一直不會(huì)發(fā)送數(shù)據(jù),再看服務(wù)端有沒(méi)有開(kāi)啟 TCP keepalive 機(jī)制?
如果有開(kāi)啟,服務(wù)端在一段時(shí)間沒(méi)有進(jìn)行數(shù)據(jù)交互時(shí),會(huì)觸發(fā) TCP keepalive 機(jī)制,探測(cè)對(duì)方是否存在,如果探測(cè)到對(duì)方已經(jīng)消亡,則會(huì)斷開(kāi)自身的 TCP 連接;
如果沒(méi)有開(kāi)啟,服務(wù)端的 TCP 連接會(huì)一直存在,并且一直保持在 ESTABLISHED 狀態(tài)。
linux命令
netstat
-a
或--all
:顯示偵聽(tīng)套接字和非偵聽(tīng)套接字。-t
或--tcp
:只顯示tcp套接字。-u
或--udp
:只顯示udp套接字。-l
或--listening
:只顯示偵聽(tīng)套接字。-n
或--numeric
:顯示數(shù)字地址,而不是嘗試確定符號(hào)主機(jī)、端口或用戶(hù)名。-p
或--program
:顯示每個(gè)套接字所屬的程序的PID和名稱(chēng)。-r
或--route
:顯示內(nèi)核路由表。-s
或--statistics
:顯示每個(gè)協(xié)議的匯總統(tǒng)計(jì)信息。?