TCP/IP知識(shí)點(diǎn)及面試??键c(diǎn)總結(jié)
“本文主要包含兩個(gè)方面,一是從計(jì)算機(jī)網(wǎng)絡(luò)分層模型出發(fā),分析每一個(gè)層的功能,以及TCP/IP協(xié)議棧的實(shí)現(xiàn);二是介紹TCP相關(guān)面試??键c(diǎn)。
”
一. TCP/IP協(xié)議棧實(shí)現(xiàn)
開局一張圖,計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)如下:
本文將采用右側(cè)五層模型從下往上依次講解,詳細(xì)講述每一層的協(xié)議和作用,并且會(huì)層層介紹協(xié)議頭如何實(shí)現(xiàn),其中數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層和傳輸層屬于內(nèi)核(TCP/IP協(xié)議棧屬于內(nèi)核),也是重點(diǎn)介紹的內(nèi)容。
1.物理層
功能:主要進(jìn)行物理信號(hào)傳輸,負(fù)責(zé)A/D轉(zhuǎn)換,即物理信號(hào)和數(shù)字信號(hào)之間的轉(zhuǎn)換。
交互中間設(shè)備:轉(zhuǎn)發(fā)器
2.數(shù)據(jù)鏈路層
網(wǎng)絡(luò)適配器工作在這一層,因此Mac地址屬于這一層,需要進(jìn)行差錯(cuò)控制等
數(shù)據(jù)傳輸單位:MAC幀
交互中間設(shè)備:網(wǎng)橋/橋接器(bridge)
協(xié)議:以太網(wǎng)協(xié)議
封裝成幀(幀頭SOH,幀尾EOT,中間為IP數(shù)據(jù)報(bào)),透明傳輸(ESC字節(jié)填充),差錯(cuò)檢測(cè)(循環(huán)冗余檢測(cè)CRC) 以太網(wǎng)MAC層的硬件地址,(計(jì)算機(jī)的硬件地址-MAC地址,在適配器的ROM中;軟件地址-IP地址在存儲(chǔ)器中)
適配器是什么?為什么要有適配器?
適配器(網(wǎng)絡(luò)接口卡NIC+ROM+RAM等),適配器與局域網(wǎng)之間的通信通過電纜/雙絞線以串行傳輸方式進(jìn)行,適配器與計(jì)算機(jī)之間的通信通過主板上IO以并行傳輸方式進(jìn)行。兩者數(shù)據(jù)率不一致,因此適配器需要緩存。
Mac幀格式如下圖:
前12個(gè)字節(jié)表示目的地址和源地址(48位長(zhǎng));在不同網(wǎng)絡(luò)上傳輸時(shí),mac地址會(huì)不斷變化 接下來2個(gè)字節(jié)表示上層協(xié)議(0x0800 IP數(shù)據(jù)報(bào)) 接下來數(shù)據(jù)范圍(46字節(jié)-1500字節(jié)) FCS,4個(gè)字節(jié),采用CRC檢驗(yàn)
以太網(wǎng)協(xié)議頭實(shí)現(xiàn):
#define ETH_ALEN 6
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; // 6個(gè)字節(jié)目的地址
unsigned char h_src[ETH_ALEN]; // 6個(gè)字節(jié)源地址
unsigned short h_proto; // 2個(gè)字節(jié)協(xié)議
};
3.網(wǎng)絡(luò)層
交互中間設(shè)備:路由器
本層主要介紹三個(gè)協(xié)議:IP協(xié)議,ICMP協(xié)議,以及ARP協(xié)議
IP協(xié)議;數(shù)據(jù)傳輸單位為IP數(shù)據(jù)報(bào),向上為運(yùn)輸層提供數(shù)據(jù)(解包),向下將運(yùn)輸層數(shù)據(jù)封裝成包進(jìn)行傳輸
不可靠(不能保證IP數(shù)據(jù)包成功到達(dá)目的地),無連接(每個(gè)數(shù)據(jù)報(bào)處理都是相互獨(dú)立的,也不保序)
網(wǎng)絡(luò)字節(jié)序:(4個(gè)字節(jié)的32bit值以大端字節(jié)序傳輸:0~7bit,8~15,16~23,24~31,因此大端字節(jié)序又稱為網(wǎng)絡(luò)字節(jié)序,因此在傳輸數(shù)據(jù)前,如果主機(jī)采用的是小端序,需要先轉(zhuǎn)換成網(wǎng)絡(luò)/大端字節(jié)序,再進(jìn)行傳輸)
接下來介紹IP協(xié)議頭的實(shí)現(xiàn),以下為IP數(shù)據(jù)報(bào)頭部固定20個(gè)字節(jié),如下圖:
版本:4位,IPv4/6 首部長(zhǎng)度:4位 區(qū)分服務(wù):1個(gè)字節(jié)服務(wù)類型TOS;4個(gè)bit,每一位分別表示最小時(shí)延,最大吞吐量,最高可靠性和最小費(fèi)用,只能置其中1個(gè)bit為1,其它的都為0;例如Telnet將第一位置為1,表示最小時(shí)延,主要用來傳輸少量的交互數(shù)據(jù)。FTP文件傳輸要求最大吞吐量; 總長(zhǎng)度:2個(gè)字節(jié),首部和數(shù)據(jù)合總長(zhǎng)度(2^16) 標(biāo)識(shí):2個(gè)字節(jié),一個(gè)數(shù)據(jù)報(bào)對(duì)應(yīng)一個(gè)標(biāo)識(shí)(相同表示片組成同一數(shù)據(jù)包) 標(biāo)志:3位,最低位MF(==1 還有分片,==0沒有分片);中間位DF(不能分片) 片偏移:13位,較長(zhǎng)的分組分片后,某片在原分組中的位置 生存時(shí)間TTL:1個(gè)字節(jié),數(shù)據(jù)報(bào)在網(wǎng)絡(luò)中的壽命(最大跳數(shù)) 協(xié)議:1個(gè)字節(jié), 首部檢驗(yàn)合:2個(gè)字節(jié),只檢驗(yàn)數(shù)據(jù)報(bào)首部; 源地址+目的地址:各4個(gè)字節(jié)
IP協(xié)議頭實(shí)現(xiàn):
struct iphdr {
unsigned char version: 4, // 4位版本號(hào)
head_len: 4; // 4位首部長(zhǎng)度
unsigned char tos; // 1個(gè)字節(jié)TOS服務(wù)類型
unsigned short tot_len; // 2個(gè)字節(jié)總長(zhǎng)度
unsigned short id; // 2個(gè)字節(jié)表示
unsigned short flag: 3, // 3位標(biāo)志
offset: 13; // 13位片偏移
unsigned char ttl; // 1個(gè)字節(jié)生存時(shí)間
unsigned char protocol; // 1個(gè)字節(jié)協(xié)議
unsigned short check; // 2個(gè)字節(jié)首部檢驗(yàn)和
unsigned int saddr; // 4個(gè)字節(jié)源地址
unsigned int daddr; // 4個(gè)字節(jié)目的地址
};
ICMP協(xié)議(網(wǎng)際控制報(bào)文協(xié)議):為更有效轉(zhuǎn)發(fā)IP數(shù)據(jù)報(bào)和提高交付成功機(jī)會(huì)。主要功能:確認(rèn)IP是否成功到達(dá)目的地址,報(bào)告發(fā)送過程中IP包被廢棄的原因和改善網(wǎng)絡(luò)設(shè)置等。
ICMP差錯(cuò)報(bào)文報(bào)告 終點(diǎn)不可達(dá)消息,類型為3,分為網(wǎng)絡(luò)不可達(dá),主機(jī)不可達(dá),協(xié)議不可達(dá),端口不可達(dá),需要分片但設(shè)置了不分片 重定向消息,類型為5 超時(shí)消息:類型11, 重定向 ICMP詢問報(bào)文類型 回送請(qǐng)求(Echo Request 類型8)和回送應(yīng)答(Echo Reply 類型0)PING(直接只用網(wǎng)絡(luò)層,沒有經(jīng)過傳輸層) 時(shí)間戳請(qǐng)求和回答
traceroute(逐一增加ttl)是ICMP差錯(cuò)報(bào)文類型的使用,
追蹤來去目的地時(shí)沿途經(jīng)過的路由器:通過不斷增加TTL實(shí)現(xiàn),當(dāng)TTL減少到0時(shí),會(huì)返回ICMP差錯(cuò)報(bào)文(類型為超時(shí))。直到到達(dá)目的IP 確認(rèn)路徑MTU:將IP包首部的分片禁止標(biāo)志位設(shè)置為1,路由器不會(huì)對(duì)大數(shù)據(jù)包分片,進(jìn)而將包丟棄,并返回一個(gè)ICMP不可達(dá)消息并攜帶數(shù)據(jù)鏈路上的MTU值。不可達(dá)消息類型為“需要分片但是設(shè)置了不分片?!?/section>
ping是ICMP詢問報(bào)文的使用,通過回送消息判斷所發(fā)送的數(shù)據(jù)包死否已經(jīng)成功到達(dá)對(duì)端。
ARP協(xié)議(地址解析協(xié)議):每臺(tái)主機(jī)都有一個(gè)ARP Cache(高速緩存),存有本局域網(wǎng)上各主機(jī)/路由器的IP地址到MAC地址的映射表。通過使用ARP,找到IP對(duì)應(yīng)的MAC地址,找不到則交給路由器處理(下一跳)。因此MAC地址只在本局域網(wǎng)有效。ARP的功能是在32bit的IP地址和采用不同網(wǎng)絡(luò)技術(shù)的硬件地址之間提供動(dòng)態(tài)映射;IP地址和MAC地址的關(guān)聯(lián)保存在ARP表中,由驅(qū)動(dòng)程序和操作系統(tǒng)完成。
4.運(yùn)輸層
網(wǎng)絡(luò)層以上的交互中間設(shè)備:網(wǎng)關(guān)(gateway)
IP網(wǎng)絡(luò)層角度:通信的端點(diǎn)是兩臺(tái)主機(jī);運(yùn)輸層角度:通信端點(diǎn)是兩臺(tái)主機(jī)中的進(jìn)程。
運(yùn)輸層向進(jìn)程通信提供通用的數(shù)據(jù)傳輸服務(wù),提供了TCP/UDP兩種協(xié)議
TCP傳輸控制協(xié)議:面向連接的、面向字節(jié)流的,全雙工的,可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)傳輸單位為報(bào)文段(segment);TCP連接的端點(diǎn)叫做socket(IP+Port). UDP用戶數(shù)據(jù)報(bào)協(xié)議:無連接的,盡最大努力數(shù)據(jù)傳輸服務(wù)(不可靠),數(shù)據(jù)傳輸單位為用戶數(shù)據(jù)報(bào),一般用在對(duì)實(shí)時(shí)性和傳輸效率有一定要求的場(chǎng)景(下載,游戲等)
UDP協(xié)議頭:
沒有擁塞控制,首部開銷小(8個(gè)字節(jié))
UDP協(xié)議頭和udp數(shù)據(jù)包
// udp頭部
struct udphdr {
unsigned short source; // 源地址
unsigned short dest; // 目的地址
unsigned short len; // 數(shù)據(jù)報(bào)長(zhǎng)度
unsigned short check; // 檢驗(yàn)和
};
// udp數(shù)據(jù)包
struct udppkt {
struct ethhdr eh; // 以太網(wǎng)協(xié)議
struct iphdr ip; // ip協(xié)議
struct udphdr udp; // tcp協(xié)議
// 柔性數(shù)組,沒法知道數(shù)組長(zhǎng)度 sizeof(body)==0
// 使用情況:1. 長(zhǎng)度不確定;2.長(zhǎng)度可以通過計(jì)算出來不越界
unsigned char body[0];
};
TCP協(xié)議頭
源端口+目的端口:各占2個(gè)字節(jié) 序號(hào)(Seq):4個(gè)字節(jié),報(bào)文段序號(hào) 確認(rèn)號(hào)(Ack):4個(gè)字節(jié),期望收到對(duì)方下一個(gè)報(bào)文段第一個(gè)數(shù)據(jù)字節(jié)的序號(hào) 數(shù)據(jù)偏移:4位, 保留:4位 緊急URG:=1,表示緊急字段有效 ACK:=1,則Ack字段有效 PSH:推送,告知對(duì)方這些數(shù)據(jù)包收到以后應(yīng)該馬上交給上層應(yīng)用,不能緩存起來 RST:復(fù)位,TCP出現(xiàn)嚴(yán)重差錯(cuò),釋放連接,重新建立連接。 SYN:在建立連接時(shí)用來同步信號(hào),=1表示連接請(qǐng)求或者接受報(bào)文。 FIN:用來釋放一個(gè)連接,=1表示數(shù)據(jù)發(fā)送完畢。 窗口大?。?個(gè)字節(jié),表示發(fā)送報(bào)文段一方的接受窗口。 校驗(yàn)和:2個(gè)字節(jié),首部+數(shù)據(jù) 緊急指針:URG==1才生效
struct tcphdr {
unsigned short sport;
unsigned short dport;
unsigned int seqnum; // seq序列號(hào),收到客戶端,SYN==1生效
unsigned int acknum; // Ack, server/client確認(rèn), ACK==1生效
unsigned char hdrlen:4,
resv:4;
unsigned char cwr:1,
ece:1,
urg:1,
ack:1,
psh:1, //
rst:1,
syn:1,
fin:1;
unsigned shrot cw; // 窗口大小
unsigned short check;
unsigned short urg_pointer;
};
MSS,MSL等概念
MSS:TCP報(bào)文段中應(yīng)用數(shù)據(jù)字段的最大長(zhǎng)度,不是TCP報(bào)文總長(zhǎng)度。
MTU(以太網(wǎng)數(shù)據(jù)幀長(zhǎng)度46~1500字節(jié)): 最大傳輸單元 (MTU = MSS + TCP頭20字節(jié)+IP頭20字節(jié)),當(dāng)IP層數(shù)據(jù)長(zhǎng)度大于MTU時(shí),IP層需要對(duì)數(shù)據(jù)進(jìn)行分片(Fragmentation)
路徑MTU:一個(gè)包從發(fā)送端傳輸?shù)浇邮斩?,中間要跨越多個(gè)網(wǎng)絡(luò),每條鏈路的MTU都可能不一樣,這個(gè)通信過程中最小的MTU稱為路徑MTU。
5.應(yīng)用層
最高層,應(yīng)用程序之間進(jìn)行通信(進(jìn)程間通信),交互數(shù)據(jù)單元為報(bào)文(Message)
二. TCP面試常考點(diǎn)
PART1: TCP三次握手,四次揮手相關(guān)問題?
下面有兩張圖,第一張清楚展示了從建立連接到數(shù)據(jù)傳輸再到最后斷開連接的整個(gè)過程,第二張為狀態(tài)轉(zhuǎn)移圖。接下來會(huì)基于這兩張圖對(duì)TCP的三次握手和四次揮手進(jìn)行講解。
三次握手建立連接
主動(dòng)端調(diào)用connect發(fā)起連接,首先發(fā)送一個(gè)SYN包(將TCP首部SYN標(biāo)記置為1),告訴被動(dòng)端初始化序列號(hào)是x,這時(shí)主動(dòng)端進(jìn)入SYN_SENT狀態(tài),被動(dòng)端處在Listen狀態(tài)(調(diào)用listen后進(jìn)入此狀態(tài)) 被動(dòng)端收到SYN包后回復(fù)ACK表明已收到,并發(fā)送自己的初始化序列號(hào)y(將TCP首部SYN標(biāo)記和ACK標(biāo)記都置為1),被動(dòng)端把這個(gè)連接信息放入SYN隊(duì)列,進(jìn)入SYN_RCVD狀態(tài) 主動(dòng)端收到包后,回復(fù)一個(gè)ACK確認(rèn)包,此時(shí)雙方進(jìn)入ESATABLISED狀態(tài),被動(dòng)端把這個(gè)連接信息從SYN隊(duì)列移除,并將其放入Accept隊(duì)列中。
四次揮手?jǐn)嚅_連接
主動(dòng)端發(fā)送一個(gè)FIN包給被動(dòng)端(TCP首部FIN標(biāo)志置為1),表示沒有數(shù)據(jù)傳輸,要求斷開連接,這時(shí)主動(dòng)端進(jìn)入FIN_WAIT_1狀態(tài) 被動(dòng)端收到FIN包后,回復(fù)一個(gè)ACK包,這時(shí)被動(dòng)端進(jìn)入CLOSE_WAIT狀態(tài),主動(dòng)端收到ACK后進(jìn)入FIN_WAIT_2狀態(tài) 被動(dòng)端沒有數(shù)據(jù)再發(fā)送后,也向主動(dòng)端發(fā)送一個(gè)FIN包,被動(dòng)端進(jìn)入LAST_ACK狀態(tài) 主動(dòng)端收到FIN包,并回復(fù)一個(gè)ACK包,主動(dòng)端從FIN_WAIT_2狀態(tài)進(jìn)入TIME_WAIT狀態(tài)。
針對(duì)建立和斷開連接有以下幾個(gè)問題需要注意
握手不一定都是3次?
可能出現(xiàn)4次握手情況,比如雙方同時(shí)發(fā)起SYN隊(duì)列建立連接時(shí),如下圖
初始序列號(hào)(ISN,Inital Sequence Number)能固定嗎?
不能。如果固定,雙方建立連接后主動(dòng)方發(fā)送的數(shù)據(jù)包被路由器緩存了(路由器是會(huì)出現(xiàn)緩存甚至丟棄數(shù)據(jù)包的),這是主動(dòng)方掛掉了,然后又采用同樣的端口連接到被動(dòng)端,這時(shí)候如果上個(gè)連接被路由器緩存的數(shù)據(jù)包到了被動(dòng)端,豈不是序列號(hào)完全錯(cuò)亂了?(
SO_REUSEADDR允許端口重用:收到一個(gè)包后不知道屬于新連接還是舊連接,也導(dǎo)致串包。)在RFC793中,初始化序列號(hào)是每4微秒加一,直到超時(shí)2^32,又從0開始回繞(大概4.5個(gè)小時(shí));這種方式遞增也容易讓攻擊者猜到ISN的值(進(jìn)而偽造RST包將連接強(qiáng)制關(guān)掉),因此一般采用在一個(gè)基準(zhǔn)值上隨機(jī)加。
初始化連接時(shí),如果主動(dòng)方發(fā)送完SYN后就掛掉了,此時(shí)連接處于什么狀態(tài)?如果大量這種連接出現(xiàn)會(huì)造成什么危害?以及如何解決?
被動(dòng)方收到SYN包后就會(huì)將這個(gè)連接放入SYN半連接隊(duì)列,一直占用服務(wù)器資源,如果大量這種連接會(huì)把SYN半連接隊(duì)列的資源耗盡(這就是所謂的DDos攻擊和SYN Flood),從而讓正常連接無法得到處理。Linux提供了相應(yīng)的超時(shí)機(jī)制,進(jìn)行5次重發(fā)SYN-ACK包,時(shí)間間隔以此為1s, 2s,4s,8s,16s,因此需要63s才能斷開連接。但是這個(gè)時(shí)間給了攻擊者可乘之機(jī)。
如何應(yīng)對(duì)SYN Flood攻擊:
增加SYN連接數(shù):tcp_max_syn_backlog 減少SYN+ACK重試次數(shù):tcp_synack_retries SYN Cookie機(jī)制:tcp_syncookies, 原理為最后階段才分配連接資源,服務(wù)端收到SYN包后,根據(jù)這個(gè)包計(jì)算一個(gè)Cookie值,作為握手第二步的序列號(hào)回復(fù)SYN+ACK,等對(duì)方回應(yīng)ACK包時(shí)校驗(yàn)回復(fù)的ACK值是否合法,合法才握手成功分配資源。具體實(shí)現(xiàn) linux/syncookies.c什么是SYN隊(duì)列,什么是Accept隊(duì)列,listen中backlog參數(shù)是指啥?
syn隊(duì)列(半連接隊(duì)列):服務(wù)端收到客戶端的SYN包并回復(fù)SYN+ACK后,該連接的信息會(huì)被放入一個(gè)隊(duì)列,即為SYN半連接隊(duì)列(此使TCP處于非同步狀態(tài)),SYN半連接隊(duì)列由
tcp_max_syn_backlog這個(gè)內(nèi)核參數(shù)決定,如果隊(duì)列滿了,服務(wù)端會(huì)丟棄新來的SYN包,客戶端在多次重發(fā) SYN 包得不到響應(yīng)而返回(connection time out)錯(cuò)誤。但是,當(dāng)服務(wù)端開啟了 syncookies,那么 SYN 半連接隊(duì)列就沒有邏輯上的最大值了,且tcp_max_syn_backlog 設(shè)置的值也會(huì)被忽略。accept隊(duì)列(全連接隊(duì)列):Server端收到SYN+ACK包的ACK后,會(huì)將連接信息從SYN半連接隊(duì)列移到另一個(gè)隊(duì)列,即為Accept全連接隊(duì)列(TCP連接建立,三次握手完成);accept 連接隊(duì)列的大小是由 backlog 參數(shù)和(/proc/sys/net/core/somaxconn)內(nèi)核參數(shù)共同決定,取值為兩個(gè)中的最小值。當(dāng) accept 連接隊(duì)列滿了,協(xié)議棧的行為根據(jù)(/proc/sys/net/ipv4/tcp_abort_on_overflow)內(nèi)核參數(shù)而定。
// accept隊(duì)列去backlog和somaxconn的小者,syn隊(duì)列大小也跟這兩個(gè)參數(shù)相關(guān),且syn隊(duì)列略大于accept隊(duì)列
int sysctl_somaxconn = SOMAXCONN;
asmlinkage long sys_listen(int fd, int backlog)
{
struct socket *sock;
int err;
if ((sock = sockfd_lookup(fd, &err)) != NULL) {
// 取小者
if ((unsigned) backlog > sysctl_somaxconn)
backlog = sysctl_somaxconn;
err = security_socket_listen(sock, backlog);
if (err) {
sockfd_put(sock);
return err;
}
err=sock->ops->listen(sock, backlog);
sockfd_put(sock);
}
return err;
}半連接隊(duì)列和全連接隊(duì)列如下圖:
listen, accept,send等系統(tǒng)調(diào)用:
從accept隊(duì)列中取出一個(gè)節(jié)點(diǎn) 為該節(jié)點(diǎn)分配一個(gè)fd,將節(jié)點(diǎn)與fd一一對(duì)應(yīng),(fd -- 節(jié)點(diǎn) -- 五元組(sip, dip, sport, dport, proto),fd通過五元組判斷客戶端的唯一性) 當(dāng)accept隊(duì)列為空,則阻塞直到有數(shù)據(jù),通過條件變量實(shí)現(xiàn) listen: listen(fd, backlog)中第二個(gè)參數(shù)backlog就表示accept隊(duì)列長(zhǎng)度(LInux kernel 2.2之后 ),服務(wù)端調(diào)用listen后,TCP狀態(tài)從CLOSE狀態(tài)變成LISTEN狀態(tài),同時(shí)在內(nèi)核創(chuàng)建半連接隊(duì)列和全連接隊(duì)列。accept: clientfd = accept(listenfd, addr)send: send(fd)通過fd找到五元組并找到對(duì)應(yīng)的客戶端tcp_abort_on_overflow=1,全連接隊(duì)列滿后,服務(wù)端直接發(fā)送RST給客戶端,客戶端出現(xiàn)(connection reset by peer)錯(cuò)誤。
tcp_abort_on_overflow=0,全連接隊(duì)列滿后,服務(wù)端會(huì)丟掉客戶端發(fā)過來的ACK,隨后重傳SYN+ACK。
什么是TFO(Tcp Fast Open)快速打開:
分為兩階段:請(qǐng)求Fast Open Cookie和TCP Fast Open
之后,客戶端有了緩存在本地的cookies值:
客戶端發(fā)送SYN數(shù)據(jù)包,里面包含數(shù)據(jù)和之前緩存在本地的Fast Open Cookie(之前SYN包不含數(shù)據(jù)) 服務(wù)都那檢驗(yàn)TFO Cookie和是否合法,合法則返回SYN+ACK+數(shù)據(jù) 客戶端發(fā)送ACK包 進(jìn)行數(shù)據(jù)傳輸... 客戶端發(fā)送一個(gè)SYN包,頭部包含F(xiàn)ast Open選項(xiàng),且該選項(xiàng)的Cookie為空,這表明客戶端請(qǐng)求Fast Open Cookie 服務(wù)端收到SYN包后,生成一個(gè)cookie值 服務(wù)端發(fā)送SYN+ACK包,在Options的Fast Open選項(xiàng)中設(shè)置cookie的值 客戶端緩存服務(wù)端的IP和收到的cookie值 進(jìn)行數(shù)據(jù)傳輸... 四次揮手能不能變成三次?
可以。被動(dòng)端收到主動(dòng)端的FIN包后,也沒有數(shù)據(jù)要發(fā)送了,就把對(duì)ACK包和自己的FIN包同時(shí)發(fā)送給主動(dòng)端,這樣四次揮手變成三次。
如果雙端同時(shí)發(fā)起斷開連接的FIN包,TCP狀態(tài)如何轉(zhuǎn)移呢?
雙方同時(shí)發(fā)送FIN包后兩者都進(jìn)入FIN_WAIT_1狀態(tài),如果FIN_WAIT_1狀態(tài)收到FIN包,會(huì)直接進(jìn)入CLOSING狀態(tài),在CLOSING狀態(tài)下收到自己發(fā)送的FIN包的ACK包后,進(jìn)入TIME_WAIT狀態(tài),即雙方可能出現(xiàn)完全一樣的狀態(tài),并同時(shí)進(jìn)入TIME_WAIT狀態(tài),如下圖:
PART2: TIME_WAIT狀態(tài)問題
為什么要有TIME_WAIT狀態(tài)?
如果沒有TIME_WAIT狀態(tài),主動(dòng)方發(fā)送對(duì)FIN的ACK包后就關(guān)掉,而ACK包在路由過程中丟掉了,被動(dòng)方?jīng)]有收到,就會(huì)超時(shí)重傳FIN數(shù)據(jù)包,此時(shí)主動(dòng)方已經(jīng)關(guān)閉,被動(dòng)方便無法正常關(guān)閉連接;所以需要有TIME_WAIT狀態(tài)以便能夠重發(fā)丟掉的被動(dòng)方的FIN的ACK包。且TIME_WAIT狀態(tài)需要保持2*MSL(Max Segment Life Time TCP報(bào)文在網(wǎng)絡(luò)中的最大生存時(shí)間)。
TIME_WAIT會(huì)帶來哪些問題?
由于TIME_WAIT狀態(tài)需要等待2*MSL,才能斷開連接釋放占用的資源。會(huì)造成以下問題:
可以進(jìn)行TIME_WAIT的快速回收和重用來緩解。
作為服務(wù)端,短時(shí)間內(nèi)存關(guān)閉了大量的client連接,會(huì)造成服務(wù)器上出現(xiàn)大量的TIME_WAIT狀態(tài)的連接,占據(jù)大量的tuple,嚴(yán)重消耗服務(wù)器資源 作為客戶端,短時(shí)間內(nèi)大量的斷開連接,會(huì)大量消耗客戶端機(jī)器的端口,畢竟端口只有65535個(gè),端口被耗盡,便無法發(fā)起新的連接。 TIME_WAIT的快速回收和重用
新連接SYN告知的初始序列號(hào)比TIME_WAIT老鏈接的序列號(hào)大 新到來的連接的時(shí)間戳比老連接的時(shí)間戳大 來自同一對(duì)端的TCP包攜帶了時(shí)間戳 之前某一臺(tái)機(jī)器的某個(gè)tcp數(shù)據(jù)在MSL時(shí)間內(nèi)到過本服務(wù)器 機(jī)器新連接的時(shí)間戳小于上次TCP到來的時(shí)間戳,且差值大于重放窗口戳(TCP_PAWS_WINDOW)。 TIME_WAIT快速回收
Linux同時(shí)打開
tcp_tw_recycle和tcp_timestamps(默認(rèn)打開)兩個(gè)選項(xiàng)開啟快速回收Linux下快速回收的時(shí)間為3.5 * RTO(Retransmission Timeout)。但是,開啟快速回收TIME_WAIT,可能造成同時(shí)滿足以下三種情況導(dǎo)致新連接被拒絕:
“
NAT(Network Address Translator)出現(xiàn)是為了緩解IP地址耗盡的臨時(shí)方案,
NAT:允許一個(gè)整體機(jī)構(gòu)以一個(gè)公用IP(Internet Protocol)地址出現(xiàn)在Internet上,內(nèi)部私有網(wǎng)絡(luò)地址(IP地址)翻譯成合法網(wǎng)絡(luò)IP地址的技術(shù)
NAT分為三種:靜態(tài)NAT(Static NAT)、動(dòng)態(tài)地址NAT(Pooled NAT)、網(wǎng)絡(luò)地址端口轉(zhuǎn)換NAPT(Port-Level NAT)
”但是需要考慮NAT(Network Address Translation網(wǎng)絡(luò)地址轉(zhuǎn)換):在一個(gè) NAT 后面的所有機(jī)器在服務(wù)端看來都是一個(gè)機(jī)器(同一個(gè)公網(wǎng)IP),NAT 后面的那么多機(jī)器的系統(tǒng)時(shí)間戳很可能不一致,有些快,有些慢,會(huì)導(dǎo)致丟包無法連接的情況,尤其在上網(wǎng)高峰期,由于網(wǎng)絡(luò)的擁塞,可能會(huì)導(dǎo)致先發(fā)出的 TCP 包后到達(dá)服務(wù)器的情況,導(dǎo)致服務(wù)器不響應(yīng)。
TIME_WAIT重用
Linux同時(shí)開啟
tcp_tw_reuse選項(xiàng)和tcp_timestamps選項(xiàng)開啟TIME_WAIT重用,還有一個(gè)條件:重用 TIME_WAIT 的條件是收到最后一個(gè)包后超過 1s。只要滿足下面兩個(gè)點(diǎn)中的一點(diǎn),一個(gè)TW狀態(tài)的四元組(即一個(gè)socket連接)可以重新被新到來的SYN連接使用:重用在NAT環(huán)境下,依然存在風(fēng)險(xiǎn),因?yàn)闀r(shí)間戳重用 TIME_WAIT 連接的機(jī)制的前提是 IP 地址唯一性,而NAT環(huán)境下所有的機(jī)器都屬同一個(gè)公網(wǎng)IP。
重用其實(shí)沒有解決TIME_WAIT造成的資源消耗問題,Linux中可以通過修改tcp_max_tw_buckets這個(gè)值來控制并發(fā)的TIME_WAIT數(shù)量。
PART3: TCP的可靠性和流量控制問題
TCP可靠性表現(xiàn)在:
對(duì)每個(gè)包提供校驗(yàn)和 包的序列號(hào)解決了亂序,重復(fù)問題 重傳機(jī)制 流量控制,擁塞控制機(jī)制
1. TCP確認(rèn)機(jī)制
如果TCP對(duì)每個(gè)SYN包都進(jìn)行確認(rèn),那么網(wǎng)絡(luò)中會(huì)出現(xiàn)大量ACK包,消耗大量帶寬,降低網(wǎng)絡(luò)利用率。因此通過延時(shí)確認(rèn)機(jī)制和累計(jì)式確認(rèn)機(jī)制來減少ACK包數(shù)量,并且將ACK包和數(shù)據(jù)一起傳輸提高效率。
累計(jì)式確認(rèn)機(jī)制:即確認(rèn)號(hào)X的確認(rèn)表明所有X之前但不包括X的數(shù)據(jù)已經(jīng)收到,而不是對(duì)所有順序包進(jìn)行ACK
采用延遲確認(rèn),ACK在收到數(shù)據(jù)后并不馬上回復(fù),而是延遲一段時(shí)間再回復(fù)
2. TCP重傳機(jī)制
超時(shí)重傳:發(fā)送完SYN包之后會(huì)開啟一個(gè)timer,timer到了還沒有收到ACK的話,就重傳SYN。那么這個(gè)timer如何設(shè)置呢?如果太短的話ACK可能還在路上,會(huì)造成重傳浪費(fèi),過多的重傳會(huì)造成網(wǎng)絡(luò)擁塞,進(jìn)一步加劇數(shù)據(jù)丟失,太長(zhǎng)的話,效率又太差。因此,合理的做法就是應(yīng)當(dāng)根據(jù)網(wǎng)絡(luò)實(shí)際情況進(jìn)行調(diào)整,一般根據(jù)往返時(shí)間RTT(Round Trip Time)來設(shè)置RTO(Retransmission TimeOut),一般RTO稍微大于RTT。
快速重傳:如果連續(xù)收到3次相同確認(rèn)號(hào)ACK的包,就立刻進(jìn)行重傳,因?yàn)檫B續(xù)收到3個(gè)相同ACK,表明當(dāng)前網(wǎng)絡(luò)狀態(tài)好。
SACK確認(rèn)機(jī)制(Selective Acknowledement機(jī)制):這種重傳方法不會(huì)重傳丟失的第一個(gè)包后面的所有包,而是只針對(duì)性的傳丟失的包,在TCP首部Option字段中加上SACK即可開啟。
3. 流量控制
TCP首部有一個(gè)2個(gè)字節(jié)大小的Window字段,其最大為(2^16=65535)個(gè)字節(jié)。還有一個(gè)TCP窗口擴(kuò)大因子,來用擴(kuò)大大Window大小。這個(gè)窗口是接收端告訴發(fā)送端自己還有多少緩沖區(qū)可以接收數(shù)據(jù),發(fā)送端根據(jù)這個(gè)窗口來調(diào)整發(fā)送數(shù)據(jù)的速率,進(jìn)而達(dá)到端對(duì)端的流量控制。
TCP把要發(fā)送的數(shù)據(jù)放入發(fā)送緩沖區(qū)(Send Buffer),接收到的數(shù)據(jù)放入接收緩沖區(qū)(Receive Buffer),應(yīng)用程序會(huì)不停的讀取接收緩沖區(qū)的內(nèi)容進(jìn)行處理;
流量控制做的事情就是,如果接收緩沖區(qū)已滿,發(fā)送端應(yīng)該停止發(fā)送數(shù)據(jù),為了控制發(fā)送端速率,接收端會(huì)告知客戶端自己接收窗口rwnd,也就是接收緩沖區(qū)中的空閑部分。
滑動(dòng)窗口工作原理
發(fā)送端維護(hù)一個(gè)跟接收端大小一樣的發(fā)送窗口,窗口內(nèi)的可發(fā),窗口外的不可,窗口在發(fā)送序列上不斷后移。如下圖:
從上圖可看出,TCP發(fā)送端數(shù)據(jù)可分為4類,其中2,3兩部分合起來稱之為發(fā)送窗口,從左往右依次為:
下圖演示窗口滑動(dòng)情況,收到36的ACK后,窗口向后滑動(dòng)5個(gè)Byte
已經(jīng)發(fā)送并得到接收端ACK的 已經(jīng)發(fā)送但未收到接收端ACK的 未發(fā)送但允許發(fā)送的(接收方還有空間) 未發(fā)送且不允許發(fā)送的(接收方?jīng)]空間) 0窗口問題
如果發(fā)送端收到一個(gè)0窗口,發(fā)送端是不能再發(fā)送數(shù)據(jù)的,如下圖:
但是如果接收端一直發(fā)送0窗口呢?發(fā)送端會(huì)一直等待嗎?答案是否定的。TCP采用Zero Window Probe(ZWP零窗口探測(cè))技術(shù),發(fā)送端窗口變成0后,會(huì)ZWP給接收端,來探測(cè)窗口大小,三次探測(cè)后如果還是0的話,會(huì)RST調(diào)這個(gè)連接。
DDos攻擊點(diǎn):攻擊者與服務(wù)端建立連接后,向服務(wù)端通知一個(gè)0窗口,服務(wù)端只能等待ZWP,如果攻擊者發(fā)送大量這樣的請(qǐng)求,會(huì)耗盡服務(wù)端資源。
糊涂窗口綜合征問題(Silly Window Syndrome)
也即小包問題,如果20個(gè)字節(jié)TCP首部,20個(gè)字節(jié)IP首部,只傳4個(gè)字節(jié)的數(shù)據(jù),那未免太浪費(fèi)資源了,造成這個(gè)問題主要有以下兩個(gè)原因:
針對(duì)上述問題,分別由兩種解決方案:
Nagle算法:接收端不通知小窗口,僅長(zhǎng)度大于MSS,或包含F(xiàn)IN,或設(shè)置了TCP_NODELAY,或超時(shí)等情況才會(huì)立即發(fā)送;(因此可以通過設(shè)置TCP_NODELAY來禁用Nagle算法
setsockopt(..., TCP_NODELAY))發(fā)送端積累一下數(shù)據(jù)再發(fā)送
接收端一直在通知一個(gè)小窗口 發(fā)送端本身一直在發(fā)送小包
4. 擁塞控制
既然有了流量控制,為啥還需要擁塞控制?因?yàn)榱髁靠刂漆槍?duì)的是端對(duì)端,只能看到對(duì)端情況,無法知道整個(gè)鏈路上的狀況,只要雙方處理能力強(qiáng),就可以很大速率發(fā)包,造成鏈路擁堵,引起大量丟包,大量丟包會(huì)導(dǎo)致大量重傳,進(jìn)一步加劇鏈路擁塞。因此需要擁塞控制來維護(hù)整個(gè)網(wǎng)絡(luò)的交通,在之前接收窗口(rwnd)的基礎(chǔ)上,加入一個(gè)擁塞窗口(cwnd),發(fā)送端真正窗口=min(rwnd, cwnd).
擁塞控制主要包括四個(gè)部分:慢啟動(dòng)算法,擁塞避免算法,快速重傳算法,快速恢復(fù)算法。
慢啟動(dòng)算法
慢啟動(dòng)體現(xiàn)了一個(gè)試探的過程,剛接入網(wǎng)絡(luò)的時(shí)候先發(fā)包慢點(diǎn),探測(cè)一下網(wǎng)絡(luò)情況,然后在慢慢提速。不要一上來就拼命發(fā)包,這樣很容易造成鏈路的擁堵,出現(xiàn)擁堵了在想到要降速來緩解擁堵這就有點(diǎn)成本高了,畢竟無數(shù)的先例告誡我們先污染后治理的成本是很高的。步驟如下:
連接建好初始化cwnd = N 每接收一個(gè)ACK,++cwnd,線性上升 每過一個(gè)RTT,cwnd = cwnd * 2, 指數(shù)上升 慢啟動(dòng)門限ssthresh(slow start threadhold),當(dāng)cwnd >= ssthresh,進(jìn)入擁塞避免算法 擁塞避免算法
每收到一個(gè)ACK,cwnd=(cwnd + 1 / cwnd) * MSS個(gè)字節(jié) 每經(jīng)過一個(gè)RTT時(shí)長(zhǎng),cwnd+=1 快速重傳算法
(正常重傳需要等幾百毫秒)當(dāng)接收端收到一個(gè)不按序到達(dá)的數(shù)據(jù)段時(shí),TCP立刻發(fā)送1個(gè)重復(fù)ACK,當(dāng)發(fā)送端收到3個(gè)或以上重復(fù)ACK,就意識(shí)到之前發(fā)的包可能丟了,就立馬重傳,而不用等重傳定時(shí)器超時(shí)再重傳。并進(jìn)入快速恢復(fù)階段,步驟如下:
調(diào)整門限sthresh的值為當(dāng)前cwnd的1/2 將cwnd設(shè)置為新的ssthresh 重新進(jìn)入擁塞避免階段 快速恢復(fù)算法
解釋為網(wǎng)絡(luò)輕度擁塞,步驟如下:
擁塞閾值ssthread降低為cwnd的一半; 擁塞窗口cwnd設(shè)置為ssthresh 擁塞窗口線性增加
PART4: TCP粘包、拆包及解決方法
1. 什么是粘包、拆包?
假設(shè) Client 向 Server 連續(xù)發(fā)送了兩個(gè)數(shù)據(jù)包,用 packet1 和 packet2 來表示,那么服務(wù)端收到的數(shù)據(jù)可以分為三種情況,現(xiàn)列舉如下:
第一種情況,接收端正常收到兩個(gè)數(shù)據(jù)包,即沒有發(fā)生拆包和粘包的現(xiàn)象。
第二種情況,接收端只收到一個(gè)數(shù)據(jù)包,但是這一個(gè)數(shù)據(jù)包中包含了發(fā)送端發(fā)送的兩個(gè)數(shù)據(jù)包的信息,這種現(xiàn)象即為粘包。這種情況由于接收端不知道這兩個(gè)數(shù)據(jù)包的界限,所以對(duì)于接收端來說很難處理。
第三種情況,這種情況有兩種表現(xiàn)形式,如下圖。接收端收到了兩個(gè)數(shù)據(jù)包,但是這兩個(gè)數(shù)據(jù)包要么是不完整的,要么就是多出來一塊,這種情況即發(fā)生了拆包和粘包。這兩種情況如果不加特殊處理,對(duì)于接收端同樣是不好處理的。
2. 為什么TCP有粘包和拆包,而不說UDP?
UDP 是基于報(bào)文發(fā)送的,UDP首部采用了 16bit 來指示 UDP 數(shù)據(jù)報(bào)文的長(zhǎng)度,因此在應(yīng)用層能很好的將不同的數(shù)據(jù)報(bào)文區(qū)分開,從而避免粘包和拆包的問題。
而 TCP 是基于字節(jié)流的,雖然應(yīng)用層和 TCP 傳輸層之間的數(shù)據(jù)交互是大小不等的數(shù)據(jù)塊,但是 TCP 并沒有把這些數(shù)據(jù)塊區(qū)分邊界,僅僅是一連串沒有結(jié)構(gòu)的字節(jié)流;另外從 TCP 的幀結(jié)構(gòu)也可以看出,在 TCP 的首部沒有表示數(shù)據(jù)長(zhǎng)度的字段,基于上面兩點(diǎn),在使用 TCP 傳輸數(shù)據(jù)時(shí),才有粘包或者拆包現(xiàn)象發(fā)生的可能。
另外,TCP短連接不用處理分包,因?yàn)榘l(fā)送方主動(dòng)關(guān)閉連接后,表示一條消息發(fā)送完畢,接收方read返回0,從而知道消息的結(jié)尾;只有長(zhǎng)連接采用處理
為什么會(huì)發(fā)生TCP粘包、拆包?
要發(fā)送的數(shù)據(jù)大于TCP發(fā)送緩沖區(qū)剩余空間大?。ㄓ山邮斩舜翱跊Q定,流量控制),將會(huì)進(jìn)行拆包
待發(fā)送數(shù)據(jù)大于MSS(最大報(bào)文長(zhǎng)度),TCP在傳輸前將進(jìn)行拆包
要發(fā)送的數(shù)據(jù)小于TCP發(fā)送緩沖區(qū)的大小,TCP將多次寫入緩沖區(qū)的數(shù)據(jù)一次發(fā)送出去,將會(huì)發(fā)生粘包
接收端應(yīng)用層沒有及時(shí)讀取接收緩沖區(qū)中的數(shù)據(jù),將會(huì)發(fā)生粘包
粘包、拆包解決辦法
由于 TCP 本身是面向字節(jié)流的,無法理解上層的業(yè)務(wù)數(shù)據(jù),所以在底層是無法保證數(shù)據(jù)包不被拆分和重組的,這個(gè)問題只能通過上層的應(yīng)用協(xié)議棧設(shè)計(jì)來解決,根據(jù)業(yè)界的主流協(xié)議的解決方案,歸納如下
消息定長(zhǎng):發(fā)送端將每個(gè)數(shù)據(jù)包封裝為固定長(zhǎng)度(不夠的可以通過補(bǔ) 0 填充),這樣接收端每次接收緩沖區(qū)中讀取固定長(zhǎng)度的數(shù)據(jù)就自然而然的把每個(gè)數(shù)據(jù)包拆分開來。 設(shè)置消息邊界:服務(wù)端從網(wǎng)絡(luò)流中按消息邊界分離出消息內(nèi)容。在包尾增加回車換行符進(jìn)行分割,例如 FTP 協(xié)議。 消息頭+消息體結(jié)構(gòu):消息頭中包含表示消息總長(zhǎng)度(或者消息體長(zhǎng)度)的字段;收到包時(shí),先接收固定字節(jié)數(shù)的頭部,解出這個(gè)包完整長(zhǎng)度,按照此長(zhǎng)度接收包體。(這是目前網(wǎng)絡(luò)應(yīng)用中用的最多);比如,每個(gè)消息有4字節(jié)長(zhǎng)度,以網(wǎng)絡(luò)序存放字符串長(zhǎng)度,比如"hello" 和 "world!"兩條消息,打包后的字節(jié)流為 0x00, 0x00, 0x00, 0x05,'h','e','l','l','o',0x00,0x00,0x00,0x06,'w','o','r','l','d','!'利用消息本身的格式來分包:比如XML格式的消息中...的配對(duì),JSON格式中的{...}配對(duì)。解析這種消息格式通常會(huì)用到狀態(tài)機(jī)。
TCP短連接和長(zhǎng)連接的區(qū)別?
短連接:Client向Serer發(fā)送消息,Server回應(yīng)Client,一次讀寫完成,雙方都可發(fā)起close操作。適用于web網(wǎng)站
長(zhǎng)連接:Client與Server完成一次讀寫后,雙方不會(huì)主動(dòng)關(guān)閉連接。靠心跳維持,需要一定的系統(tǒng)開銷,適用于頻繁交互的場(chǎng)景,如即時(shí)通信工具:微信,游戲等
發(fā)送緩沖區(qū)和接收緩沖區(qū)
用戶態(tài)進(jìn)程調(diào)用描述符上的read,它會(huì)導(dǎo)致內(nèi)核從其接收緩沖區(qū)中刪除數(shù)據(jù),并將該數(shù)據(jù)復(fù)制到此進(jìn)程調(diào)用所提供的緩沖區(qū)中。
用戶態(tài)調(diào)用write時(shí),會(huì)將數(shù)據(jù)從用戶提供的緩沖區(qū)復(fù)制到內(nèi)核寫入隊(duì)列,內(nèi)核再將數(shù)據(jù)從寫入隊(duì)列復(fù)制到NIC中。
首先網(wǎng)卡將接收到的數(shù)據(jù)放到內(nèi)核緩沖區(qū),內(nèi)核緩沖區(qū)存放的數(shù)據(jù)根據(jù)TCP信息將數(shù)據(jù)移動(dòng)到具體的某一個(gè)TCP連接上的接收緩沖區(qū)內(nèi),也就是TCP接收滑動(dòng)窗口內(nèi),然后應(yīng)用程序從TCP的接收緩沖區(qū)內(nèi)讀取數(shù)據(jù),如果應(yīng)用程序一直不讀取,那么滑動(dòng)窗口會(huì)變小,直至為0
TCP發(fā)給對(duì)方的數(shù)據(jù),對(duì)方在收到數(shù)據(jù)時(shí)必須給矛確認(rèn),只有在收到對(duì)方的確認(rèn)時(shí),本方TCP才會(huì)把TCP發(fā)送緩沖區(qū)中的數(shù)據(jù)刪除。
UDP因?yàn)槭遣豢煽窟B接,不必保存應(yīng)用進(jìn)程的數(shù)據(jù)拷貝,應(yīng)用進(jìn)程中的數(shù)據(jù)在沿協(xié)議棧向下傳遞時(shí),以某種形式拷貝到內(nèi)核緩沖區(qū),當(dāng)數(shù)據(jù)鏈路層把數(shù)據(jù)傳出后就把內(nèi)核緩沖區(qū)中數(shù)據(jù)拷貝刪除。因此它不需要一個(gè)發(fā)送緩沖區(qū)。
tcp socket的發(fā)送緩沖區(qū)實(shí)際上是一個(gè)結(jié)構(gòu)體struct sk_buff的隊(duì)列,我們可以把它稱為發(fā)送緩沖隊(duì)列,分配一個(gè)struct sk_buff是用于存放一個(gè)tcp數(shù)據(jù)報(bào)
packetdrill
最后介紹一個(gè)學(xué)習(xí)TCP的工具packeetdrill。其它實(shí)例參考這里
packetdrill原理:在執(zhí)行腳本前創(chuàng)建了一個(gè)名為tun0的虛擬網(wǎng)卡,腳本執(zhí)行完后,tun0會(huì)被銷毀。該虛擬網(wǎng)卡對(duì)應(yīng)于操作系統(tǒng)中/dev/net/tun文件,每次程序通過write等系統(tǒng)調(diào)用將數(shù)據(jù)寫入到這個(gè)文件fd時(shí),這些數(shù)據(jù)會(huì)經(jīng)過tun0這個(gè)虛擬網(wǎng)卡,將數(shù)據(jù)寫入到內(nèi)核協(xié)議棧,read系統(tǒng)調(diào)用讀取數(shù)據(jù)的過程類似。協(xié)議棧可以向操作普通網(wǎng)卡一樣操作虛擬網(wǎng)卡tun0.
packetdrill example
使用packetetdrill構(gòu)造一個(gè)SYN_SENT狀態(tài)
# 構(gòu)造一個(gè)SYN_SENT狀態(tài)的連接
+0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 // 新建一個(gè)server socket
+0 connect(3, ..., ...) = -1 // 客戶端connect
執(zhí)行netstat命令可查看相關(guān)狀態(tài)
netstat -atnp | grep -i 8080
tcp 0 ... SYN_SENT
SYN包重傳了6次后關(guān)閉連接,通過參數(shù)tcp_syn_retries可以看到
cat /proc/sys/net/ipv4/tcp_syn_retries
tcpdump抓包指令使用
$ tcpdump -i any
-i 指定哪個(gè)網(wǎng)卡,any表示任意,有哪些網(wǎng)卡可以用ifconfig來查看
# 過濾主機(jī):host選項(xiàng)
tcpdump -i any host 10.211.55.2 # 只查看10.211.55.2的網(wǎng)絡(luò)包,ip可以源地址,也可以是目的地址
# 過濾源地址,目的地址:src,dst
tcpdump -i any src 10.211.55.10 # 只抓取hostnum發(fā)出的包
tcpdump -i any dst hostnum # 只抓取hostunm收到的包
# 過濾端口,port選項(xiàng)
tcpdump -i any port 80 # 只查看80端口
總結(jié)
主要講了計(jì)算機(jī)網(wǎng)絡(luò)分層模型,TCP/IP協(xié)議頭實(shí)現(xiàn),TCP面試常考點(diǎn):三次握手,四次揮手問題;TIME_WAIT狀態(tài)問題;TCP的可靠性和流量控制問題;TCP粘包、拆包問題;以及packetdrill工具使用。
“參考:
《計(jì)算機(jī)網(wǎng)絡(luò)》
《TCP/IP詳解 卷一》
《Tcp小冊(cè)》
”
—————END————— 推薦閱讀:
IDEA 永久注冊(cè)碼來了?。?! 一個(gè)非常好的行為驗(yàn)證碼Java開源項(xiàng)目! Google 開源的依賴注入庫(kù),比 Spring 更小更快! GitHub 近兩萬 Star,無需編碼,可一鍵生成前后端代碼 Spring Boot 中引入 MyBatisPlus 的常規(guī)流程 免費(fèi)版的 IDEA 為啥不能使用 Tomcat ? 給新手的 11 個(gè) Docker 免費(fèi)上手項(xiàng)目
最近面試BAT,整理一份面試資料《Java面試BAT通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。 獲取方式:關(guān)注公眾號(hào)并回復(fù) java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。



















