大家好,æˆ‘æ˜¯å°æž—,一个专为大家图解的工具人。
ä¸ç®¡é¢è¯• Java ã€C/C++ã€Python ç‰å¼€å‘å²—ä½, TCP 的知识点å¯ä»¥è¯´æ˜¯å¿…问的了。
ä»» TCP è™æˆ‘åƒç™¾é,我ä»å¾… TCP å¦‚åˆæ‹ã€‚
过去ä¸ä¼šæ²¡å…³ç³»,ä»Šå¤©å°±è®©æˆ‘ä»¬æ¥æ¶ˆé™¤è¿™ä»½ææƒ§,微笑ç€å‹‡æ•¢çš„é¢å¯¹å®ƒå§!
æ‰€ä»¥å°æž—æ•´ç†äº†å…³äºŽ TCP çš„é¢è¯•题型,全文共 3 ä¸‡å— + 100 å¼ å›¾,跟大家一起探讨探讨。
1〠TCP 基本认识
2ã€TCP 连接建立
3〠TCP 连接æ–å¼€
4〠Socket 编程
5ã€TCP é‡ä¼ ã€æ»‘动窗å£ã€æµé‡æŽ§åˆ¶ã€æ‹¥å¡žæŽ§åˆ¶
01 TCP 基本认识
çž§çž§ TCP å¤´æ ¼å¼
我们先æ¥çœ‹çœ‹ TCP å¤´çš„æ ¼å¼,æ ‡æ³¨é¢œè‰²çš„è¡¨ç¤ºä¸Žæœ¬æ–‡å…³è”æ¯”è¾ƒå¤§çš„å—æ®µ,å…¶ä»–å—æ®µä¸åšè¯¦ç»†é˜è¿°ã€‚
åºåˆ—å·:在建立连接时由计算机生æˆçš„éšæœºæ•°ä½œä¸ºå…¶åˆå§‹å€¼,通过 SYN åŒ…ä¼ ç»™æŽ¥æ”¶ç«¯ä¸»æœº,æ¯å‘é€ä¸€æ¬¡æ•°æ®,å°±ã€Œç´¯åŠ ã€ä¸€æ¬¡è¯¥ã€Œæ•°æ®å—节数ã€çš„大å°ã€‚用æ¥è§£å†³ç½‘络包乱åºé—®é¢˜ã€‚
确认应ç”å·:æŒ‡ä¸‹ä¸€æ¬¡ã€ŒæœŸæœ›ã€æ”¶åˆ°çš„æ•°æ®çš„åºåˆ—å·,å‘é€ç«¯æ”¶åˆ°è¿™ä¸ªç¡®è®¤åº”ç”以åŽå¯ä»¥è®¤ä¸ºåœ¨è¿™ä¸ªåºå·ä»¥å‰çš„æ•°æ®éƒ½å·²ç»è¢«æ£å¸¸æŽ¥æ”¶ã€‚用æ¥è§£å†³ä¸ä¸¢åŒ…的问题。
控制ä½:
- ACK:该ä½ä¸º
1æ—¶,「确认应ç”ã€çš„å—æ®µå˜ä¸ºæœ‰æ•ˆ,TCP 规定除了最åˆå»ºç«‹è¿žæŽ¥æ—¶çš„SYN包之外该ä½å¿…须设置为1。 - RST:该ä½ä¸º
1æ—¶,表示 TCP 连接ä¸å‡ºçŽ°å¼‚å¸¸å¿…é¡»å¼ºåˆ¶æ–开连接。 - SYN:该ä½ä¸º
1æ—¶,表示希望建立连接,并在其「åºåˆ—å·ã€çš„å—æ®µè¿›è¡Œåºåˆ—å·åˆå§‹å€¼çš„设定。 - FIN:该ä½ä¸º
1æ—¶,表示今åŽä¸ä¼šå†æœ‰æ•°æ®å‘é€,希望æ–开连接。当通信结æŸå¸Œæœ›æ–开连接时,é€šä¿¡åŒæ–¹çš„主机之间就å¯ä»¥ç›¸äº’交æ¢FINä½ä¸º 1 çš„ TCP 段。
ä¸ºä»€ä¹ˆéœ€è¦ TCP åè®®? TCP 工作在哪一层?
IP 层是「ä¸å¯é ã€çš„,它ä¸ä¿è¯ç½‘络包的交付ã€ä¸ä¿è¯ç½‘络包的按åºäº¤ä»˜ã€ä¹Ÿä¸ä¿è¯ç½‘络包ä¸çš„æ•°æ®çš„完整性。
如果需è¦ä¿éšœç½‘络数æ®åŒ…çš„å¯é 性,那么就需è¦ç”±ä¸Šå±‚(ä¼ è¾“å±‚)çš„ TCP åè®®æ¥è´Ÿè´£ã€‚
å› ä¸º TCP æ˜¯ä¸€ä¸ªå·¥ä½œåœ¨ä¼ è¾“å±‚çš„å¯é æ•°æ®ä¼ 输的æœåŠ¡,å®ƒèƒ½ç¡®ä¿æŽ¥æ”¶ç«¯æŽ¥æ”¶çš„ç½‘ç»œåŒ…æ˜¯æ— æŸåã€æ— é—´éš”ã€éžå†—余和按åºçš„。
什么是 TCP ?
TCP 是é¢å‘连接的ã€å¯é çš„ã€åŸºäºŽå—节æµçš„ä¼ è¾“å±‚é€šä¿¡å议。
-
é¢å‘连接:ä¸€å®šæ˜¯ã€Œä¸€å¯¹ä¸€ã€æ‰èƒ½è¿žæŽ¥,ä¸èƒ½åƒ UDP åè®®å¯ä»¥ä¸€ä¸ªä¸»æœºåŒæ—¶å‘多个主机å‘逿¶ˆæ¯,ä¹Ÿå°±æ˜¯ä¸€å¯¹å¤šæ˜¯æ— æ³•åšåˆ°çš„;
-
å¯é çš„:æ— è®ºçš„ç½‘ç»œé“¾è·¯ä¸å‡ºçŽ°äº†æ€Žæ ·çš„é“¾è·¯å˜åŒ–,TCP 都å¯ä»¥ä¿è¯ä¸€ä¸ªæŠ¥æ–‡ä¸€å®šèƒ½å¤Ÿåˆ°è¾¾æŽ¥æ”¶ç«¯;
-
å—节æµ:æ¶ˆæ¯æ˜¯ã€Œæ²¡æœ‰è¾¹ç•Œã€çš„,æ‰€ä»¥æ— è®ºæˆ‘ä»¬æ¶ˆæ¯æœ‰å¤šå¤§éƒ½å¯ä»¥è¿›è¡Œä¼ è¾“ã€‚å¹¶ä¸”æ¶ˆæ¯æ˜¯ã€Œæœ‰åºçš„ã€,当「å‰ä¸€ä¸ªã€æ¶ˆæ¯æ²¡æœ‰æ”¶åˆ°çš„æ—¶å€™,å³ä½¿å®ƒå…ˆæ”¶åˆ°äº†åŽé¢çš„å—节,那么也ä¸èƒ½æ‰”给应用层去处ç†,åŒæ—¶å¯¹ã€Œé‡å¤ã€çš„æŠ¥æ–‡ä¼šè‡ªåŠ¨ä¸¢å¼ƒã€‚
什么是 TCP 连接?
我们æ¥çœ‹çœ‹ RFC 793 是如何定义「连接ã€çš„:
Connections:
The reliability and flow control mechanisms described above require
that TCPs initialize and maintain certain status information for
each data stream. The combination of this information, including
sockets, sequence numbers, and window sizes, is called a connection.
ç®€å•æ¥è¯´å°±æ˜¯,用于ä¿è¯å¯é 性和æµé‡æŽ§åˆ¶ç»´æŠ¤çš„æŸäº›çŠ¶æ€ä¿¡æ¯,这些信æ¯çš„组åˆ,包括Socketã€åºåˆ—å·å’Œçª—å£å¤§å°ç§°ä¸ºè¿žæŽ¥ã€‚
所以我们å¯ä»¥çŸ¥é“,建立一个 TCP 连接是需è¦å®¢æˆ·ç«¯ä¸ŽæœåŠ¡å™¨ç«¯è¾¾æˆä¸Šè¿°ä¸‰ä¸ªä¿¡æ¯çš„共识。
- Socket:ç”± IP 地å€å’Œç«¯å£å·ç»„æˆ
- åºåˆ—å·:用æ¥è§£å†³ä¹±åºé—®é¢˜ç‰
- 窗å£å¤§å°:用æ¥åšæµé‡æŽ§åˆ¶
如何唯一确定一个 TCP 连接呢?
TCP 四元组å¯ä»¥å”¯ä¸€çš„确定一个连接,四元组包括如下:
- æºåœ°å€
- æºç«¯å£
- 目的地å€
- 目的端å£
æºåœ°å€å’Œç›®çš„地å€çš„å—æ®µ(32ä½)是在 IP 头部ä¸,作用是通过 IP åè®®å‘é€æŠ¥æ–‡ç»™å¯¹æ–¹ä¸»æœºã€‚
æºç«¯å£å’Œç›®çš„端å£çš„å—æ®µ(16ä½)是在 TCP 头部ä¸,作用是告诉 TCP å议应该把报文å‘给哪个进程。
有一个 IP çš„æœåŠ¡å™¨ç›‘å¬äº†ä¸€ä¸ªç«¯å£,它的 TCP 的最大连接数是多少?
æœåŠ¡å™¨é€šå¸¸å›ºå®šåœ¨æŸä¸ªæœ¬åœ°ç«¯å£ä¸Šç›‘å¬,ç‰å¾…客户端的连接请求。
å› æ¤,客户端 IP å’Œ ç«¯å£æ˜¯å¯å˜çš„,å…¶ç†è®ºå€¼è®¡ç®—å…¬å¼å¦‚下:
对 IPv4,客户端的 IP 数最多为 2 çš„ 32 次方,å®¢æˆ·ç«¯çš„ç«¯å£æ•°æœ€å¤šä¸º 2 çš„ 16 次方,也就是æœåŠ¡ç«¯å•æœºæœ€å¤§ TCP 连接数,约为 2 çš„ 48 次方。
当然,æœåŠ¡ç«¯æœ€å¤§å¹¶å‘ TCP 连接数远ä¸èƒ½è¾¾åˆ°ç†è®ºä¸Šé™ã€‚
- é¦–å…ˆä¸»è¦æ˜¯æ–‡ä»¶æè¿°ç¬¦é™åˆ¶,Socket 都是文件,所以首先è¦é€šè¿‡
ulimité…置文件æè¿°ç¬¦çš„æ•°ç›®; - å¦ä¸€ä¸ªæ˜¯å†…å˜é™åˆ¶,æ¯ä¸ª TCP 连接都è¦å 用一定内å˜,æ“ä½œç³»ç»Ÿçš„å†…å˜æ˜¯æœ‰é™çš„。
UDP å’Œ TCP 有什么区别呢?分别的应用场景是?
UDP 䏿供夿‚的控制机制,利用 IP æä¾›é¢å‘ã€Œæ— è¿žæŽ¥ã€çš„通信æœåŠ¡ã€‚
UDP å议真的éžå¸¸ç®€,å¤´éƒ¨åªæœ‰ 8 个å—节( 64 ä½),UDP çš„å¤´éƒ¨æ ¼å¼å¦‚下:
- ç›®æ ‡å’Œæºç«¯å£:ä¸»è¦æ˜¯å‘Šè¯‰ UDP å议应该把报文å‘给哪个进程。
- 包长度:è¯¥å—æ®µä¿å˜äº† UDP 首部的长度跟数æ®çš„长度之和。
- æ ¡éªŒå’Œ:æ ¡éªŒå’Œæ˜¯ä¸ºäº†æä¾›å¯é çš„ UDP 首部和数æ®è€Œè®¾è®¡ã€‚
TCP å’Œ UDP 区别:
1. 连接
- TCP 是é¢å‘è¿žæŽ¥çš„ä¼ è¾“å±‚åè®®,ä¼ è¾“æ•°æ®å‰å…ˆè¦å»ºç«‹è¿žæŽ¥ã€‚
- UDP 是ä¸éœ€è¦è¿žæŽ¥,å³åˆ»ä¼ 输数æ®ã€‚
2. æœåŠ¡å¯¹è±¡
- TCP 是一对一的两点æœåŠ¡,å³ä¸€æ¡è¿žæŽ¥åªæœ‰ä¸¤ä¸ªç«¯ç‚¹ã€‚
- UDP 支æŒä¸€å¯¹ä¸€ã€ä¸€å¯¹å¤šã€å¤šå¯¹å¤šçš„交互通信
3. å¯é 性
- TCP 是å¯é 交付数æ®çš„,æ•°æ®å¯ä»¥æ— 差错ã€ä¸ä¸¢å¤±ã€ä¸é‡å¤ã€æŒ‰éœ€åˆ°è¾¾ã€‚
- UDP 是尽最大努力交付,ä¸ä¿è¯å¯é 交付数æ®ã€‚
4. æ‹¥å¡žæŽ§åˆ¶ã€æµé‡æŽ§åˆ¶
- TCP 有拥塞控制和æµé‡æŽ§åˆ¶æœºåˆ¶,ä¿è¯æ•°æ®ä¼ 输的安全性。
- UDP 则没有,å³ä½¿ç½‘络éžå¸¸æ‹¥å µäº†,也ä¸ä¼šå½±å“ UDP çš„å‘é€é€ŸçŽ‡ã€‚
5. 首部开销
- TCP 首部长度较长,会有一定的开销,首部在没有使用「选项ã€å—段时是
20个å—节,如果使用了「选项ã€å—段则会å˜é•¿çš„。 - UDP é¦–éƒ¨åªæœ‰ 8 个å—节,并且是固定ä¸å˜çš„,开销较å°ã€‚
6. ä¼ è¾“æ–¹å¼
- TCP 是æµå¼ä¼ 输,没有边界,但ä¿è¯é¡ºåºå’Œå¯é 。
- UDP 是一个包一个包的å‘é€,是有边界的,但å¯èƒ½ä¼šä¸¢åŒ…和乱åºã€‚
7. 分片ä¸åŒ
- TCP 的数æ®å¤§å°å¦‚果大于 MSS 大å°,åˆ™ä¼šåœ¨ä¼ è¾“å±‚è¿›è¡Œåˆ†ç‰‡,ç›®æ ‡ä¸»æœºæ”¶åˆ°åŽ,ä¹ŸåŒæ ·åœ¨ä¼ 输层组装 TCP æ•°æ®åŒ…,如果ä¸é€”丢失了一个分片,åªéœ€è¦ä¼ 输丢失的这个分片。
- UDP 的数æ®å¤§å°å¦‚果大于 MTU 大å°,则会在 IP 层进行分片,ç›®æ ‡ä¸»æœºæ”¶åˆ°åŽ,在 IP 层组装完数æ®,接ç€å†ä¼ ç»™ä¼ è¾“å±‚,但是如果ä¸é€”丢了一个分片,则就需è¦é‡ä¼ 所有的数æ®åŒ…,è¿™æ ·ä¼ è¾“æ•ˆçŽ‡éžå¸¸å·®,所以通常 UDP 的报文应该å°äºŽ MTU。
TCP å’Œ UDP 应用场景:
由于 TCP 是é¢å‘连接,能ä¿è¯æ•°æ®çš„å¯é 性交付,å› æ¤ç»å¸¸ç”¨äºŽ:
FTPæ–‡ä»¶ä¼ è¾“HTTP/HTTPS
由于 UDP é¢å‘æ— è¿žæŽ¥,它å¯ä»¥éšæ—¶å‘逿•°æ®,å†åŠ ä¸ŠUDPæœ¬èº«çš„å¤„ç†æ—¢ç®€å•åˆé«˜æ•ˆ,å› æ¤ç»å¸¸ç”¨äºŽ:
- 包总é‡è¾ƒå°‘的通信,如
DNSã€SNMPç‰ - 视频ã€éŸ³é¢‘ç‰å¤šåª’体通信
- 广æ’通信
为什么 UDP 头部没有「首部长度ã€å—段,而 TCP 头部有「首部长度ã€å—段呢?
åŽŸå› æ˜¯ TCP 有å¯å˜é•¿çš„「选项ã€å—段,而 UDP 头部长度则是ä¸ä¼šå˜åŒ–çš„,æ— éœ€å¤šä¸€ä¸ªå—æ®µåŽ»è®°å½• UDP 的首部长度。
为什么 UDP 头部有「包长度ã€å—段,而 TCP 头部则没有「包长度ã€å—段呢?
先说说 TCP 是如何计算负载数æ®é•¿åº¦:
å…¶ä¸ IP 总长度 å’Œ IP 首部长度,在 IP é¦–éƒ¨æ ¼å¼æ˜¯å·²çŸ¥çš„。TCP 首部长度,则是在 TCP é¦–éƒ¨æ ¼å¼å·²çŸ¥çš„,所以就å¯ä»¥æ±‚å¾— TCP æ•°æ®çš„长度。
大家这时就奇怪了问:“ UDP 也是基于 IP 层的呀,é‚£ UDP 的数æ®é•¿åº¦ä¹Ÿå¯ä»¥é€šè¿‡è¿™ä¸ªå…¬å¼è®¡ç®—å‘€? ä¸ºä½•è¿˜è¦æœ‰ã€ŒåŒ…长度ã€å‘¢?â€
这么一问,确实感觉 UDP ã€ŒåŒ…é•¿åº¦ã€æ˜¯å†—余的。
å› ä¸ºä¸ºäº†ç½‘ç»œè®¾å¤‡ç¡¬ä»¶è®¾è®¡å’Œå¤„ç†æ–¹ä¾¿,é¦–éƒ¨é•¿åº¦éœ€è¦æ˜¯ 4å—节的整数å€ã€‚
如果去掉 UDP 「包长度ã€å—段,é‚£ UDP é¦–éƒ¨é•¿åº¦å°±ä¸æ˜¯ 4 å—节的整数å€äº†,æ‰€ä»¥å°æž—觉得这å¯èƒ½æ˜¯ä¸ºäº†è¡¥å…¨ UDP 首部长度是 4 å—节的整数å€,æ‰è¡¥å……了「包长度ã€å—段。
02 TCP 连接建立
TCP ä¸‰æ¬¡æ¡æ‰‹è¿‡ç¨‹å’Œçжæ€å˜è¿
TCP 是é¢å‘连接的åè®®,所以使用 TCP å‰å¿…须先建立连接,è€Œå»ºç«‹è¿žæŽ¥æ˜¯é€šè¿‡ä¸‰æ¬¡æ¡æ‰‹æ¥è¿›è¡Œçš„。
- 一开始,客户端和æœåŠ¡ç«¯éƒ½å¤„äºŽ
CLOSED状æ€ã€‚先是æœåŠ¡ç«¯ä¸»åŠ¨ç›‘å¬æŸä¸ªç«¯å£,处于LISTEN状æ€
- å®¢æˆ·ç«¯ä¼šéšæœºåˆå§‹åŒ–åºå·(
client_isn),å°†æ¤åºå·ç½®äºŽ TCP 首部的「åºå·ã€å—段ä¸,åŒæ—¶æŠŠSYNæ ‡å¿—ä½ç½®ä¸º1,表示SYNæŠ¥æ–‡ã€‚æŽ¥ç€æŠŠç¬¬ä¸€ä¸ª SYN 报文å‘é€ç»™æœåŠ¡ç«¯,è¡¨ç¤ºå‘æœåŠ¡ç«¯å‘起连接,该报文ä¸åŒ…å«åº”用层数æ®,之åŽå®¢æˆ·ç«¯å¤„于SYN-SENT状æ€ã€‚
- æœåŠ¡ç«¯æ”¶åˆ°å®¢æˆ·ç«¯çš„
SYN报文åŽ,首先æœåŠ¡ç«¯ä¹Ÿéšæœºåˆå§‹åŒ–自己的åºå·(server_isn),å°†æ¤åºå·å¡«å…¥ TCP 首部的「åºå·ã€å—段ä¸,其次把 TCP 首部的「确认应ç”å·ã€å—段填入client_isn + 1, æŽ¥ç€æŠŠSYNå’ŒACKæ ‡å¿—ä½ç½®ä¸º1ã€‚æœ€åŽæŠŠè¯¥æŠ¥æ–‡å‘给客户端,该报文也ä¸åŒ…å«åº”用层数æ®,ä¹‹åŽæœåŠ¡ç«¯å¤„äºŽSYN-RCVD状æ€ã€‚
-
客户端收到æœåŠ¡ç«¯æŠ¥æ–‡åŽ,还è¦å‘æœåŠ¡ç«¯å›žåº”æœ€åŽä¸€ä¸ªåº”ç”æŠ¥æ–‡,é¦–å…ˆè¯¥åº”ç”æŠ¥æ–‡ TCP 首部
ACKæ ‡å¿—ä½ç½®ä¸º1,其次「确认应ç”å·ã€å—段填入server_isn + 1,æœ€åŽæŠŠæŠ¥æ–‡å‘é€ç»™æœåŠ¡ç«¯,这次报文å¯ä»¥æºå¸¦å®¢æˆ·åˆ°æœåŠ¡å™¨çš„æ•°æ®,之åŽå®¢æˆ·ç«¯å¤„于ESTABLISHED状æ€ã€‚ -
æœåŠ¡å™¨æ”¶åˆ°å®¢æˆ·ç«¯çš„åº”ç”æŠ¥æ–‡åŽ,也进入
ESTABLISHED状æ€ã€‚
从上é¢çš„过程å¯ä»¥å‘çŽ°ç¬¬ä¸‰æ¬¡æ¡æ‰‹æ˜¯å¯ä»¥æºå¸¦æ•°æ®çš„,å‰ä¸¤æ¬¡æ¡æ‰‹æ˜¯ä¸å¯ä»¥æºå¸¦æ•°æ®çš„,这也是é¢è¯•常问的题。
一旦完æˆä¸‰æ¬¡æ¡æ‰‹,åŒæ–¹éƒ½å¤„于 ESTABLISHED 状æ€,æ¤æ—¶è¿žæŽ¥å°±å·²å»ºç«‹å®Œæˆ,客户端和æœåŠ¡ç«¯å°±å¯ä»¥ç›¸äº’å‘逿•°æ®äº†ã€‚
如何在 Linux ç³»ç»Ÿä¸æŸ¥çœ‹ TCP 状æ€?
TCP çš„è¿žæŽ¥çŠ¶æ€æŸ¥çœ‹,在 Linux å¯ä»¥é€šè¿‡ netstat -napt 命令查看。
ä¸ºä»€ä¹ˆæ˜¯ä¸‰æ¬¡æ¡æ‰‹?䏿˜¯ä¸¤æ¬¡ã€å››æ¬¡?
相信大家比较常回ç”的是:â€œå› ä¸ºä¸‰æ¬¡æ¡æ‰‹æ‰èƒ½ä¿è¯åŒæ–¹å…·æœ‰æŽ¥æ”¶å’Œå‘é€çš„能力。â€
è¿™å›žç”æ˜¯æ²¡é—®é¢˜,ä½†è¿™å›žç”æ˜¯ç‰‡é¢çš„,并没有说出主è¦çš„åŽŸå› ã€‚
在å‰é¢æˆ‘们知é“了什么是 TCP 连接:
- 用于ä¿è¯å¯é 性和æµé‡æŽ§åˆ¶ç»´æŠ¤çš„æŸäº›çŠ¶æ€ä¿¡æ¯,这些信æ¯çš„组åˆ,包括Socketã€åºåˆ—å·å’Œçª—å£å¤§å°ç§°ä¸ºè¿žæŽ¥ã€‚
所以,é‡è¦çš„æ˜¯ä¸ºä»€ä¹ˆä¸‰æ¬¡æ¡æ‰‹æ‰å¯ä»¥åˆå§‹åŒ–Socketã€åºåˆ—å·å’Œçª—å£å¤§å°å¹¶å»ºç«‹ TCP 连接。
接下æ¥ä»¥ä¸‰ä¸ªæ–¹é¢åˆ†æžä¸‰æ¬¡æ¡æ‰‹çš„åŽŸå› :
- ä¸‰æ¬¡æ¡æ‰‹æ‰å¯ä»¥é˜»æ¢é‡å¤åކå²è¿žæŽ¥çš„åˆå§‹åŒ–(主è¦åŽŸå› )
- ä¸‰æ¬¡æ¡æ‰‹æ‰å¯ä»¥åŒæ¥åŒæ–¹çš„åˆå§‹åºåˆ—å·
- ä¸‰æ¬¡æ¡æ‰‹æ‰å¯ä»¥é¿å…èµ„æºæµªè´¹
åŽŸå› ä¸€:é¿å…历å²è¿žæŽ¥
我们æ¥çœ‹çœ‹ RFC 793 指出的 TCP è¿žæŽ¥ä½¿ç”¨ä¸‰æ¬¡æ¡æ‰‹çš„首è¦åŽŸå› :
The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.
ç®€å•æ¥è¯´,ä¸‰æ¬¡æ¡æ‰‹çš„首è¦åŽŸå› æ˜¯ä¸ºäº†é˜²æ¢æ—§çš„é‡å¤è¿žæŽ¥åˆå§‹åŒ–é€ æˆæ··ä¹±ã€‚
ç½‘ç»œçŽ¯å¢ƒæ˜¯é”™ç»¼å¤æ‚çš„,往往并䏿˜¯å¦‚æˆ‘ä»¬æœŸæœ›çš„ä¸€æ ·,å…ˆå‘é€çš„æ•°æ®åŒ…,å°±å…ˆåˆ°è¾¾ç›®æ ‡ä¸»æœº,å而它很骚,å¯èƒ½ä¼šç”±äºŽç½‘ç»œæ‹¥å µç‰ä¹±ä¸ƒå…«ç³Ÿçš„åŽŸå› ,会使得旧的数æ®åŒ…,å…ˆåˆ°è¾¾ç›®æ ‡ä¸»æœº,é‚£ä¹ˆè¿™ç§æƒ…况下 TCP ä¸‰æ¬¡æ¡æ‰‹æ˜¯å¦‚何é¿å…的呢?
客户端连ç»å‘é€å¤šæ¬¡ SYN 建立连接的报文,åœ¨ç½‘ç»œæ‹¥å µæƒ…å†µä¸‹:
- 一个「旧 SYN æŠ¥æ–‡ã€æ¯”「最新的 SYN 〠报文早到达了æœåŠ¡ç«¯;
- é‚£ä¹ˆæ¤æ—¶æœåŠ¡ç«¯å°±ä¼šå›žä¸€ä¸ª
SYN + ACK报文给客户端; - 客户端收到åŽå¯ä»¥æ ¹æ®è‡ªèº«çš„上下文,判æ–这是一个历å²è¿žæŽ¥(åºåˆ—å·è¿‡æœŸæˆ–è¶…æ—¶),那么客户端就会å‘é€
RST报文给æœåŠ¡ç«¯,è¡¨ç¤ºä¸æ¢è¿™ä¸€æ¬¡è¿žæŽ¥ã€‚
å¦‚æžœæ˜¯ä¸¤æ¬¡æ¡æ‰‹è¿žæŽ¥,å°±ä¸èƒ½åˆ¤æ–当å‰è¿žæŽ¥æ˜¯å¦æ˜¯åކå²è¿žæŽ¥,ä¸‰æ¬¡æ¡æ‰‹åˆ™å¯ä»¥åœ¨å®¢æˆ·ç«¯(å‘逿–¹)准备å‘é€ç¬¬ä¸‰æ¬¡æŠ¥æ–‡æ—¶,å®¢æˆ·ç«¯å› æœ‰è¶³å¤Ÿçš„ä¸Šä¸‹æ–‡æ¥åˆ¤æ–当å‰è¿žæŽ¥æ˜¯å¦æ˜¯åކå²è¿žæŽ¥:
- 如果是历å²è¿žæŽ¥(åºåˆ—å·è¿‡æœŸæˆ–è¶…æ—¶),åˆ™ç¬¬ä¸‰æ¬¡æ¡æ‰‹å‘é€çš„æŠ¥æ–‡æ˜¯
RST报文,以æ¤ä¸æ¢åކå²è¿žæŽ¥; - å¦‚æžœä¸æ˜¯åކå²è¿žæŽ¥,则第三次å‘é€çš„æŠ¥æ–‡æ˜¯
ACK报文,é€šä¿¡åŒæ–¹å°±ä¼šæˆåŠŸå»ºç«‹è¿žæŽ¥;
所以,TCP ä½¿ç”¨ä¸‰æ¬¡æ¡æ‰‹å»ºç«‹è¿žæŽ¥çš„æœ€ä¸»è¦åŽŸå› æ˜¯é˜²æ¢åކå²è¿žæŽ¥åˆå§‹åŒ–了连接。
åŽŸå› äºŒ:åŒæ¥åŒæ–¹åˆå§‹åºåˆ—å·
TCP åè®®çš„é€šä¿¡åŒæ–¹, 都必须维护一个「åºåˆ—å·ã€, åºåˆ—å·æ˜¯å¯é ä¼ è¾“çš„ä¸€ä¸ªå…³é”®å› ç´ ,它的作用:
- 接收方å¯ä»¥å޻除é‡å¤çš„æ•°æ®;
- 接收方å¯ä»¥æ ¹æ®æ•°æ®åŒ…çš„åºåˆ—å·æŒ‰åºæŽ¥æ”¶;
- å¯ä»¥æ ‡è¯†å‘é€å‡ºåŽ»çš„æ•°æ®åŒ…ä¸, 哪些是已ç»è¢«å¯¹æ–¹æ”¶åˆ°çš„;
å¯è§,åºåˆ—å·åœ¨ TCP 连接ä¸å æ®ç€éžå¸¸é‡è¦çš„作用,所以当客户端å‘逿ºå¸¦ã€Œåˆå§‹åºåˆ—å·ã€çš„ SYN 报文的时候,éœ€è¦æœåŠ¡ç«¯å›žä¸€ä¸ª ACK åº”ç”æŠ¥æ–‡,表示客户端的 SYN 报文已被æœåŠ¡ç«¯æˆåŠŸæŽ¥æ”¶,那当æœåŠ¡ç«¯å‘é€ã€Œåˆå§‹åºåˆ—å·ã€ç»™å®¢æˆ·ç«¯çš„æ—¶å€™,ä¾ç„¶ä¹Ÿè¦å¾—到客户端的应ç”回应,è¿™æ ·ä¸€æ¥ä¸€å›ž,æ‰èƒ½ç¡®ä¿åŒæ–¹çš„åˆå§‹åºåˆ—å·èƒ½è¢«å¯é çš„åŒæ¥ã€‚
å››æ¬¡æ¡æ‰‹å…¶å®žä¹Ÿèƒ½å¤Ÿå¯é çš„åŒæ¥åŒæ–¹çš„åˆå§‹åŒ–åºå·,但由于第二æ¥å’Œç¬¬ä¸‰æ¥å¯ä»¥ä¼˜åŒ–æˆä¸€æ¥,所以就æˆäº†ã€Œä¸‰æ¬¡æ¡æ‰‹ã€ã€‚
è€Œä¸¤æ¬¡æ¡æ‰‹åªä¿è¯äº†ä¸€æ–¹çš„åˆå§‹åºåˆ—å·èƒ½è¢«å¯¹æ–¹æˆåŠŸæŽ¥æ”¶,没办法ä¿è¯åŒæ–¹çš„åˆå§‹åºåˆ—å·éƒ½èƒ½è¢«ç¡®è®¤æŽ¥æ”¶ã€‚
åŽŸå› ä¸‰:é¿å…èµ„æºæµªè´¹
å¦‚æžœåªæœ‰ã€Œä¸¤æ¬¡æ¡æ‰‹ã€,当客户端的 SYN 请求连接在网络ä¸é˜»å¡ž,客户端没有接收到 ACK 报文,å°±ä¼šé‡æ–°å‘é€ SYN ,ç”±äºŽæ²¡æœ‰ç¬¬ä¸‰æ¬¡æ¡æ‰‹,æœåС噍䏿¸…æ¥šå®¢æˆ·ç«¯æ˜¯å¦æ”¶åˆ°äº†è‡ªå·±å‘é€çš„建立连接的 ACK 确认信å·,æ‰€ä»¥æ¯æ”¶åˆ°ä¸€ä¸ª SYN å°±åªèƒ½å…ˆä¸»åŠ¨å»ºç«‹ä¸€ä¸ªè¿žæŽ¥,è¿™ä¼šé€ æˆä»€ä¹ˆæƒ…况呢?
如果客户端的 SYN 阻塞了,é‡å¤å‘é€å¤šæ¬¡ SYN 报文,那么æœåŠ¡å™¨åœ¨æ”¶åˆ°è¯·æ±‚åŽå°±ä¼šå»ºç«‹å¤šä¸ªå†—ä½™çš„æ— æ•ˆé“¾æŽ¥,é€ æˆä¸å¿…è¦çš„èµ„æºæµªè´¹ã€‚
å³ä¸¤æ¬¡æ¡æ‰‹ä¼šé€ æˆæ¶ˆæ¯æ»žç•™æƒ…况下,æœåС噍é‡å¤æŽ¥å—æ— ç”¨çš„è¿žæŽ¥è¯·æ±‚ SYN 报文,è€Œé€ æˆé‡å¤åˆ†é…资æºã€‚
å°ç»“
TCP 建立连接时,é€šè¿‡ä¸‰æ¬¡æ¡æ‰‹èƒ½é˜²æ¢åކå²è¿žæŽ¥çš„建立,能å‡å°‘åŒæ–¹ä¸å¿…è¦çš„资æºå¼€é”€,èƒ½å¸®åŠ©åŒæ–¹åŒæ¥åˆå§‹åŒ–åºåˆ—å·ã€‚åºåˆ—å·èƒ½å¤Ÿä¿è¯æ•°æ®åŒ…ä¸é‡å¤ã€ä¸ä¸¢å¼ƒå’ŒæŒ‰åºä¼ 输。
ä¸ä½¿ç”¨ã€Œä¸¤æ¬¡æ¡æ‰‹ã€å’Œã€Œå››æ¬¡æ¡æ‰‹ã€çš„åŽŸå› :
- ã€Œä¸¤æ¬¡æ¡æ‰‹ã€:æ— æ³•é˜²æ¢åކå²è¿žæŽ¥çš„建立,ä¼šé€ æˆåŒæ–¹èµ„æºçš„æµªè´¹,ä¹Ÿæ— æ³•å¯é çš„åŒæ¥åŒæ–¹åºåˆ—å·;
- ã€Œå››æ¬¡æ¡æ‰‹ã€:ä¸‰æ¬¡æ¡æ‰‹å°±å·²ç»ç†è®ºä¸Šæœ€å°‘å¯é 连接建立,所以ä¸éœ€è¦ä½¿ç”¨æ›´å¤šçš„通信次数。
为什么客户端和æœåŠ¡ç«¯çš„åˆå§‹åºåˆ—å· ISN 是ä¸ç›¸åŒçš„?
如果一个已ç»å¤±æ•ˆçš„连接被é‡ç”¨äº†,ä½†æ˜¯è¯¥æ—§è¿žæŽ¥çš„åŽ†å²æŠ¥æ–‡è¿˜æ®‹ç•™åœ¨ç½‘ç»œä¸,如果åºåˆ—å·ç›¸åŒ,é‚£ä¹ˆå°±æ— æ³•åˆ†è¾¨å‡ºè¯¥æŠ¥æ–‡æ˜¯ä¸æ˜¯åŽ†å²æŠ¥æ–‡,å¦‚æžœåŽ†å²æŠ¥æ–‡è¢«æ–°çš„è¿žæŽ¥æŽ¥æ”¶äº†,则会产生数æ®é”™ä¹±ã€‚
所以,æ¯æ¬¡å»ºç«‹è¿žæŽ¥å‰é‡æ–°åˆå§‹åŒ–一个åºåˆ—å·ä¸»è¦æ˜¯ä¸ºäº†é€šä¿¡åŒæ–¹èƒ½å¤Ÿæ ¹æ®åºå·å°†ä¸å±žäºŽæœ¬è¿žæŽ¥çš„æŠ¥æ–‡æ®µä¸¢å¼ƒã€‚
å¦ä¸€æ–¹é¢æ˜¯ä¸ºäº†å®‰å…¨æ€§,防æ¢é»‘å®¢ä¼ªé€ çš„ç›¸åŒåºåˆ—å·çš„ TCP 报文被对方接收。
åˆå§‹åºåˆ—å· ISN æ˜¯å¦‚ä½•éšæœºäº§ç”Ÿçš„?
èµ·å§‹ ISN 是基于时钟的,æ¯ 4 毫秒 + 1,è½¬ä¸€åœˆè¦ 4.55 ä¸ªå°æ—¶ã€‚
RFC1948 ä¸æå‡ºäº†ä¸€ä¸ªè¾ƒå¥½çš„åˆå§‹åŒ–åºåˆ—å· ISN éšæœºç”Ÿæˆç®—法。
ISN = M + F (localhost, localport, remotehost, remoteport)
M是一个计时器,这个计时器æ¯éš” 4 æ¯«ç§’åŠ 1。F是一个 Hash 算法,æ ¹æ®æº IPã€ç›®çš„ IPã€æºç«¯å£ã€ç›®çš„端å£ç”Ÿæˆä¸€ä¸ªéšæœºæ•°å€¼ã€‚è¦ä¿è¯ Hash 算法ä¸èƒ½è¢«å¤–部轻易推算得出,用 MD5 算法是一个比较好的选择。
既然 IP 层会分片,为什么 TCP å±‚è¿˜éœ€è¦ MSS å‘¢?
我们先æ¥è®¤è¯†ä¸‹ MTU å’Œ MSS
MTU:一个网络包的最大长度,以太网ä¸ä¸€èˆ¬ä¸º1500å—节;MSS:除去 IP å’Œ TCP 头部之åŽ,一个网络包所能容纳的 TCP æ•°æ®çš„æœ€å¤§é•¿åº¦;
如果在 TCP 的整个报文(头部 + æ•°æ®)交给 IP 层进行分片,会有什么异常呢?
当 IP 层有一个超过 MTU 大å°çš„æ•°æ®(TCP 头部 + TCP æ•°æ®)è¦å‘é€,那么 IP 层就è¦è¿›è¡Œåˆ†ç‰‡,把数æ®åˆ†ç‰‡æˆè‹¥å¹²ç‰‡,ä¿è¯æ¯ä¸€ä¸ªåˆ†ç‰‡éƒ½å°äºŽ MTU。把一份 IP æ•°æ®æŠ¥è¿›è¡Œåˆ†ç‰‡ä»¥åŽ,ç”±ç›®æ ‡ä¸»æœºçš„ IP 层æ¥è¿›è¡Œé‡æ–°ç»„装åŽ,å†äº¤ç»™ä¸Šä¸€å±‚ TCP ä¼ è¾“å±‚ã€‚
这看起æ¥äº•然有åº,但这å˜åœ¨éšæ‚£çš„,那么当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得é‡ä¼ 。
å› ä¸º IP 层本身没有超时é‡ä¼ 机制,å®ƒç”±ä¼ è¾“å±‚çš„ TCP æ¥è´Ÿè´£è¶…æ—¶å’Œé‡ä¼ 。
当接收方å‘现 TCP 报文(头部 + æ•°æ®)çš„æŸä¸€ç‰‡ä¸¢å¤±åŽ,则ä¸ä¼šå“应 ACK 给对方,那么å‘逿–¹çš„ TCP 在超时åŽ,就会é‡å‘「整个 TCP 报文(头部 + æ•°æ®)ã€ã€‚
å› æ¤,å¯ä»¥å¾—知由 IP å±‚è¿›è¡Œåˆ†ç‰‡ä¼ è¾“,是éžå¸¸æ²¡æœ‰æ•ˆçŽ‡çš„ã€‚
所以,ä¸ºäº†è¾¾åˆ°æœ€ä½³çš„ä¼ è¾“æ•ˆèƒ½ TCP å议在建立连接的时候通常è¦åå•†åŒæ–¹çš„ MSS 值,当 TCP 层å‘现数æ®è¶…过 MSS æ—¶,则就先会进行分片,当然由它形æˆçš„ IP 包的长度也就ä¸ä¼šå¤§äºŽ MTU ,自然也就ä¸ç”¨ IP 分片了。
ç»è¿‡ TCP 层分片åŽ,如果一个 TCP 分片丢失åŽ,进行é‡å‘时也是以 MSS 为å•ä½,而ä¸ç”¨é‡ä¼ 所有的分片,å¤§å¤§å¢žåŠ äº†é‡ä¼ 的效率。
什么是 SYN 攻击?如何é¿å… SYN 攻击?
SYN 攻击
æˆ‘ä»¬éƒ½çŸ¥é“ TCP 连接建立是需è¦ä¸‰æ¬¡æ¡æ‰‹,å‡è®¾æ”»å‡»è€…çŸæ—¶é—´ä¼ªé€ ä¸åŒ IP 地å€çš„ SYN 报文,æœåŠ¡ç«¯æ¯æŽ¥æ”¶åˆ°ä¸€ä¸ª SYN 报文,就进入SYN_RCVD 状æ€,但æœåŠ¡ç«¯å‘é€å‡ºåŽ»çš„ ACK + SYN 报文,æ— æ³•å¾—åˆ°æœªçŸ¥ IP 主机的 ACK 应ç”,ä¹…è€Œä¹…ä¹‹å°±ä¼šå æ»¡æœåŠ¡ç«¯çš„ SYN 接收队列(未连接队列),使得æœåС噍ä¸èƒ½ä¸ºæ£å¸¸ç”¨æˆ·æœåŠ¡ã€‚
é¿å… SYN 攻击方å¼ä¸€
å…¶ä¸ä¸€ç§è§£å†³æ–¹å¼æ˜¯é€šè¿‡ä¿®æ”¹ Linux å†…æ ¸å‚æ•°,控制队列大å°å’Œå½“队列满时应åšä»€ä¹ˆå¤„ç†ã€‚
- å½“ç½‘å¡æŽ¥æ”¶æ•°æ®åŒ…çš„é€Ÿåº¦å¤§äºŽå†…æ ¸å¤„ç†çš„速度时,会有一个队列ä¿å˜è¿™äº›æ•°æ®åŒ…ã€‚æŽ§åˆ¶è¯¥é˜Ÿåˆ—çš„æœ€å¤§å€¼å¦‚ä¸‹å‚æ•°:
net.coredev_max_backlog
- SYN_RCVD 状æ€è¿žæŽ¥çš„æœ€å¤§ä¸ªæ•°:
net.ipv4.tcp_max_syn_backlog
- 超出处ç†èƒ½æ—¶,对新的 SYN 直接回报 RST,丢弃连接:
net.ipv4.tcp_abort_on_overflow
é¿å… SYN 攻击方å¼äºŒ
我们先æ¥çœ‹ä¸‹ Linux å†…æ ¸çš„ SYN (未完æˆè¿žæŽ¥å»ºç«‹)队列与 Accpet (已完æˆè¿žæŽ¥å»ºç«‹)队列是如何工作的?
æ£å¸¸æµç¨‹:
- 当æœåŠ¡ç«¯æŽ¥æ”¶åˆ°å®¢æˆ·ç«¯çš„ SYN 报文时,ä¼šå°†å…¶åŠ å…¥åˆ°å†…æ ¸çš„ã€Œ SYN 队列ã€;
- 接ç€å‘é€ SYN + ACK 给客户端,ç‰å¾…客户端回应 ACK 报文;
- æœåŠ¡ç«¯æŽ¥æ”¶åˆ° ACK 报文åŽ,从「 SYN 队列ã€ç§»é™¤æ”¾å…¥åˆ°ã€Œ Accept 队列ã€;
- 应用通过调用
accpet()socket 接å£,从「 Accept 队列ã€å–出连接。
应用程åºè¿‡æ…¢:
- 如果应用程åºè¿‡æ…¢æ—¶,就会导致「 Accept 队列ã€è¢«å 满。
å—到 SYN 攻击:
- å¦‚æžœä¸æ–å—到 SYN 攻击,就会导致「 SYN 队列ã€è¢«å 满。
tcp_syncookies 的方å¼å¯ä»¥åº”对 SYN 攻击的方法:
net.ipv4.tcp_syncookies = 1
- 当 「 SYN é˜Ÿåˆ—ã€æ»¡ä¹‹åŽ,åŽç»æœåŠ¡å™¨æ”¶åˆ° SYN 包,ä¸è¿›å…¥ã€Œ SYN 队列ã€;
- 计算出一个
cookie值,å†ä»¥ SYN + ACK ä¸çš„「åºåˆ—å·ã€è¿”回客户端, - æœåŠ¡ç«¯æŽ¥æ”¶åˆ°å®¢æˆ·ç«¯çš„åº”ç”æŠ¥æ–‡æ—¶,æœåŠ¡å™¨ä¼šæ£€æŸ¥è¿™ä¸ª ACK åŒ…çš„åˆæ³•æ€§ã€‚å¦‚æžœåˆæ³•,直接放入到「 Accept 队列ã€ã€‚
- 最åŽåº”用通过调用
accpet()socket 接å£,从「 Accept 队列ã€å–出的连接。
03 TCP 连接æ–å¼€
TCP 四次挥手过程和状æ€å˜è¿
å¤©ä¸‹æ²¡æœ‰ä¸æ•£çš„å®´å¸,对于 TCP è¿žæŽ¥ä¹Ÿæ˜¯è¿™æ ·, TCP æ–开连接是通过四次挥手方å¼ã€‚
åŒæ–¹éƒ½å¯ä»¥ä¸»åЍæ–开连接,æ–开连接åŽä¸»æœºä¸çš„「资æºã€å°†è¢«é‡Šæ”¾ã€‚
- 客户端打算关é—连接,æ¤æ—¶ä¼šå‘é€ä¸€ä¸ª TCP 首部
FINæ ‡å¿—ä½è¢«ç½®ä¸º1的报文,也å³FIN报文,之åŽå®¢æˆ·ç«¯è¿›å…¥FIN_WAIT_1状æ€ã€‚ - æœåŠ¡ç«¯æ”¶åˆ°è¯¥æŠ¥æ–‡åŽ,å°±å‘客户端å‘é€
ACKåº”ç”æŠ¥æ–‡,æŽ¥ç€æœåŠ¡ç«¯è¿›å…¥CLOSED_WAIT状æ€ã€‚ - 客户端收到æœåŠ¡ç«¯çš„
ACKåº”ç”æŠ¥æ–‡åŽ,之åŽè¿›å…¥FIN_WAIT_2状æ€ã€‚ - ç‰å¾…æœåŠ¡ç«¯å¤„ç†å®Œæ•°æ®åŽ,也å‘客户端å‘é€
FIN报文,ä¹‹åŽæœåŠ¡ç«¯è¿›å…¥LAST_ACK状æ€ã€‚ - 客户端收到æœåŠ¡ç«¯çš„
FIN报文åŽ,回一个ACKåº”ç”æŠ¥æ–‡,之åŽè¿›å…¥TIME_WAITçŠ¶æ€ - æœåŠ¡å™¨æ”¶åˆ°äº†
ACKåº”ç”æŠ¥æ–‡åŽ,就进入了CLOSED状æ€,è‡³æ¤æœåŠ¡ç«¯å·²ç»å®Œæˆè¿žæŽ¥çš„å…³é—。 - 客户端在ç»è¿‡
2MSL一段时间åŽ,自动进入CLOSED状æ€,至æ¤å®¢æˆ·ç«¯ä¹Ÿå®Œæˆè¿žæŽ¥çš„å…³é—。
ä½ å¯ä»¥çœ‹åˆ°,æ¯ä¸ªæ–¹å‘都需è¦ä¸€ä¸ª FIN 和一个 ACK,å› æ¤é€šå¸¸è¢«ç§°ä¸ºå››æ¬¡æŒ¥æ‰‹ã€‚
è¿™é‡Œä¸€ç‚¹éœ€è¦æ³¨æ„是:主动关é—连接的,æ‰æœ‰ TIME_WAIT 状æ€ã€‚
为什么挥手需è¦å››æ¬¡?
冿¥å›žé¡¾ä¸‹å››æ¬¡æŒ¥æ‰‹åŒæ–¹å‘ FIN 包的过程,就能ç†è§£ä¸ºä»€ä¹ˆéœ€è¦å››æ¬¡äº†ã€‚
- å…³é—连接时,å®¢æˆ·ç«¯å‘æœåŠ¡ç«¯å‘é€
FINæ—¶,仅仅表示客户端ä¸å†å‘逿•°æ®äº†ä½†æ˜¯è¿˜èƒ½æŽ¥æ”¶æ•°æ®ã€‚ - æœåŠ¡å™¨æ”¶åˆ°å®¢æˆ·ç«¯çš„
FIN报文时,先回一个ACKåº”ç”æŠ¥æ–‡,而æœåŠ¡ç«¯å¯èƒ½è¿˜æœ‰æ•°æ®éœ€è¦å¤„ç†å’Œå‘é€,ç‰æœåŠ¡ç«¯ä¸å†å‘逿•°æ®æ—¶,æ‰å‘é€FIN报文给客户端æ¥è¡¨ç¤ºåŒæ„现在关é—连接。
从上é¢è¿‡ç¨‹å¯çŸ¥,æœåŠ¡ç«¯é€šå¸¸éœ€è¦ç‰å¾…å®Œæˆæ•°æ®çš„å‘é€å’Œå¤„ç†,所以æœåŠ¡ç«¯çš„ ACK å’Œ FIN 一般都会分开å‘é€,ä»Žè€Œæ¯”ä¸‰æ¬¡æ¡æ‰‹å¯¼è‡´å¤šäº†ä¸€æ¬¡ã€‚
为什么 TIME_WAIT ç‰å¾…的时间是 2MSL?
MSL 是 Maximum Segment Lifetime,æŠ¥æ–‡æœ€å¤§ç”Ÿå˜æ—¶é—´,它是任何报文在网络上å˜åœ¨çš„æœ€é•¿æ—¶é—´,è¶…è¿‡è¿™ä¸ªæ—¶é—´æŠ¥æ–‡å°†è¢«ä¸¢å¼ƒã€‚å› ä¸º TCP 报文基于是 IP å议的,而 IP 头䏿œ‰ä¸€ä¸ª TTL å—æ®µ,是 IP æ•°æ®æŠ¥å¯ä»¥ç»è¿‡çš„æœ€å¤§è·¯ç”±æ•°,æ¯ç»è¿‡ä¸€ä¸ªå¤„ç†ä»–的路由器æ¤å€¼å°±å‡ 1,当æ¤å€¼ä¸º 0 åˆ™æ•°æ®æŠ¥å°†è¢«ä¸¢å¼ƒ,åŒæ—¶å‘é€ ICMP 报文通知æºä¸»æœºã€‚
MSL 与 TTL 的区别: MSL çš„å•使˜¯æ—¶é—´,而 TTL 是ç»è¿‡è·¯ç”±è·³æ•°ã€‚所以 MSL 应该è¦å¤§äºŽç‰äºŽ TTL 消耗为 0 的时间,ä»¥ç¡®ä¿æŠ¥æ–‡å·²è¢«è‡ªç„¶æ¶ˆäº¡ã€‚
TIME_WAIT ç‰å¾… 2 å€çš„ MSL,比较åˆç†çš„解释是: 网络ä¸å¯èƒ½å˜åœ¨æ¥è‡ªå‘逿–¹çš„æ•°æ®åŒ…,当这些å‘逿–¹çš„æ•°æ®åŒ…被接收方处ç†åŽåˆä¼šå‘对方å‘é€å“应,所以一æ¥ä¸€å›žéœ€è¦ç‰å¾… 2 å€çš„æ—¶é—´ã€‚
æ¯”å¦‚å¦‚æžœè¢«åŠ¨å…³é—æ–¹æ²¡æœ‰æ”¶åˆ°æ–开连接的最åŽçš„ ACK 报文,就会触å‘è¶…æ—¶é‡å‘ Fin 报文,å¦ä¸€æ–¹æŽ¥æ”¶åˆ° FIN åŽ,会é‡å‘ ACK ç»™è¢«åŠ¨å…³é—æ–¹, 一æ¥ä¸€åŽ»æ£å¥½ 2 个 MSL。
2MSL 的时间是从客户端接收到 FIN åŽå‘é€ ACK 开始计时的。如果在 TIME-WAIT 时间内,å› ä¸ºå®¢æˆ·ç«¯çš„ ACK æ²¡æœ‰ä¼ è¾“åˆ°æœåŠ¡ç«¯,å®¢æˆ·ç«¯åˆæŽ¥æ”¶åˆ°äº†æœåŠ¡ç«¯é‡å‘çš„ FIN 报文,那么 2MSL æ—¶é—´å°†é‡æ–°è®¡æ—¶ã€‚
在 Linux 系统里 2MSL 默认是 60 ç§’,那么一个 MSL 也就是 30 秒。Linux 系统åœç•™åœ¨ TIME_WAIT 的时间为固定的 60 秒。
其定义在 Linux å†…æ ¸ä»£ç 里的å称为 TCP_TIMEWAIT_LEN:
#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
state, about 60 seconds */
如果è¦ä¿®æ”¹ TIME_WAIT 的时间长度,åªèƒ½ä¿®æ”¹ Linux å†…æ ¸ä»£ç 里 TCP_TIMEWAIT_LEN 的值,并釿–°ç¼–译 Linux å†…æ ¸ã€‚
ä¸ºä»€ä¹ˆéœ€è¦ TIME_WAIT 状æ€?
主动å‘èµ·å…³é—连接的一方,æ‰ä¼šæœ‰ TIME-WAIT 状æ€ã€‚
éœ€è¦ TIME-WAIT 状æ€,ä¸»è¦æ˜¯ä¸¤ä¸ªåŽŸå› :
- 防æ¢å…·æœ‰ç›¸åŒã€Œå››å…ƒç»„ã€çš„ã€Œæ—§ã€æ•°æ®åŒ…被收到;
- ä¿è¯ã€Œè¢«åЍ关é—连接ã€çš„一方能被æ£ç¡®çš„å…³é—,å³ä¿è¯æœ€åŽçš„ ACK èƒ½è®©è¢«åŠ¨å…³é—æ–¹æŽ¥æ”¶,从而帮助其æ£å¸¸å…³é—;
åŽŸå› ä¸€:é˜²æ¢æ—§è¿žæŽ¥çš„æ•°æ®åŒ…
å‡è®¾ TIME-WAIT 没有ç‰å¾…时间或时间过çŸ,被延迟的数æ®åŒ…抵达åŽä¼šå‘生什么呢?
- 如上图黄色框框æœåŠ¡ç«¯åœ¨å…³é—连接之å‰å‘é€çš„
SEQ = 301报文,被网络延迟了。 - 这时有相åŒç«¯å£çš„ TCP 连接被å¤ç”¨åŽ,被延迟的
SEQ = 301抵达了客户端,那么客户端是有å¯èƒ½æ£å¸¸æŽ¥æ”¶è¿™ä¸ªè¿‡æœŸçš„æŠ¥æ–‡,这就会产生数æ®é”™ä¹±ç‰ä¸¥é‡çš„问题。
所以,TCP 就设计出了这么一个机制,ç»è¿‡ 2MSL 这个时间,足以让两个方å‘上的数æ®åŒ…都被丢弃,使得原æ¥è¿žæŽ¥çš„æ•°æ®åŒ…在网络ä¸éƒ½è‡ªç„¶æ¶ˆå¤±,å†å‡ºçŽ°çš„æ•°æ®åŒ…一定都是新建立连接所产生的。
åŽŸå› äºŒ:ä¿è¯è¿žæŽ¥æ£ç¡®å…³é—
在 RFC 793 指出 TIME-WAIT å¦ä¸€ä¸ªé‡è¦çš„作用是:
TIME-WAIT - represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request.
也就是说,TIME-WAIT 作用是ç‰å¾…è¶³å¤Ÿçš„æ—¶é—´ä»¥ç¡®ä¿æœ€åŽçš„ ACK èƒ½è®©è¢«åŠ¨å…³é—æ–¹æŽ¥æ”¶,从而帮助其æ£å¸¸å…³é—。
å‡è®¾ TIME-WAIT 没有ç‰å¾…时间或时间过çŸ,æ–å¼€è¿žæŽ¥ä¼šé€ æˆä»€ä¹ˆé—®é¢˜å‘¢?
- 如上图红色框框客户端四次挥手的最åŽä¸€ä¸ª
ACK报文如果在网络ä¸è¢«ä¸¢å¤±äº†,æ¤æ—¶å¦‚果客户端TIME-WAITè¿‡çŸæˆ–没有,则就直接进入了CLOSED状æ€äº†,那么æœåŠ¡ç«¯åˆ™ä¼šä¸€ç›´å¤„åœ¨LASE_ACK状æ€ã€‚ - 当客户端å‘起建立连接的
SYN请求报文åŽ,æœåŠ¡ç«¯ä¼šå‘é€RST报文给客户端,连接建立的过程就会被终æ¢ã€‚
如果 TIME-WAIT ç‰å¾…足够长的情况就会é‡åˆ°ä¸¤ç§æƒ…况:
- æœåŠ¡ç«¯æ£å¸¸æ”¶åˆ°å››æ¬¡æŒ¥æ‰‹çš„æœ€åŽä¸€ä¸ª
ACK报文,则æœåŠ¡ç«¯æ£å¸¸å…³é—连接。 - æœåŠ¡ç«¯æ²¡æœ‰æ”¶åˆ°å››æ¬¡æŒ¥æ‰‹çš„æœ€åŽä¸€ä¸ª
ACK报文时,则会é‡å‘FINå…³é—连接报文并ç‰å¾…æ–°çš„ACK报文。
所以客户端在 TIME-WAIT 状æ€ç‰å¾… 2MSL æ—¶é—´åŽ,å°±å¯ä»¥ä¿è¯åŒæ–¹çš„连接都å¯ä»¥æ£å¸¸çš„å…³é—。
TIME_WAIT 过多有什么å±å®³?
如果æœåŠ¡å™¨æœ‰å¤„äºŽ TIME-WAIT 状æ€çš„ TCP,则说明是由æœåŠ¡å™¨æ–¹ä¸»åŠ¨å‘èµ·çš„æ–开请求。
过多的 TIME-WAIT 状æ€ä¸»è¦çš„å±å®³æœ‰ä¸¤ç§:
- 第一是内å˜èµ„æºå 用;
- 第二是对端å£èµ„æºçš„å 用,一个 TCP 连接至少消耗一个本地端å£;
第二个å±å®³æ˜¯ä¼šé€ æˆä¸¥é‡çš„åŽæžœçš„,è¦çŸ¥é“,端å£èµ„æºä¹Ÿæ˜¯æœ‰é™çš„,一般å¯ä»¥å¼€å¯çš„端å£ä¸º 32768~61000,也å¯ä»¥é€šè¿‡å¦‚䏋傿•°è®¾ç½®æŒ‡å®š
net.ipv4.ip_local_port_range
如果å‘起连接一方的 TIME_WAIT 状æ€è¿‡å¤š,å æ»¡äº†æ‰€æœ‰ç«¯å£èµ„æº,åˆ™ä¼šå¯¼è‡´æ— æ³•åˆ›å»ºæ–°è¿žæŽ¥ã€‚
客户端å—端å£èµ„æºé™åˆ¶:
- 客户端TIME_WAIT过多,就会导致端å£èµ„æºè¢«å 用,å› ä¸ºç«¯å£å°±65536个,è¢«å æ»¡å°±ä¼šå¯¼è‡´æ— 法创建新的连接。
æœåŠ¡ç«¯å—系统资æºé™åˆ¶:
- 由于一个四元组表示 TCP 连接,ç†è®ºä¸ŠæœåŠ¡ç«¯å¯ä»¥å»ºç«‹å¾ˆå¤šè¿žæŽ¥,æœåŠ¡ç«¯ç¡®å®žåªç›‘å¬ä¸€ä¸ªç«¯å£ 但是会把连接扔给处ç†çº¿ç¨‹,所以ç†è®ºä¸Šç›‘å¬çš„端å£å¯ä»¥ç»§ç»ç›‘å¬ã€‚ä½†æ˜¯çº¿ç¨‹æ± å¤„ç†ä¸äº†é‚£ä¹ˆå¤šä¸€ç›´ä¸æ–的连接了。所以当æœåŠ¡ç«¯å‡ºçŽ°å¤§é‡ TIME_WAIT æ—¶,系统资æºè¢«å 满时,会导致处ç†ä¸è¿‡æ¥æ–°çš„连接。
如何优化 TIME_WAIT?
这里给出优化 TIME-WAIT çš„å‡ ä¸ªæ–¹å¼,都是有利有弊:
- 打开 net.ipv4.tcp_tw_reuse å’Œ net.ipv4.tcp_timestamps 选项;
- net.ipv4.tcp_max_tw_buckets
- 程åºä¸ä½¿ç”¨ SO_LINGER ,应用强制使用 RST å…³é—。
æ–¹å¼ä¸€:net.ipv4.tcp_tw_reuse å’Œ tcp_timestamps
如下的 Linux å†…æ ¸å‚æ•°å¼€å¯åŽ,则å¯ä»¥å¤ç”¨å¤„于 TIME_WAIT çš„ socket 为新的连接所用。
æœ‰ä¸€ç‚¹éœ€è¦æ³¨æ„的是,tcp_tw_reuse 功能åªèƒ½ç”¨å®¢æˆ·ç«¯(连接å‘èµ·æ–¹),å› ä¸ºå¼€å¯äº†è¯¥åŠŸèƒ½,在调用 connect() 函数时,å†…æ ¸ä¼šéšæœºæ‰¾ä¸€ä¸ª time_wait 状æ€è¶…过 1 秒的连接给新的连接å¤ç”¨ã€‚
net.ipv4.tcp_tw_reuse = 1
使用这个选项,è¿˜æœ‰ä¸€ä¸ªå‰æ,éœ€è¦æ‰“开对 TCP 时间戳的支æŒ,å³
net.ipv4.tcp_timestamps=1(默认å³ä¸º 1)
è¿™ä¸ªæ—¶é—´æˆ³çš„å—æ®µæ˜¯åœ¨ TCP 头部的「选项ã€é‡Œ,用于记录 TCP å‘逿–¹çš„当剿—¶é—´æˆ³å’Œä»Žå¯¹ç«¯æŽ¥æ”¶åˆ°çš„æœ€æ–°æ—¶é—´æˆ³ã€‚
由于引入了时间戳,我们在å‰é¢æåˆ°çš„ 2MSL 问题就ä¸å¤å˜åœ¨äº†,å› ä¸ºé‡å¤çš„æ•°æ®åŒ…ä¼šå› ä¸ºæ—¶é—´æˆ³è¿‡æœŸè¢«è‡ªç„¶ä¸¢å¼ƒã€‚
æ–¹å¼äºŒ:net.ipv4.tcp_max_tw_buckets
这个值默认为 18000,当系统ä¸å¤„于 TIME_WAIT 的连接一旦超过这个值时,系统就会将åŽé¢çš„ TIME_WAIT 连接状æ€é‡ç½®ã€‚
这个方法过于暴力,è€Œä¸”æ²»æ ‡ä¸æ²»æœ¬,带æ¥çš„问题远比解决的问题多,䏿ލè使用。
æ–¹å¼ä¸‰:程åºä¸ä½¿ç”¨ SO_LINGER
我们å¯ä»¥é€šè¿‡è®¾ç½® socket 选项,æ¥è®¾ç½®è°ƒç”¨ close å…³é—连接行为。
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));
如果l_onoffä¸ºéž 0, 且l_linger值为 0,那么调用closeåŽ,会立该å‘é€ä¸€ä¸ªRSTæ ‡å¿—ç»™å¯¹ç«¯,该 TCP 连接将跳过四次挥手,也就跳过了TIME_WAIT状æ€,直接关é—。
但这为跨越TIME_WAITçŠ¶æ€æä¾›äº†ä¸€ä¸ªå¯èƒ½,ä¸è¿‡æ˜¯ä¸€ä¸ªéžå¸¸å±é™©çš„行为,ä¸å€¼å¾—æå€¡ã€‚
如果已ç»å»ºç«‹äº†è¿žæŽ¥,但是客户端çªç„¶å‡ºçŽ°æ•…éšœäº†æ€Žä¹ˆåŠž?
TCP æœ‰ä¸€ä¸ªæœºåˆ¶æ˜¯ä¿æ´»æœºåˆ¶ã€‚è¿™ä¸ªæœºåˆ¶çš„åŽŸç†æ˜¯è¿™æ ·çš„:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP ä¿æ´»æœºåˆ¶ä¼šå¼€å§‹ä½œç”¨,æ¯éš”一个时间间隔,å‘é€ä¸€ä¸ªæŽ¢æµ‹æŠ¥æ–‡,该探测报文包å«çš„æ•°æ®éžå¸¸å°‘,如果连ç»å‡ 个探测报文都没有得到å“应,则认为当å‰çš„ TCP è¿žæŽ¥å·²ç»æ»äº¡,ç³»ç»Ÿå†…æ ¸å°†é”™è¯¯ä¿¡æ¯é€šçŸ¥ç»™ä¸Šå±‚应用程åºã€‚
在 Linux å†…æ ¸å¯ä»¥æœ‰å¯¹åº”çš„å‚æ•°å¯ä»¥è®¾ç½®ä¿æ´»æ—¶é—´ã€ä¿æ´»æŽ¢æµ‹çš„æ¬¡æ•°ã€ä¿æ´»æŽ¢æµ‹çš„æ—¶é—´é—´éš”,以下都为默认值:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
- tcp_keepalive_time=7200:è¡¨ç¤ºä¿æ´»æ—¶é—´æ˜¯ 7200 ç§’(2å°æ—¶),也就 2 å°æ—¶å†…如果没有任何连接相关的活动,则会å¯åŠ¨ä¿æ´»æœºåˆ¶
- tcp_keepalive_intvl=75:è¡¨ç¤ºæ¯æ¬¡æ£€æµ‹é—´éš” 75 ç§’;
- tcp_keepalive_probes=9:表示检测 9 æ¬¡æ— å“应,认为对方是ä¸å¯è¾¾çš„,ä»Žè€Œä¸æ–本次的连接。
也就是说在 Linux 系统ä¸,最少需è¦ç»è¿‡ 2 å°æ—¶ 11 分 15 ç§’æ‰å¯ä»¥å‘现一个「æ»äº¡ã€è¿žæŽ¥ã€‚
这个时间是有点长的,我们也å¯ä»¥æ ¹æ®å®žé™…的需求,å¯¹ä»¥ä¸Šçš„ä¿æ´»ç›¸å…³çš„傿•°è¿›è¡Œè®¾ç½®ã€‚
如果开å¯äº† TCP ä¿æ´»,需è¦è€ƒè™‘ä»¥ä¸‹å‡ ç§æƒ…况:
第一ç§,å¯¹ç«¯ç¨‹åºæ˜¯æ£å¸¸å·¥ä½œçš„。当 TCP ä¿æ´»çš„æŽ¢æµ‹æŠ¥æ–‡å‘é€ç»™å¯¹ç«¯, 对端会æ£å¸¸å“应,è¿™æ · TCP ä¿æ´»æ—¶é—´ä¼šè¢«é‡ç½®,ç‰å¾…下一个 TCP ä¿æ´»æ—¶é—´çš„到æ¥ã€‚
第二ç§,对端程åºå´©æºƒå¹¶é‡å¯ã€‚当 TCP ä¿æ´»çš„æŽ¢æµ‹æŠ¥æ–‡å‘é€ç»™å¯¹ç«¯åŽ,对端是å¯ä»¥å“应的,但由于没有该连接的有效信æ¯,会产生一个 RST 报文,è¿™æ ·å¾ˆå¿«å°±ä¼šå‘现 TCP 连接已ç»è¢«é‡ç½®ã€‚
第三ç§,是对端程åºå´©æºƒ,æˆ–å¯¹ç«¯ç”±äºŽå…¶ä»–åŽŸå› å¯¼è‡´æŠ¥æ–‡ä¸å¯è¾¾ã€‚当 TCP ä¿æ´»çš„æŽ¢æµ‹æŠ¥æ–‡å‘é€ç»™å¯¹ç«¯åŽ,石沉大海,没有å“应,连ç»å‡ 次,è¾¾åˆ°ä¿æ´»æŽ¢æµ‹æ¬¡æ•°åŽ,TCP 会报告该 TCP è¿žæŽ¥å·²ç»æ»äº¡ã€‚
04 Socket 编程
针对 TCP 应该如何 Socket 编程?
- æœåŠ¡ç«¯å’Œå®¢æˆ·ç«¯åˆå§‹åŒ–
socket,得到文件æè¿°ç¬¦; - æœåŠ¡ç«¯è°ƒç”¨
bind,将绑定在 IP 地å€å’Œç«¯å£; - æœåŠ¡ç«¯è°ƒç”¨
listen,进行监å¬; - æœåŠ¡ç«¯è°ƒç”¨
accept,ç‰å¾…客户端连接; - 客户端调用
connect,呿œåŠ¡å™¨ç«¯çš„åœ°å€å’Œç«¯å£å‘起连接请求; - æœåŠ¡ç«¯
acceptè¿”å›žç”¨äºŽä¼ è¾“çš„socket的文件æè¿°ç¬¦; - 客户端调用
write写入数æ®;æœåŠ¡ç«¯è°ƒç”¨readè¯»å–æ•°æ®; - 客户端æ–开连接时,会调用
close,那么æœåŠ¡ç«¯readè¯»å–æ•°æ®çš„æ—¶å€™,就会读å–到了EOF,待处ç†å®Œæ•°æ®åŽ,æœåŠ¡ç«¯è°ƒç”¨close,表示连接关é—。
è¿™é‡Œéœ€è¦æ³¨æ„的是,æœåŠ¡ç«¯è°ƒç”¨ accept æ—¶,连接æˆåŠŸäº†ä¼šè¿”å›žä¸€ä¸ªå·²å®Œæˆè¿žæŽ¥çš„ socket,åŽç»ç”¨æ¥ä¼ 输数æ®ã€‚
所以,监å¬çš„ socket 和真æ£ç”¨æ¥ä¼ 逿•°æ®çš„ socket,是「两个〠socket,一个å«ä½œç›‘å¬ socket,一个å«ä½œå·²å®Œæˆè¿žæŽ¥ socket。
æˆåŠŸè¿žæŽ¥å»ºç«‹ä¹‹åŽ,åŒæ–¹å¼€å§‹é€šè¿‡ read å’Œ write 函数æ¥è¯»å†™æ•°æ®,å°±åƒå¾€ä¸€ä¸ªæ–‡ä»¶æµé‡Œé¢å†™ä¸œè¥¿ä¸€æ ·ã€‚
listen æ—¶å€™å‚æ•° backlog çš„æ„义?
Linuxå†…æ ¸ä¸ä¼šç»´æŠ¤ä¸¤ä¸ªé˜Ÿåˆ—:
- 未完æˆè¿žæŽ¥é˜Ÿåˆ—(SYN 队列):接收到一个 SYN 建立连接请求,处于 SYN_RCVD 状æ€;
- 已完æˆè¿žæŽ¥é˜Ÿåˆ—(Accpet 队列):å·²å®Œæˆ TCP ä¸‰æ¬¡æ¡æ‰‹è¿‡ç¨‹,处于 ESTABLISHED 状æ€;
int listen (int socketfd, int backlog)
- 傿•°ä¸€ socketfd 为 socketfd 文件æè¿°ç¬¦
- 傿•°äºŒ backlog,è¿™å‚æ•°åœ¨åކå²ç‰ˆæœ¬æœ‰ä¸€å®šçš„å˜åŒ–
在早期 Linux å†…æ ¸ backlog 是 SYN 队列大å°,也就是未完æˆçš„队列大å°ã€‚
在 Linux å†…æ ¸ 2.2 之åŽ,backlog å˜æˆ accept 队列,也就是已完æˆè¿žæŽ¥å»ºç«‹çš„队列长度,所以现在通常认为 backlog 是 accept 队列。
但是上é™å€¼æ˜¯å†…æ ¸å‚æ•° somaxconn 的大å°,也就说 accpet 队列长度 = min(backlog, somaxconn)。
accept å‘ç”Ÿåœ¨ä¸‰æ¬¡æ¡æ‰‹çš„哪一æ¥?
我们先看看客户端连接æœåŠ¡ç«¯æ—¶,å‘é€äº†ä»€ä¹ˆ?
- 客户端的åè®®æ ˆå‘æœåŠ¡å™¨ç«¯å‘é€äº† SYN 包,并告诉æœåŠ¡å™¨ç«¯å½“å‰å‘é€åºåˆ—å· client_isn,客户端进入 SYN_SENT 状æ€;
- æœåŠ¡å™¨ç«¯çš„åè®®æ ˆæ”¶åˆ°è¿™ä¸ªåŒ…ä¹‹åŽ,和客户端进行 ACK 应ç”,应ç”的值为 client_isn+1,表示对 SYN 包 client_isn 的确认,åŒæ—¶æœåŠ¡å™¨ä¹Ÿå‘é€ä¸€ä¸ª SYN 包,å‘Šè¯‰å®¢æˆ·ç«¯å½“å‰æˆ‘çš„å‘é€åºåˆ—å·ä¸º server_isn,æœåŠ¡å™¨ç«¯è¿›å…¥ SYN_RCVD 状æ€;
- 客户端åè®®æ ˆæ”¶åˆ° ACK 之åŽ,使得应用程åºä»Ž
connect调用返回,表示客户端到æœåŠ¡å™¨ç«¯çš„å•å‘连接建立æˆåŠŸ,客户端的状æ€ä¸º ESTABLISHED,åŒæ—¶å®¢æˆ·ç«¯åè®®æ ˆä¹Ÿä¼šå¯¹æœåŠ¡å™¨ç«¯çš„ SYN 包进行应ç”,åº”ç”æ•°æ®ä¸º server_isn+1; - 应ç”包到达æœåŠ¡å™¨ç«¯åŽ,æœåŠ¡å™¨ç«¯åè®®æ ˆä½¿å¾—
accept阻塞调用返回,这个时候æœåŠ¡å™¨ç«¯åˆ°å®¢æˆ·ç«¯çš„å•å‘连接也建立æˆåŠŸ,æœåŠ¡å™¨ç«¯ä¹Ÿè¿›å…¥ ESTABLISHED 状æ€ã€‚
从上é¢çš„æè¿°è¿‡ç¨‹,我们å¯ä»¥å¾—知客户端 connect æˆåŠŸè¿”å›žæ˜¯åœ¨ç¬¬äºŒæ¬¡æ¡æ‰‹,æœåŠ¡ç«¯ accept æˆåŠŸè¿”å›žæ˜¯åœ¨ä¸‰æ¬¡æ¡æ‰‹æˆåŠŸä¹‹åŽã€‚
客户端调用 close 了,连接是æ–开的æµç¨‹æ˜¯ä»€ä¹ˆ?
我们看看客户端主动调用了 close,会å‘生什么?
- 客户端调用
close,表明客户端没有数æ®éœ€è¦å‘é€äº†,åˆ™æ¤æ—¶ä¼šå‘æœåŠ¡ç«¯å‘é€ FIN 报文,进入 FIN_WAIT_1 状æ€; - æœåŠ¡ç«¯æŽ¥æ”¶åˆ°äº† FIN 报文,TCP åè®®æ ˆä¼šä¸º FIN 包æ’入一个文件结æŸç¬¦
EOF到接收缓冲区ä¸,应用程åºå¯ä»¥é€šè¿‡readè°ƒç”¨æ¥æ„ŸçŸ¥è¿™ä¸ª FIN 包。这个EOF会被放在已排队ç‰å€™çš„其他已接收的数æ®ä¹‹åŽ,这就æ„å‘³ç€æœåŠ¡ç«¯éœ€è¦å¤„ç†è¿™ç§å¼‚常情况,å› ä¸º EOF è¡¨ç¤ºåœ¨è¯¥è¿žæŽ¥ä¸Šå†æ— é¢å¤–æ•°æ®åˆ°è¾¾ã€‚æ¤æ—¶,æœåŠ¡ç«¯è¿›å…¥ CLOSE_WAIT 状æ€; - 接ç€,当处ç†å®Œæ•°æ®åŽ,自然就会读到
EOF,于是也调用closeå…³é—它的套接å—,这会使得客户端会å‘出一个 FIN 包,之åŽå¤„于 LAST_ACK 状æ€; - 客户端接收到æœåŠ¡ç«¯çš„ FIN 包,å¹¶å‘é€ ACK 确认包给æœåŠ¡ç«¯,æ¤æ—¶å®¢æˆ·ç«¯å°†è¿›å…¥ TIME_WAIT 状æ€;
- æœåŠ¡ç«¯æ”¶åˆ° ACK 确认包åŽ,就进入了最åŽçš„ CLOSE 状æ€;
- 客户端ç»è¿‡
2MSL时间之åŽ,也进入 CLOSE 状æ€;
05 é‡ä¼ 机制
TCP 实现å¯é ä¼ è¾“çš„æ–¹å¼ä¹‹ä¸€,是通过åºåˆ—å·ä¸Žç¡®è®¤åº”ç”。
在 TCP ä¸,当å‘é€ç«¯çš„æ•°æ®åˆ°è¾¾æŽ¥æ”¶ä¸»æœºæ—¶,æŽ¥æ”¶ç«¯ä¸»æœºä¼šè¿”å›žä¸€ä¸ªç¡®è®¤åº”ç”æ¶ˆæ¯,表示已收到消æ¯ã€‚
ä½†åœ¨é”™ç»¼å¤æ‚的网络,å¹¶ä¸ä¸€å®šèƒ½å¦‚上图那么顺利能æ£å¸¸çš„æ•°æ®ä¼ 输,万一数æ®åœ¨ä¼ 输过程ä¸ä¸¢å¤±äº†å‘¢?
所以 TCP 针对数æ®åŒ…丢失的情况,会用é‡ä¼ 机制解决。
接下æ¥è¯´è¯´å¸¸è§çš„é‡ä¼ 机制:
- è¶…æ—¶é‡ä¼
- 快速é‡ä¼
- SACK
- D-SACK
5.1 è¶…æ—¶é‡ä¼
é‡ä¼ 机制的其ä¸ä¸€ä¸ªæ–¹å¼,就是在å‘逿•°æ®æ—¶,设定一个定时器,当超过指定的时间åŽ,没有收到对方的 ACK ç¡®è®¤åº”ç”æŠ¥æ–‡,就会é‡å‘该数æ®,也就是我们常说的超时é‡ä¼ 。
TCP ä¼šåœ¨ä»¥ä¸‹ä¸¤ç§æƒ…况å‘生超时é‡ä¼ :
- æ•°æ®åŒ…丢失
- 确认应ç”丢失
超时时间应该设置为多少呢?
我们先æ¥äº†è§£ä¸€ä¸‹ä»€ä¹ˆæ˜¯ RTT(Round-Trip Time 往返时延),从下图我们就å¯ä»¥çŸ¥é“:
RTT 就是数æ®ä»Žç½‘ç»œä¸€ç«¯ä¼ é€åˆ°å¦ä¸€ç«¯æ‰€éœ€çš„æ—¶é—´,也就是包的往返时间。
è¶…æ—¶é‡ä¼ 时间是以 RTO (Retransmission Timeout è¶…æ—¶é‡ä¼ æ—¶é—´)表示。
å‡è®¾åœ¨é‡ä¼ 的情况下,è¶…æ—¶æ—¶é—´ RTO 「较长或较çŸã€æ—¶,会å‘生什么事情呢?
ä¸Šå›¾ä¸æœ‰ä¸¤ç§è¶…æ—¶æ—¶é—´ä¸åŒçš„æƒ…况:
- 当超时时间 RTO 较大时,é‡å‘就慢,丢了è€åŠå¤©æ‰é‡å‘,没有效率,性能差;
- 当超时时间 RTO è¾ƒå°æ—¶,会导致å¯èƒ½å¹¶æ²¡æœ‰ä¸¢å°±é‡å‘,于是é‡å‘的就快,ä¼šå¢žåŠ ç½‘ç»œæ‹¥å¡ž,导致更多的超时,更多的超时导致更多的é‡å‘。
精确的测é‡è¶…æ—¶æ—¶é—´ RTO 的值是éžå¸¸é‡è¦çš„,è¿™å¯è®©æˆ‘们的é‡ä¼ 机制更高效。
æ ¹æ®ä¸Šè¿°çš„ä¸¤ç§æƒ…况,我们å¯ä»¥å¾—知,è¶…æ—¶é‡ä¼ æ—¶é—´ RTO 的值应该略大于报文往返 RTT 的值。
至æ¤,å¯èƒ½å¤§å®¶è§‰å¾—è¶…æ—¶é‡ä¼ æ—¶é—´ RTO 的值计算,ä¹Ÿä¸æ˜¯å¾ˆå¤æ‚嘛。
好åƒå°±æ˜¯åœ¨å‘é€ç«¯å‘包时记下 t0 ,ç„¶åŽæŽ¥æ”¶ç«¯å†æŠŠè¿™ä¸ª ack å›žæ¥æ—¶å†è®°ä¸€ä¸ª t1,于是 RTT = t1 – t0。没那么简å•,è¿™åªæ˜¯ä¸€ä¸ªé‡‡æ ·,ä¸èƒ½ä»£è¡¨æ™®é情况。
实际上「报文往返 RTT çš„å€¼ã€æ˜¯ç»å¸¸å˜åŒ–çš„,å› ä¸ºæˆ‘ä»¬çš„ç½‘ç»œä¹Ÿæ˜¯æ—¶å¸¸å˜åŒ–çš„ã€‚ä¹Ÿå°±å› ä¸ºã€ŒæŠ¥æ–‡å¾€è¿” RTT 的值〠是ç»å¸¸æ³¢åЍå˜åŒ–çš„,所以「超时é‡ä¼ æ—¶é—´ RTO 的值ã€åº”该是一个动æ€å˜åŒ–的值。
我们æ¥çœ‹çœ‹ Linux 是如何计算 RTO 的呢?
估计往返时间,通常需è¦é‡‡æ ·ä»¥ä¸‹ä¸¤ä¸ª:
- éœ€è¦ TCP é€šè¿‡é‡‡æ · RTT 的时间,ç„¶åŽè¿›è¡ŒåŠ æƒå¹³å‡,算出一个平滑 RTT 的值,而且这个值还是è¦ä¸æ–å˜åŒ–çš„,å› ä¸ºç½‘ç»œçŠ¶å†µä¸æ–地å˜åŒ–。
- é™¤äº†é‡‡æ · RTT,还è¦é‡‡æ · RTT 的波动范围,è¿™æ ·å°±é¿å…如果 RTT 有一个大的波动的è¯,很难被å‘现的情况。
RFC6289 建议使用以下的公å¼è®¡ç®— RTO:
å…¶ä¸ SRTT 是计算平滑的RTT ,DevRTR 是计算平滑的RTT 与 最新 RTT 的差è·ã€‚
在 Linux 下,α = 0.125,β = 0.25, μ = 1,∂ = 4。别问怎么æ¥çš„,问就是大é‡å®žéªŒä¸è°ƒå‡ºæ¥çš„。
如果超时é‡å‘的数æ®,冿¬¡è¶…时的时候,åˆéœ€è¦é‡ä¼ 的时候,TCP çš„ç–ç•¥æ˜¯è¶…æ—¶é—´éš”åŠ å€ã€‚
也就是æ¯å½“é‡åˆ°ä¸€æ¬¡è¶…æ—¶é‡ä¼ 的时候,都会将下一次超时时间间隔设为先å‰å€¼çš„两å€ã€‚两次超时,就说明网络环境差,ä¸å®œé¢‘ç¹åå¤å‘é€ã€‚
超时触å‘é‡ä¼ å˜åœ¨çš„问题是,超时周期å¯èƒ½ç›¸å¯¹è¾ƒé•¿ã€‚é‚£æ˜¯ä¸æ˜¯å¯ä»¥æœ‰æ›´å¿«çš„æ–¹å¼å‘¢?
于是就å¯ä»¥ç”¨ã€Œå¿«é€Ÿé‡ä¼ ã€æœºåˆ¶æ¥è§£å†³è¶…æ—¶é‡å‘的时间ç‰å¾…。
5.2 快速é‡ä¼
TCP 还有å¦å¤–一ç§å¿«é€Ÿé‡ä¼ (Fast Retransmit)机制,它ä¸ä»¥æ—¶é—´ä¸ºé©±åЍ,而是以数æ®é©±åЍé‡ä¼ 。
快速é‡ä¼ 机制,是如何工作的呢?其实很简å•,一图胜åƒè¨€ã€‚
在上图,å‘逿–¹å‘出了 1,2,3,4,5 份数æ®:
- 第一份 Seq1 å…ˆé€åˆ°äº†,于是就 Ack 回 2;
- 结果 Seq2 å› ä¸ºæŸäº›åŽŸå› æ²¡æ”¶åˆ°,Seq3 到达了,于是还是 Ack 回 2;
- åŽé¢çš„ Seq4 å’Œ Seq5 都到了,但还是 Ack 回 2,å› ä¸º Seq2 还是没有收到;
- å‘é€ç«¯æ”¶åˆ°äº†ä¸‰ä¸ª Ack = 2 的确认,知é“了 Seq2 还没有收到,就会在定时器过期之å‰,é‡ä¼ 丢失的 Seq2。
- 最åŽ,收到了 Seq2,æ¤æ—¶å› 为 Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6 。
所以,快速é‡ä¼ çš„å·¥ä½œæ–¹å¼æ˜¯å½“收到三个相åŒçš„ ACK 报文时,会在定时器过期之å‰,é‡ä¼ 丢失的报文段。
快速é‡ä¼ 机制åªè§£å†³äº†ä¸€ä¸ªé—®é¢˜,就是超时时间的问题,但是它ä¾ç„¶é¢ä¸´ç€å¦å¤–一个问题。就是é‡ä¼ 的时候,是é‡ä¼ 之å‰çš„一个,还是é‡ä¼ 所有的问题。
比如对于上é¢çš„例å,是é‡ä¼ Seq2 å‘¢?还是é‡ä¼ Seq2ã€Seq3ã€Seq4ã€Seq5 å‘¢?å› ä¸ºå‘é€ç«¯å¹¶ä¸æ¸…楚这连ç»çš„三个 Ack 2 是è°ä¼ 回æ¥çš„。
æ ¹æ® TCP ä¸åŒçš„实现,ä»¥ä¸Šä¸¤ç§æƒ…况都是有å¯èƒ½çš„。å¯è§,这是一把åŒåˆƒå‰‘。
为了解决ä¸çŸ¥é“该é‡ä¼ 哪些 TCP 报文,于是就有 SACK 方法。
5.3 SACK 方法
还有一ç§å®žçްé‡ä¼ 机制的方å¼å«:SACK( Selective Acknowledgment 选择性确认)。
è¿™ç§æ–¹å¼éœ€è¦åœ¨ TCP 头部「选项ã€å—æ®µé‡ŒåŠ ä¸€ä¸ª SACK 的东西,它å¯ä»¥å°†ç¼“å˜çš„地图å‘é€ç»™å‘逿–¹,è¿™æ ·å‘逿–¹å°±å¯ä»¥çŸ¥é“å“ªäº›æ•°æ®æ”¶åˆ°äº†,å“ªäº›æ•°æ®æ²¡æ”¶åˆ°,知é“了这些信æ¯,å°±å¯ä»¥åªé‡ä¼ 丢失的数æ®ã€‚
如下图,å‘逿–¹æ”¶åˆ°äº†ä¸‰æ¬¡åŒæ ·çš„ ACK 确认报文,于是就会触å‘快速é‡å‘机制,通过 SACK ä¿¡æ¯å‘çŽ°åªæœ‰ 200~299 这段数æ®ä¸¢å¤±,则é‡å‘æ—¶,å°±åªé€‰æ‹©äº†è¿™ä¸ª TCP 段进行é‡å¤ã€‚
å¦‚æžœè¦æ”¯æŒ SACK,å¿…é¡»åŒæ–¹éƒ½è¦æ”¯æŒã€‚在 Linux 下,å¯ä»¥é€šè¿‡ net.ipv4.tcp_sack 傿•°æ‰“开这个功能(Linux 2.4 åŽé»˜è®¤æ‰“å¼€)。
5.4 Duplicate SACK
Duplicate SACK åˆç§° D-SACK,其主è¦ä½¿ç”¨äº† SACK æ¥å‘Šè¯‰ã€Œå‘逿–¹ã€æœ‰å“ªäº›æ•°æ®è¢«é‡å¤æŽ¥æ”¶äº†ã€‚
下é¢ä¸¾ä¾‹ä¸¤ä¸ªæ —å,æ¥è¯´æ˜Ž D-SACK 的作用。
æ —å一å·:ACK 丢包
- 「接收方ã€å‘给「å‘逿–¹ã€çš„两个 ACK 确认应ç”都丢失了,所以å‘逿–¹è¶…æ—¶åŽ,é‡ä¼ 第一个数æ®åŒ…(3000 ~ 3499)
- 于是「接收方ã€å‘çŽ°æ•°æ®æ˜¯é‡å¤æ”¶åˆ°çš„,于是回了一个 SACK = 3000~3500,告诉「å‘逿–¹ã€ 3000~3500 çš„æ•°æ®æ—©å·²è¢«æŽ¥æ”¶äº†,å› ä¸º ACK 都到了 4000 了,å·²ç»æ„å‘³ç€ 4000 之å‰çš„æ‰€æœ‰æ•°æ®éƒ½å·²æ”¶åˆ°,所以这个 SACK 就代表ç€
D-SACK。 - è¿™æ ·ã€Œå‘逿–¹ã€å°±çŸ¥é“了,æ•°æ®æ²¡æœ‰ä¸¢,是「接收方ã€çš„ ACK 确认报文丢了。
æ —å二å·:网络延时
- æ•°æ®åŒ…(1000~1499) 被网络延迟了,导致「å‘逿–¹ã€æ²¡æœ‰æ”¶åˆ° Ack 1500 的确认报文。
- 而åŽé¢æŠ¥æ–‡åˆ°è¾¾çš„三个相åŒçš„ ACK 确认报文,就触å‘了快速é‡ä¼ 机制,但是在é‡ä¼ åŽ,被延迟的数æ®åŒ…(1000~1499)åˆåˆ°äº†ã€ŒæŽ¥æ”¶æ–¹ã€;
- 所以「接收方ã€å›žäº†ä¸€ä¸ª SACK=1000~1500,å› ä¸º ACK å·²ç»åˆ°äº† 3000,所以这个 SACK 是 D-SACK,表示收到了é‡å¤çš„包。
- è¿™æ ·å‘逿–¹å°±çŸ¥é“快速é‡ä¼ 触å‘çš„åŽŸå› ä¸æ˜¯å‘出去的包丢了,ä¹Ÿä¸æ˜¯å› 为回应的 ACK 包丢了,è€Œæ˜¯å› ä¸ºç½‘ç»œå»¶è¿Ÿäº†ã€‚
å¯è§,D-SACK æœ‰è¿™ä¹ˆå‡ ä¸ªå¥½å¤„:
- å¯ä»¥è®©ã€Œå‘逿–¹ã€çŸ¥é“,是å‘出去的包丢了,还是接收方回应的 ACK 包丢了;
- å¯ä»¥çŸ¥é“æ˜¯ä¸æ˜¯ã€Œå‘逿–¹ã€çš„æ•°æ®åŒ…被网络延迟了;
- å¯ä»¥çŸ¥é“ç½‘ç»œä¸æ˜¯ä¸æ˜¯æŠŠã€Œå‘逿–¹ã€çš„æ•°æ®åŒ…ç»™å¤åˆ¶äº†;
在 Linux 下å¯ä»¥é€šè¿‡ net.ipv4.tcp_dsack 傿•°å¼€å¯/å…³é—这个功能(Linux 2.4 åŽé»˜è®¤æ‰“å¼€)。
06 滑动窗å£
å¼•å…¥çª—å£æ¦‚念的原å›
æˆ‘ä»¬éƒ½çŸ¥é“ TCP 是æ¯å‘é€ä¸€ä¸ªæ•°æ®,都è¦è¿›è¡Œä¸€æ¬¡ç¡®è®¤åº”ç”。当上一个数æ®åŒ…收到了应ç”了, å†å‘é€ä¸‹ä¸€ä¸ªã€‚
这个模å¼å°±æœ‰ç‚¹åƒæˆ‘å’Œä½ é¢å¯¹é¢èŠå¤©,ä½ ä¸€å¥æˆ‘一å¥ã€‚ä½†è¿™ç§æ–¹å¼çš„缺点是效率比较低的。
å¦‚æžœä½ è¯´å®Œä¸€å¥è¯,我在处ç†å…¶ä»–事情,æ²¡æœ‰åŠæ—¶å›žå¤ä½ ,é‚£ä½ ä¸æ˜¯è¦å¹²ç‰ç€æˆ‘åšå®Œå…¶ä»–事情åŽ,我回å¤ä½ ,ä½ æ‰èƒ½è¯´ä¸‹ä¸€å¥è¯,很显然这ä¸çŽ°å®žã€‚
所以,è¿™æ ·çš„ä¼ è¾“æ–¹å¼æœ‰ä¸€ä¸ªç¼ºç‚¹:æ•°æ®åŒ…的往返时间越长,通信的效率就越低。
为解决这个问题,TCP 引入了窗å£è¿™ä¸ªæ¦‚念。å³ä½¿åœ¨å¾€è¿”时间较长的情况下,它也ä¸ä¼šé™ä½Žç½‘络通信的效率。
那么有了窗å£,å°±å¯ä»¥æŒ‡å®šçª—å£å¤§å°,窗å£å¤§å°å°±æ˜¯æŒ‡æ— 需ç‰å¾…确认应ç”,而å¯ä»¥ç»§ç»å‘逿•°æ®çš„æœ€å¤§å€¼ã€‚
窗å£çš„实现实际上是æ“作系统开辟的一个缓å˜ç©ºé—´,å‘逿–¹ä¸»æœºåœ¨ç‰åˆ°ç¡®è®¤åº”ç”返回之å‰,必须在缓冲区ä¸ä¿ç•™å·²å‘é€çš„æ•°æ®ã€‚如果按期收到确认应ç”,æ¤æ—¶æ•°æ®å°±å¯ä»¥ä»Žç¼“å˜åŒºæ¸…除。
å‡è®¾çª—å£å¤§å°ä¸º 3 个 TCP 段,那么å‘逿–¹å°±å¯ä»¥ã€Œè¿žç»å‘é€ã€ 3 个 TCP 段,并且ä¸é€”若有 ACK 丢失,å¯ä»¥é€šè¿‡ã€Œä¸‹ä¸€ä¸ªç¡®è®¤åº”ç”进行确认ã€ã€‚如下图:
图ä¸çš„ ACK 600 ç¡®è®¤åº”ç”æŠ¥æ–‡ä¸¢å¤±,也没关系,å› ä¸ºå¯ä»¥é€šè¿‡ä¸‹ä¸€ä¸ªç¡®è®¤åº”ç”进行确认,åªè¦å‘逿–¹æ”¶åˆ°äº† ACK 700 确认应ç”,å°±æ„å‘³ç€ 700 之å‰çš„æ‰€æœ‰æ•°æ®ã€ŒæŽ¥æ”¶æ–¹ã€éƒ½æ”¶åˆ°äº†ã€‚这个模å¼å°±å«ç´¯è®¡ç¡®è®¤æˆ–者累计应ç”。
窗å£å¤§å°ç”±å“ªä¸€æ–¹å†³å®š?
TCP å¤´é‡Œæœ‰ä¸€ä¸ªå—æ®µå« Window,也就是窗å£å¤§å°ã€‚
è¿™ä¸ªå—æ®µæ˜¯æŽ¥æ”¶ç«¯å‘Šè¯‰å‘é€ç«¯è‡ªå·±è¿˜æœ‰å¤šå°‘缓冲区å¯ä»¥æŽ¥æ”¶æ•°æ®ã€‚于是å‘é€ç«¯å°±å¯ä»¥æ ¹æ®è¿™ä¸ªæŽ¥æ”¶ç«¯çš„处ç†èƒ½åŠ›æ¥å‘逿•°æ®,而ä¸ä¼šå¯¼è‡´æŽ¥æ”¶ç«¯å¤„ç†ä¸è¿‡æ¥ã€‚
所以,通常窗å£çš„大尿˜¯ç”±æŽ¥æ”¶æ–¹çš„窗å£å¤§å°æ¥å†³å®šçš„。
å‘逿–¹å‘é€çš„æ•°æ®å¤§å°ä¸èƒ½è¶…过接收方的窗å£å¤§å°,å¦åˆ™æŽ¥æ”¶æ–¹å°±æ— 法æ£å¸¸æŽ¥æ”¶åˆ°æ•°æ®ã€‚
å‘逿–¹çš„æ»‘动窗å£
我们先æ¥çœ‹çœ‹å‘逿–¹çš„窗å£,下图就是å‘逿–¹ç¼“å˜çš„æ•°æ®,æ ¹æ®å¤„ç†çš„æƒ…况分æˆå››ä¸ªéƒ¨åˆ†,其䏿·±è“色方框是å‘é€çª—å£,紫色方框是å¯ç”¨çª—å£:
- #1 是已å‘é€å¹¶æ”¶åˆ° ACK确认的数æ®:1~31 å—节
- #2 是已å‘é€ä½†æœªæ”¶åˆ° ACK确认的数æ®:32~45 å—节
- #3 是未å‘é€ä½†æ€»å¤§å°åœ¨æŽ¥æ”¶æ–¹å¤„ç†èŒƒå›´å†…(接收方还有空间):46~51å—节
- #4 是未å‘é€ä½†æ€»å¤§å°è¶…过接收方处ç†èŒƒå›´(接收方没有空间):52å—节以åŽ
在下图,当å‘逿–¹æŠŠæ•°æ®ã€Œå…¨éƒ¨ã€éƒ½ä¸€ä¸‹å‘é€å‡ºåŽ»åŽ,å¯ç”¨çª—å£çš„大å°å°±ä¸º 0 了,表明å¯ç”¨çª—å£è€—å°½,在没收到 ACK ç¡®è®¤ä¹‹å‰æ˜¯æ— 法继ç»å‘逿•°æ®äº†ã€‚
在下图,当收到之å‰å‘é€çš„æ•°æ® 32~36 å—节的 ACK 确认应ç”åŽ,如果å‘é€çª—å£çš„大尿²¡æœ‰å˜åŒ–,则滑动窗å£å¾€å³è¾¹ç§»åЍ 5 个å—节,å› ä¸ºæœ‰ 5 个å—节的数æ®è¢«åº”ç”确认,æŽ¥ä¸‹æ¥ 52~56 å—节åˆå˜æˆäº†å¯ç”¨çª—å£,那么åŽç»ä¹Ÿå°±å¯ä»¥å‘é€ 52~56 è¿™ 5 个å—节的数æ®äº†ã€‚
ç¨‹åºæ˜¯å¦‚何表示å‘逿–¹çš„四个部分的呢?
TCP æ»‘åŠ¨çª—å£æ–¹æ¡ˆä½¿ç”¨ä¸‰ä¸ªæŒ‡é’ˆæ¥è·Ÿè¸ªåœ¨å››ä¸ªä¼ 输类别ä¸çš„æ¯ä¸€ä¸ªç±»åˆ«ä¸çš„å—节。其ä¸ä¸¤ä¸ªæŒ‡é’ˆæ˜¯ç»å¯¹æŒ‡é’ˆ(指特定的åºåˆ—å·),一个是相对指针(需è¦åšåç§»)。
-
SND.WND:表示å‘é€çª—å£çš„大å°(大尿˜¯ç”±æŽ¥æ”¶æ–¹æŒ‡å®šçš„); -
SND.UNA:是一个ç»å¯¹æŒ‡é’ˆ,它指å‘的是已å‘é€ä½†æœªæ”¶åˆ°ç¡®è®¤çš„第一个å—节的åºåˆ—å·,也就是 #2 的第一个å—节。 -
SND.NXT:也是一个ç»å¯¹æŒ‡é’ˆ,å®ƒæŒ‡å‘æœªå‘é€ä½†å¯å‘é€èŒƒå›´çš„第一个å—节的åºåˆ—å·,也就是 #3 的第一个å—节。 -
æŒ‡å‘ #4 的第一个å—节是个相对指针,它需è¦
SND.UNAæŒ‡é’ˆåŠ ä¸ŠSND.WND大å°çš„åç§»é‡,å°±å¯ä»¥æŒ‡å‘ #4 的第一个å—节了。
那么å¯ç”¨çª—å£å¤§å°çš„计算就å¯ä»¥æ˜¯:
å¯ç”¨çª—å£å¤§ = SND.WND -(SND.NXT - SND.UNA)
接收方的滑动窗å£
æŽ¥ä¸‹æ¥æˆ‘们看看接收方的窗å£,接收窗å£ç›¸å¯¹ç®€å•一些,æ ¹æ®å¤„ç†çš„æƒ…况划分æˆä¸‰ä¸ªéƒ¨åˆ†:
- #1 + #2 是已æˆåŠŸæŽ¥æ”¶å¹¶ç¡®è®¤çš„æ•°æ®(ç‰å¾…应用进程读å–);
- #3 是未收到数æ®ä½†å¯ä»¥æŽ¥æ”¶çš„æ•°æ®;
- #4 未收到数æ®å¹¶ä¸å¯ä»¥æŽ¥æ”¶çš„æ•°æ®;
å…¶ä¸ä¸‰ä¸ªæŽ¥æ”¶éƒ¨åˆ†,使用两个指针进行划分:
RCV.WND:表示接收窗å£çš„大å°,它会通告给å‘逿–¹ã€‚RCV.NXT:是一个指针,å®ƒæŒ‡å‘æœŸæœ›ä»Žå‘逿–¹å‘逿¥çš„下一个数æ®å—节的åºåˆ—å·,也就是 #3 的第一个å—节。- æŒ‡å‘ #4 的第一个å—节是个相对指针,它需è¦
RCV.NXTæŒ‡é’ˆåŠ ä¸ŠRCV.WND大å°çš„åç§»é‡,å°±å¯ä»¥æŒ‡å‘ #4 的第一个å—节了。
接收窗å£å’Œå‘é€çª—å£çš„大尿˜¯ç›¸ç‰çš„å—?
并䏿˜¯å®Œå…¨ç›¸ç‰,接收窗å£çš„大尿˜¯çº¦ç‰äºŽå‘é€çª—å£çš„大å°çš„。
å› ä¸ºæ»‘åŠ¨çª—å£å¹¶ä¸æ˜¯ä¸€æˆä¸å˜çš„。比如,å½“æŽ¥æ”¶æ–¹çš„åº”ç”¨è¿›ç¨‹è¯»å–æ•°æ®çš„速度éžå¸¸å¿«çš„è¯,è¿™æ ·çš„è¯æŽ¥æ”¶çª—å£å¯ä»¥å¾ˆå¿«çš„就空缺出æ¥ã€‚那么新的接收窗å£å¤§å°,是通过 TCP 报文ä¸çš„ Windows å—æ®µæ¥å‘Šè¯‰å‘逿–¹ã€‚é‚£ä¹ˆè¿™ä¸ªä¼ è¾“è¿‡ç¨‹æ˜¯å˜åœ¨æ—¶å»¶çš„,所以接收窗å£å’Œå‘é€çª—壿˜¯çº¦ç‰äºŽçš„关系。
07 æµé‡æŽ§åˆ¶
å‘逿–¹ä¸èƒ½æ— è„‘çš„å‘æ•°æ®ç»™æŽ¥æ”¶æ–¹,è¦è€ƒè™‘接收方处ç†èƒ½åŠ›ã€‚
å¦‚æžœä¸€ç›´æ— è„‘çš„å‘æ•°æ®ç»™å¯¹æ–¹,但对方处ç†ä¸è¿‡æ¥,那么就会导致触å‘é‡å‘机制,从而导致网络æµé‡çš„æ— 端的浪费。
为了解决这ç§çŽ°è±¡å‘生,TCP æä¾›ä¸€ç§æœºåˆ¶å¯ä»¥è®©ã€Œå‘逿–¹ã€æ ¹æ®ã€ŒæŽ¥æ”¶æ–¹ã€çš„实际接收能力控制å‘é€çš„æ•°æ®é‡,这就是所谓的æµé‡æŽ§åˆ¶ã€‚
下é¢ä¸¾ä¸ªæ —å,为了简å•èµ·è§,å‡è®¾ä»¥ä¸‹åœºæ™¯:
- 客户端是接收方,æœåŠ¡ç«¯æ˜¯å‘逿–¹
- å‡è®¾æŽ¥æ”¶çª—å£å’Œå‘é€çª—å£ç›¸åŒ,都为
200 - å‡è®¾ä¸¤ä¸ªè®¾å¤‡åœ¨æ•´ä¸ªä¼ 输过程ä¸éƒ½ä¿æŒç›¸åŒçš„窗å£å¤§å°,ä¸å—外界影å“
æ ¹æ®ä¸Šå›¾çš„æµé‡æŽ§åˆ¶,说明下æ¯ä¸ªè¿‡ç¨‹:
- å®¢æˆ·ç«¯å‘æœåŠ¡ç«¯å‘é€è¯·æ±‚æ•°æ®æŠ¥æ–‡ã€‚è¿™é‡Œè¦è¯´æ˜Žä¸‹,æœ¬æ¬¡ä¾‹åæ˜¯æŠŠæœåŠ¡ç«¯ä½œä¸ºå‘逿–¹,所以没有画出æœåŠ¡ç«¯çš„æŽ¥æ”¶çª—å£ã€‚
- æœåŠ¡ç«¯æ”¶åˆ°è¯·æ±‚æŠ¥æ–‡åŽ,å‘é€ç¡®è®¤æŠ¥æ–‡å’Œ 80 å—节的数æ®,于是å¯ç”¨çª—å£
Usableå‡å°‘为 120 å—节,åŒæ—¶SND.NXT指针也å‘å³åç§» 80 å—节åŽ,æŒ‡å‘ 321,è¿™æ„味ç€ä¸‹æ¬¡å‘逿•°æ®çš„æ—¶å€™,åºåˆ—å·æ˜¯ 321。 - 客户端收到 80 å—节数æ®åŽ,于是接收窗å£å¾€å³ç§»åЍ 80 å—节,
RCV.NXTä¹Ÿå°±æŒ‡å‘ 321,è¿™æ„味ç€å®¢æˆ·ç«¯æœŸæœ›çš„下一个报文的åºåˆ—å·æ˜¯ 321,接ç€å‘é€ç¡®è®¤æŠ¥æ–‡ç»™æœåŠ¡ç«¯ã€‚ - æœåŠ¡ç«¯å†æ¬¡å‘é€äº† 120 å—节数æ®,于是å¯ç”¨çª—å£è€—尽为 0,æœåŠ¡ç«¯æ— æ³•å†ç»§ç»å‘逿•°æ®ã€‚
- 客户端收到 120 å—节的数æ®åŽ,于是接收窗å£å¾€å³ç§»åЍ 120 å—节,
RCV.NXTä¹Ÿå°±æŒ‡å‘ 441,接ç€å‘é€ç¡®è®¤æŠ¥æ–‡ç»™æœåŠ¡ç«¯ã€‚ - æœåŠ¡ç«¯æ”¶åˆ°å¯¹ 80 å—节数æ®çš„确认报文åŽ,
SND.UNA指针往å³åç§»åŽæŒ‡å‘ 321,于是å¯ç”¨çª—å£Usable增大到 80。 - æœåŠ¡ç«¯æ”¶åˆ°å¯¹ 120 å—节数æ®çš„确认报文åŽ,
SND.UNA指针往å³åç§»åŽæŒ‡å‘ 441,于是å¯ç”¨çª—å£Usable增大到 200。 - æœåŠ¡ç«¯å¯ä»¥ç»§ç»å‘é€äº†,于是å‘é€äº† 160 å—节的数æ®åŽ,
SND.NXTæŒ‡å‘ 601,于是å¯ç”¨çª—å£Usableå‡å°‘到 40。 - 客户端收到 160 å—节åŽ,接收窗å£å¾€å³ç§»åŠ¨äº† 160 å—节,
RCV.NXT也就是指å‘了 601,接ç€å‘é€ç¡®è®¤æŠ¥æ–‡ç»™æœåŠ¡ç«¯ã€‚ - æœåŠ¡ç«¯æ”¶åˆ°å¯¹ 160 å—节数æ®çš„确认报文åŽ,å‘é€çª—å£å¾€å³ç§»åŠ¨äº† 160 å—节,于是
SND.UNA指针å移了 160 åŽæŒ‡å‘ 601,å¯ç”¨çª—å£Usable也就增大至了 200。
7.1 æ“作系统缓冲区与滑动窗å£çš„关系
å‰é¢çš„æµé‡æŽ§åˆ¶ä¾‹å,我们å‡å®šäº†å‘é€çª—å£å’ŒæŽ¥æ”¶çª—壿˜¯ä¸å˜çš„,但是实际上,å‘é€çª—å£å’ŒæŽ¥æ”¶çª—å£ä¸æ‰€å˜æ”¾çš„å—节数,都是放在æ“作系统内å˜ç¼“冲区ä¸çš„,而æ“作系统的缓冲区,会被æ“作系统调整。
å½“åº”ç”¨è¿›ç¨‹æ²¡åŠžæ³•åŠæ—¶è¯»å–缓冲区的内容时,ä¹Ÿä¼šå¯¹æˆ‘ä»¬çš„ç¼“å†²åŒºé€ æˆå½±å“。
é‚£æ“心系统的缓冲区,是如何影å“å‘é€çª—å£å’ŒæŽ¥æ”¶çª—å£çš„å‘¢?
我们先æ¥çœ‹çœ‹ç¬¬ä¸€ä¸ªä¾‹å。
å½“åº”ç”¨ç¨‹åºæ²¡æœ‰åŠæ—¶è¯»å–ç¼“å˜æ—¶,å‘é€çª—å£å’ŒæŽ¥æ”¶çª—å£çš„å˜åŒ–。
考虑以下场景:
- 客户端作为å‘逿–¹,æœåŠ¡ç«¯ä½œä¸ºæŽ¥æ”¶æ–¹,å‘é€çª—å£å’ŒæŽ¥æ”¶çª—å£åˆå§‹å¤§å°ä¸º
360; - æœåŠ¡ç«¯éžå¸¸çš„ç¹å¿™,å½“æ”¶åˆ°å®¢æˆ·ç«¯çš„æ•°æ®æ—¶,应用层ä¸èƒ½åŠæ—¶è¯»å–æ•°æ®ã€‚
æ ¹æ®ä¸Šå›¾çš„æµé‡æŽ§åˆ¶,说明下æ¯ä¸ªè¿‡ç¨‹:
- 客户端å‘é€ 140 å—节数æ®åŽ,å¯ç”¨çª—å£å˜ä¸º 220 (360 - 140)。
- æœåŠ¡ç«¯æ”¶åˆ° 140 å—节数æ®,但是æœåŠ¡ç«¯éžå¸¸ç¹å¿™,应用进程åªè¯»å–了 40 个å—节,还有 100 å—节å 用ç€ç¼“冲区,äºŽæ˜¯æŽ¥æ”¶çª—å£æ”¶ç¼©åˆ°äº† 260 (360 - 100),最åŽå‘é€ç¡®è®¤ä¿¡æ¯æ—¶,将窗å£å¤§å°é€šå‘Šç»™å®¢æˆ·ç«¯ã€‚
- 客户端收到确认和窗å£é€šå‘ŠæŠ¥æ–‡åŽ,å‘é€çª—å£å‡å°‘为 260。
- 客户端å‘é€ 180 å—节数æ®,æ¤æ—¶å¯ç”¨çª—å£å‡å°‘到 80。
- æœåŠ¡ç«¯æ”¶åˆ° 180 å—节数æ®,ä½†æ˜¯åº”ç”¨ç¨‹åºæ²¡æœ‰è¯»å–任何数æ®,è¿™ 180 å—节直接就留在了缓冲区,äºŽæ˜¯æŽ¥æ”¶çª—å£æ”¶ç¼©åˆ°äº† 80 (260 - 180),并在å‘é€ç¡®è®¤ä¿¡æ¯æ—¶,通过窗å£å¤§å°ç»™å®¢æˆ·ç«¯ã€‚
- 客户端收到确认和窗å£é€šå‘ŠæŠ¥æ–‡åŽ,å‘é€çª—å£å‡å°‘为 80。
- 客户端å‘é€ 80 å—节数æ®åŽ,å¯ç”¨çª—å£è€—尽。
- æœåŠ¡ç«¯æ”¶åˆ° 80 å—节数æ®,但是应用程åºä¾ç„¶æ²¡æœ‰è¯»å–任何数æ®,è¿™ 80 å—节留在了缓冲区,äºŽæ˜¯æŽ¥æ”¶çª—å£æ”¶ç¼©åˆ°äº† 0,并在å‘é€ç¡®è®¤ä¿¡æ¯æ—¶,通过窗å£å¤§å°ç»™å®¢æˆ·ç«¯ã€‚
- 客户端收到确认和窗å£é€šå‘ŠæŠ¥æ–‡åŽ,å‘é€çª—å£å‡å°‘为 0。
å¯è§æœ€åŽçª—å£éƒ½æ”¶ç¼©ä¸º 0 了,也就是å‘生了窗å£å…³é—。当å‘逿–¹å¯ç”¨çª—å£å˜ä¸º 0 æ—¶,å‘逿–¹å®žé™…上会定时å‘é€çª—å£æŽ¢æµ‹æŠ¥æ–‡,ä»¥ä¾¿çŸ¥é“æŽ¥æ”¶æ–¹çš„çª—å£æ˜¯å¦å‘生了改å˜,这个内容åŽé¢ä¼šè¯´,è¿™é‡Œå…ˆç®€å•æä¸€ä¸‹ã€‚
我们先æ¥çœ‹çœ‹ç¬¬äºŒä¸ªä¾‹å。
当æœåŠ¡ç«¯ç³»ç»Ÿèµ„æºéžå¸¸ç´§å¼ 的时候,æ“心系统å¯èƒ½ä¼šç›´æŽ¥å‡å°‘了接收缓冲区大å°,这时应用程åºåˆæ— æ³•åŠæ—¶è¯»å–ç¼“å˜æ•°æ®,那么这时候就有严é‡çš„事情å‘生了,会出现数æ®åŒ…丢失的现象。
说明下æ¯ä¸ªè¿‡ç¨‹:
- 客户端å‘é€ 140 å—节的数æ®,于是å¯ç”¨çª—å£å‡å°‘到了 220。
- æœåŠ¡ç«¯å› ä¸ºçŽ°åœ¨éžå¸¸çš„ç¹å¿™,æ“作系统于是就把接收缓å˜å‡å°‘了 120 å—节,当收到 140 å—节数æ®åŽ,åˆå› ä¸ºåº”ç”¨ç¨‹åºæ²¡æœ‰è¯»å–任何数æ®,所以 140 å—节留在了缓冲区ä¸,于是接收窗å£å¤§å°ä»Ž 360 收缩æˆäº† 100,最åŽå‘é€ç¡®è®¤ä¿¡æ¯æ—¶,通告窗å£å¤§å°ç»™å¯¹æ–¹ã€‚
- æ¤æ—¶å®¢æˆ·ç«¯å› 为还没有收到æœåŠ¡ç«¯çš„é€šå‘Šçª—å£æŠ¥æ–‡,所以ä¸çŸ¥é“æ¤æ—¶æŽ¥æ”¶çª—壿”¶ç¼©æˆäº† 100,客户端åªä¼šçœ‹è‡ªå·±çš„å¯ç”¨çª—å£è¿˜æœ‰ 220,所以客户端就å‘é€äº† 180 å—节数æ®,于是å¯ç”¨çª—å£å‡å°‘到 40。
- æœåŠ¡ç«¯æ”¶åˆ°äº† 180 å—èŠ‚æ•°æ®æ—¶,å‘现数æ®å¤§å°è¶…过了接收窗å£çš„大å°,于是就把数æ®åŒ…丢失了。
- 客户端收到第 2 æ¥æ—¶,æœåŠ¡ç«¯å‘é€çš„ç¡®è®¤æŠ¥æ–‡å’Œé€šå‘Šçª—å£æŠ¥æ–‡,å°è¯•å‡å°‘å‘é€çª—å£åˆ° 100,把窗å£çš„å³ç«¯å‘左收缩了 80,æ¤æ—¶å¯ç”¨çª—å£çš„大å°å°±ä¼šå‡ºçŽ°è¯¡å¼‚çš„è´Ÿå€¼ã€‚
所以,如果å‘生了先å‡å°‘缓å˜,冿”¶ç¼©çª—å£,就会出现丢包的现象。
为了防æ¢è¿™ç§æƒ…况å‘生,TCP 规定是ä¸å…è®¸åŒæ—¶å‡å°‘缓å˜åˆæ”¶ç¼©çª—å£çš„,而是采用先收缩窗å£,过段时间å†å‡å°‘缓å˜,è¿™æ ·å°±å¯ä»¥é¿å…了丢包情况。
7.2 窗å£å…³é—
在å‰é¢æˆ‘们都看到了,TCP 通过让接收方指明希望从å‘逿–¹æŽ¥æ”¶çš„æ•°æ®å¤§å°(窗å£å¤§å°)æ¥è¿›è¡Œæµé‡æŽ§åˆ¶ã€‚
如果窗å£å¤§å°ä¸º 0 æ—¶,就会阻æ¢å‘逿–¹ç»™æŽ¥æ”¶æ–¹ä¼ 递数æ®,直到窗å£å˜ä¸ºéž 0 为æ¢,这就是窗å£å…³é—。
窗å£å…³é—潜在的å±é™©
接收方å‘å‘逿–¹é€šå‘Šçª—å£å¤§å°æ—¶,是通过 ACK 报文æ¥é€šå‘Šçš„。
那么,当å‘生窗å£å…³é—æ—¶,接收方处ç†å®Œæ•°æ®åŽ,会å‘å‘逿–¹é€šå‘Šä¸€ä¸ªçª—å£éž 0 çš„ ACK 报文,如果这个通告窗å£çš„ ACK 报文在网络ä¸ä¸¢å¤±äº†,那麻烦就大了。
这会导致å‘逿–¹ä¸€ç›´ç‰å¾…æŽ¥æ”¶æ–¹çš„éž 0 窗å£é€šçŸ¥,接收方也一直ç‰å¾…å‘逿–¹çš„æ•°æ®,如ä¸é‡‡å–措施,è¿™ç§ç›¸äº’ç‰å¾…的过程,ä¼šé€ æˆäº†æ»é”的现象。
TCP 是如何解决窗å£å…³é—æ—¶,潜在的æ»é”现象呢?
为了解决这个问题,TCP 为æ¯ä¸ªè¿žæŽ¥è®¾æœ‰ä¸€ä¸ªæŒç»å®šæ—¶å™¨,åªè¦ TCP 连接一方收到对方的零窗å£é€šçŸ¥,å°±å¯åЍæŒç»è®¡æ—¶å™¨ã€‚
如果æŒç»è®¡æ—¶å™¨è¶…æ—¶,就会å‘é€çª—å£æŽ¢æµ‹ ( Window
probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗å£å¤§å°ã€‚
- 如果接收窗å£ä»ç„¶ä¸º 0,é‚£ä¹ˆæ”¶åˆ°è¿™ä¸ªæŠ¥æ–‡çš„ä¸€æ–¹å°±ä¼šé‡æ–°å¯åЍæŒç»è®¡æ—¶å™¨;
- 如果接收窗å£ä¸æ˜¯ 0,那么æ»é”的局é¢å°±å¯ä»¥è¢«æ‰“ç ´äº†ã€‚
çª—å£æŽ¢æµ‹çš„æ¬¡æ•°ä¸€èˆ¬ä¸º 3 次,æ¯æ¬¡å¤§çº¦ 30-60 ç§’(ä¸åŒçš„实现å¯èƒ½ä¼šä¸ä¸€æ ·)。如果 3 æ¬¡è¿‡åŽæŽ¥æ”¶çª—å£è¿˜æ˜¯ 0 çš„è¯,有的 TCP å®žçŽ°å°±ä¼šå‘ RST 报文æ¥ä¸æ–连接。
7.3 糊涂窗å£ç»¼åˆç—‡
如果接收方太忙了,æ¥ä¸åŠå–走接收窗å£é‡Œçš„æ•°æ®,那么就会导致å‘逿–¹çš„å‘é€çª—å£è¶Šæ¥è¶Šå°ã€‚
到最åŽ,å¦‚æžœæŽ¥æ”¶æ–¹è…¾å‡ºå‡ ä¸ªå—节并告诉å‘逿–¹çŽ°åœ¨æœ‰å‡ ä¸ªå—节的窗å£,而å‘逿–¹ä¼šä¹‰æ— å顾地å‘é€è¿™å‡ 个å—节,这就是糊涂窗å£ç»¼åˆç—‡ã€‚
è¦çŸ¥é“,我们的 TCP + IP 头有 40 个å—节,ä¸ºäº†ä¼ è¾“é‚£å‡ ä¸ªå—节的数æ®,è¦è¾¾ä¸Šè¿™ä¹ˆå¤§çš„开销,这太ä¸ç»æµŽäº†ã€‚
就好åƒä¸€ä¸ªå¯ä»¥æ‰¿è½½ 50 人的大巴车,æ¯æ¬¡æ¥äº†ä¸€ä¸¤ä¸ªäºº,就直接å‘车。除éžå®¶é‡Œæœ‰çŸ¿çš„大巴叿œº,æ‰æ•¢è¿™æ ·çŽ©,ä¸ç„¶è¿Ÿæ—©ç ´äº§ã€‚è¦è§£å†³è¿™ä¸ªé—®é¢˜ä¹Ÿä¸éš¾,大巴叿œºç‰ä¹˜å®¢æ•°é‡è¶…过了 25 个,æ‰è®¤å®šå¯ä»¥å‘车。
现举个糊涂窗å£ç»¼åˆç—‡çš„æ —å,考虑以下场景:
接收方的窗å£å¤§å°æ˜¯ 360 å—节,但接收方由于æŸäº›åŽŸå› é™·å…¥å›°å¢ƒ,å‡è®¾æŽ¥æ”¶æ–¹çš„应用层读å–的能力如下:
- æŽ¥æ”¶æ–¹æ¯æŽ¥æ”¶ 3 个å—节,应用程åºå°±åªèƒ½ä»Žç¼“冲区ä¸è¯»å– 1 个å—节的数æ®;
- 在下一个å‘逿–¹çš„ TCP 段到达之å‰,应用程åºè¿˜ä»Žç¼“冲区ä¸è¯»å–了 40 个é¢å¤–çš„å—节;
æ¯ä¸ªè¿‡ç¨‹çš„窗å£å¤§å°çš„å˜åŒ–,在图ä¸éƒ½æè¿°çš„很清楚了,å¯ä»¥å‘现窗å£ä¸æ–å‡å°‘了,并且å‘é€çš„æ•°æ®éƒ½æ˜¯æ¯”较å°çš„了。
所以,糊涂窗å£ç»¼åˆç—‡çš„现象是å¯ä»¥å‘生在å‘逿–¹å’ŒæŽ¥æ”¶æ–¹:
- 接收方å¯ä»¥é€šå‘Šä¸€ä¸ªå°çš„窗å£
- 而å‘逿–¹å¯ä»¥å‘é€å°æ•°æ®
于是,è¦è§£å†³ç³Šæ¶‚窗å£ç»¼åˆç—‡,就解决上é¢ä¸¤ä¸ªé—®é¢˜å°±å¯ä»¥äº†
- 让接收方ä¸é€šå‘Šå°çª—å£ç»™å‘逿–¹
- 让å‘逿–¹é¿å…å‘é€å°æ•°æ®
怎么让接收方ä¸é€šå‘Šå°çª—å£å‘¢?
接收方通常的ç–略如下:
当「窗å£å¤§å°ã€å°äºŽ min( MSS,缓å˜ç©ºé—´/2 ) ,也就是å°äºŽ MSS 与 1/2 缓å˜å¤§å°ä¸çš„æœ€å°å€¼æ—¶,就会å‘å‘逿–¹é€šå‘Šçª—å£ä¸º 0,也就阻æ¢äº†å‘逿–¹å†å‘æ•°æ®è¿‡æ¥ã€‚
ç‰åˆ°æŽ¥æ”¶æ–¹å¤„ç†äº†ä¸€äº›æ•°æ®åŽ,窗å£å¤§å° >= MSS,或者接收方缓å˜ç©ºé—´æœ‰ä¸€åŠå¯ä»¥ä½¿ç”¨,å°±å¯ä»¥æŠŠçª—壿‰“开让å‘逿–¹å‘逿•°æ®è¿‡æ¥ã€‚
怎么让å‘逿–¹é¿å…å‘é€å°æ•°æ®å‘¢?
å‘逿–¹é€šå¸¸çš„ç–ç•¥:
使用 Nagle 算法,该算法的æ€è·¯æ˜¯å»¶æ—¶å¤„ç†,它满足以下两个æ¡ä»¶ä¸çš„ä¸€æ¡æ‰å¯ä»¥å‘逿•°æ®:
- è¦ç‰åˆ°çª—å£å¤§å° >=
MSS或是 æ•°æ®å¤§å° >=MSS - 收到之å‰å‘逿•°æ®çš„
ack回包
åªè¦æ²¡æ»¡è¶³ä¸Šé¢æ¡ä»¶ä¸çš„一æ¡,å‘逿–¹ä¸€ç›´åœ¨å›¤ç§¯æ•°æ®,直到满足上é¢çš„å‘逿¡ä»¶ã€‚
å¦å¤–,Nagle 算法默认是打开的,如果对于一些需è¦å°æ•°æ®åŒ…交互的场景的程åº,比如,telnet 或 ssh è¿™æ ·çš„äº¤äº’æ€§æ¯”è¾ƒå¼ºçš„ç¨‹åº,则需è¦å…³é— Nagle 算法。
å¯ä»¥åœ¨ Socket 设置 TCP_NODELAY 选项æ¥å…³é—这个算法(å…³é— Nagle ç®—æ³•æ²¡æœ‰å…¨å±€å‚æ•°,éœ€è¦æ ¹æ®æ¯ä¸ªåº”用自己的特点æ¥å…³é—)
setsockopt(sock_fd, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int));
08 拥塞控制
ä¸ºä»€ä¹ˆè¦æœ‰æ‹¥å¡žæŽ§åˆ¶å‘€,䏿˜¯æœ‰æµé‡æŽ§åˆ¶äº†å—?
å‰é¢çš„æµé‡æŽ§åˆ¶æ˜¯é¿å…「å‘逿–¹ã€çš„æ•°æ®å¡«æ»¡ã€ŒæŽ¥æ”¶æ–¹ã€çš„缓å˜,但是并ä¸çŸ¥é“网络的ä¸å‘生了什么。
一般æ¥è¯´,è®¡ç®—æœºç½‘ç»œéƒ½å¤„åœ¨ä¸€ä¸ªå…±äº«çš„çŽ¯å¢ƒã€‚å› æ¤ä¹Ÿæœ‰å¯èƒ½ä¼šå› ä¸ºå…¶ä»–ä¸»æœºä¹‹é—´çš„é€šä¿¡ä½¿å¾—ç½‘ç»œæ‹¥å µã€‚
åœ¨ç½‘ç»œå‡ºçŽ°æ‹¥å µæ—¶,如果继ç»å‘é€å¤§é‡æ•°æ®åŒ…,å¯èƒ½ä¼šå¯¼è‡´æ•°æ®åŒ…æ—¶å»¶ã€ä¸¢å¤±ç‰,这时 TCP 就会é‡ä¼ æ•°æ®,但是一é‡ä¼ 就会导致网络的负担更é‡,äºŽæ˜¯ä¼šå¯¼è‡´æ›´å¤§çš„å»¶è¿Ÿä»¥åŠæ›´å¤šçš„丢包,è¿™ä¸ªæƒ…å†µå°±ä¼šè¿›å…¥æ¶æ€§å¾ªçŽ¯è¢«ä¸æ–地放大…
所以,TCP ä¸èƒ½å¿½ç•¥ç½‘络上å‘生的事,它被设计æˆä¸€ä¸ªæ— ç§çš„åè®®,当网络å‘逿‹¥å¡žæ—¶,TCP 会自我牺牲,é™ä½Žå‘é€çš„æ•°æ®é‡ã€‚
于是,就有了拥塞控制,控制的目的就是é¿å…「å‘逿–¹ã€çš„æ•°æ®å¡«æ»¡æ•´ä¸ªç½‘络。
为了在「å‘逿–¹ã€è°ƒèŠ‚æ‰€è¦å‘逿•°æ®çš„é‡,定义了一个å«åšã€Œæ‹¥å¡žçª—å£ã€çš„æ¦‚念。
什么是拥塞窗å£?å’Œå‘é€çª—壿œ‰ä»€ä¹ˆå…³ç³»å‘¢?
æ‹¥å¡žçª—å£ cwnd是å‘逿–¹ç»´æŠ¤çš„一个的状æ€å˜é‡,å®ƒä¼šæ ¹æ®ç½‘络的拥塞程度动æ€å˜åŒ–的。
我们在å‰é¢æåˆ°è¿‡å‘é€çª—å£ swnd å’ŒæŽ¥æ”¶çª—å£ rwnd 是约ç‰äºŽçš„关系,é‚£ä¹ˆç”±äºŽåŠ å…¥äº†æ‹¥å¡žçª—å£çš„æ¦‚念åŽ,æ¤æ—¶å‘é€çª—å£çš„值是swnd = min(cwnd, rwnd),也就是拥塞窗å£å’ŒæŽ¥æ”¶çª—å£ä¸çš„æœ€å°å€¼ã€‚
æ‹¥å¡žçª—å£ cwnd å˜åŒ–的规则:
- åªè¦ç½‘ç»œä¸æ²¡æœ‰å‡ºçŽ°æ‹¥å¡ž,
cwnd就会增大; - 但网络ä¸å‡ºçŽ°äº†æ‹¥å¡ž,
cwndå°±å‡å°‘;
那么怎么知é“当å‰ç½‘络是å¦å‡ºçŽ°äº†æ‹¥å¡žå‘¢?
其实åªè¦ã€Œå‘逿–¹ã€æ²¡æœ‰åœ¨è§„定时间内接收到 ACK åº”ç”æŠ¥æ–‡,也就是å‘生了超时é‡ä¼ ,就会认为网络出现了用拥塞。
拥塞控制有哪些控制算法?
æ‹¥å¡žæŽ§åˆ¶ä¸»è¦æ˜¯å››ä¸ªç®—法:
- æ…¢å¯åЍ
- 拥塞é¿å…
- 拥塞å‘生
- 快速æ¢å¤
8.1 æ…¢å¯åЍ
TCP 在刚建立连接完æˆåŽ,首先是有个慢å¯åŠ¨çš„è¿‡ç¨‹,这个慢å¯åŠ¨çš„æ„æ€å°±æ˜¯ä¸€ç‚¹ä¸€ç‚¹çš„æé«˜å‘逿•°æ®åŒ…的数é‡,如果一上æ¥å°±å‘大é‡çš„æ•°æ®,è¿™ä¸æ˜¯ç»™ç½‘ç»œæ·»å µå—?
æ…¢å¯åŠ¨çš„ç®—æ³•è®°ä½ä¸€ä¸ªè§„则就行:当å‘逿–¹æ¯æ”¶åˆ°ä¸€ä¸ª ACK,æ‹¥å¡žçª—å£ cwnd 的大å°å°±ä¼šåŠ 1。
这里å‡å®šæ‹¥å¡žçª—å£ cwnd å’Œå‘é€çª—å£ swnd 相ç‰,下é¢ä¸¾ä¸ªæ —å:
- 连接建立完æˆåŽ,一开始åˆå§‹åŒ–
cwnd = 1,表示å¯ä»¥ä¼ 一个MSS大å°çš„æ•°æ®ã€‚ - 当收到一个 ACK 确认应ç”åŽ,cwnd å¢žåŠ 1,于是一次能够å‘é€ 2 个
- 当收到 2 个的 ACK 确认应ç”åŽ, cwnd å¢žåŠ 2,于是就å¯ä»¥æ¯”之å‰å¤šå‘2 个,所以这一次能够å‘é€ 4 个
- 当这 4 个的 ACK 确认到æ¥çš„æ—¶å€™,æ¯ä¸ªç¡®è®¤ cwnd å¢žåŠ 1, 4 个确认 cwnd å¢žåŠ 4,于是就å¯ä»¥æ¯”之å‰å¤šå‘ 4 个,所以这一次能够å‘é€ 8 个。
å¯ä»¥çœ‹å‡ºæ…¢å¯åŠ¨ç®—æ³•,å‘包的个数是指数性的增长。
那慢å¯åŠ¨æ¶¨åˆ°ä»€ä¹ˆæ—¶å€™æ˜¯ä¸ªå¤´å‘¢?
æœ‰ä¸€ä¸ªå«æ…¢å¯åŠ¨é—¨é™ ssthresh (slow start threshold)状æ€å˜é‡ã€‚
- 当
cwnd<ssthreshæ—¶,使用慢å¯åŠ¨ç®—æ³•ã€‚ - 当
cwnd>=ssthreshæ—¶,就会使用「拥塞é¿å…算法ã€ã€‚
8.2 拥塞é¿å…算法
å‰é¢è¯´é“,å½“æ‹¥å¡žçª—å£ cwnd ã€Œè¶…è¿‡ã€æ…¢å¯åŠ¨é—¨é™ ssthresh 就会进入拥塞é¿å…算法。
一般æ¥è¯´ ssthresh çš„å¤§å°æ˜¯ 65535 å—节。
那么进入拥塞é¿å…算法åŽ,它的规则是:æ¯å½“收到一个 ACK æ—¶,cwnd å¢žåŠ 1/cwnd。
接上å‰é¢çš„æ…¢å¯åŠ¨çš„æ —å,现å‡å®š ssthresh 为 8:
- 当 8 个 ACK 应ç”ç¡®è®¤åˆ°æ¥æ—¶,æ¯ä¸ªç¡®è®¤å¢žåŠ 1/8,8 个 ACK 确认 cwnd ä¸€å…±å¢žåŠ 1,于是这一次能够å‘é€ 9 个
MSS大å°çš„æ•°æ®,å˜æˆäº†çº¿æ€§å¢žé•¿ã€‚
所以,我们å¯ä»¥å‘现,拥塞é¿å…算法就是将原本慢å¯åŠ¨ç®—æ³•çš„æŒ‡æ•°å¢žé•¿å˜æˆäº†çº¿æ€§å¢žé•¿,还是增长阶段,但是增长速度缓慢了一些。
就这么一直增长ç€åŽ,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需è¦å¯¹ä¸¢å¤±çš„æ•°æ®åŒ…进行é‡ä¼ 。
当触å‘了é‡ä¼ 机制,也就进入了「拥塞å‘生算法ã€ã€‚
8.3 拥塞å‘生
当网络出现拥塞,也就是会å‘生数æ®åŒ…é‡ä¼ ,é‡ä¼ æœºåˆ¶ä¸»è¦æœ‰ä¸¤ç§:
- è¶…æ—¶é‡ä¼
- 快速é‡ä¼
这两ç§ä½¿ç”¨çš„æ‹¥å¡žå‘é€ç®—法是ä¸åŒçš„,接下æ¥åˆ†åˆ«æ¥è¯´è¯´ã€‚
å‘生超时é‡ä¼ 的拥塞å‘生算法
当å‘生了「超时é‡ä¼ ã€,则就会使用拥塞å‘生算法。
这个时候,ssthresh å’Œ cwnd 的值会å‘生å˜åŒ–:
ssthresh设为cwnd/2,cwndé‡ç½®ä¸º1
接ç€,就釿–°å¼€å§‹æ…¢å¯åЍ,æ…¢å¯åŠ¨æ˜¯ä¼šçªç„¶å‡å°‘æ•°æ®æµçš„。这真是一旦「超时é‡ä¼ ã€,马上回到解放å‰ã€‚ä½†æ˜¯è¿™ç§æ–¹å¼å¤ªæ¿€è¿›äº†,å应也很强烈,ä¼šé€ æˆç½‘络å¡é¡¿ã€‚
å°±å¥½åƒæœ¬æ¥åœ¨ç§‹å山高速漂移ç€,çªç„¶æ¥ä¸ªç´§æ€¥åˆ¹è½¦,轮胎å—得了å—。。。
å‘生快速é‡ä¼ 的拥塞å‘生算法
还有更好的方å¼,å‰é¢æˆ‘们讲过「快速é‡ä¼ 算法ã€ã€‚当接收方å‘现丢了一个ä¸é—´åŒ…的时候,å‘é€ä¸‰æ¬¡å‰ä¸€ä¸ªåŒ…çš„ ACK,于是å‘é€ç«¯å°±ä¼šå¿«é€Ÿåœ°é‡ä¼ ,ä¸å¿…ç‰å¾…è¶…æ—¶å†é‡ä¼ 。
TCP è®¤ä¸ºè¿™ç§æƒ…况ä¸ä¸¥é‡,å› ä¸ºå¤§éƒ¨åˆ†æ²¡ä¸¢,åªä¸¢äº†ä¸€å°éƒ¨åˆ†,则 ssthresh å’Œ cwnd å˜åŒ–如下:
cwnd = cwnd/2,也就是设置为原æ¥çš„一åŠ;ssthresh = cwnd;- 进入快速æ¢å¤ç®—法
8.4 快速æ¢å¤
快速é‡ä¼ 和快速æ¢å¤ç®—æ³•ä¸€èˆ¬åŒæ—¶ä½¿ç”¨,快速æ¢å¤ç®—法是认为,ä½ è¿˜èƒ½æ”¶åˆ° 3 个é‡å¤ ACK 说明网络也ä¸é‚£ä¹ˆç³Ÿç³•,所以没有必è¦åƒ RTO 超时那么强烈。
æ£å¦‚å‰é¢æ‰€è¯´,进入快速æ¢å¤ä¹‹å‰,cwnd å’Œ ssthresh 已被更新了:
cwnd = cwnd/2,也就是设置为原æ¥çš„一åŠ;ssthresh = cwnd;
ç„¶åŽ,进入快速æ¢å¤ç®—法如下:
- 拥塞窗å£
cwnd = ssthresh + 3( 3 çš„æ„æ€æ˜¯ç¡®è®¤æœ‰ 3 个数æ®åŒ…被收到了); - é‡ä¼ 丢失的数æ®åŒ…;
- å¦‚æžœå†æ”¶åˆ°é‡å¤çš„ ACK,那么 cwnd å¢žåŠ 1;
- 如果收到新数æ®çš„ ACK åŽ,把 cwnd 设置为第一æ¥ä¸çš„ ssthresh 的值,åŽŸå› æ˜¯è¯¥ ACK 确认了新的数æ®,说明从 duplicated ACK 时的数æ®éƒ½å·²æ”¶åˆ°,该æ¢å¤è¿‡ç¨‹å·²ç»ç»“æŸ,å¯ä»¥å›žåˆ°æ¢å¤ä¹‹å‰çš„状æ€äº†,也å³å†æ¬¡è¿›å…¥æ‹¥å¡žé¿å…状æ€;
也就是没有åƒã€Œè¶…æ—¶é‡ä¼ ã€ä¸€å¤œå›žåˆ°è§£æ”¾å‰,而是还在比较高的值,åŽç»å‘ˆçº¿æ€§å¢žé•¿ã€‚
8.5 拥塞算法示æ„图
好了,以上就是拥塞控制的全部内容了,看完åŽ,ä½ å†æ¥çœ‹ä¸‹é¢è¿™å¼ 图片,æ¯ä¸ªè¿‡ç¨‹æˆ‘ç›¸ä¿¡ä½ éƒ½èƒ½æ˜Žç™½:
结尾
ç å—䏿˜“,ç”»å›¾æ›´ä¸æ˜“,å¤§å®¶ç»™å°æž—个一键三连å§,这对我éžå¸¸é‡è¦,感谢大家!
作者简介
作者简介:大家好,æˆ‘æ˜¯å°æž—,一个专为大家图解的工具人,微信æœç´¢ã€Œå°æž—codingã€,关注专注于图解计算机基础的我,å†™äº†å¾ˆå¤šåŽŸåˆ›çš„å›¾è§£ç³»åˆ—æ–‡ç« ,如图解网络ã€å›¾è§£ç³»ç»Ÿã€å›¾è§£æ•°æ®åº“ç‰ç‰,这么å–力的图解,当然是希望大家能å¦èµ·æ¥ä¸ä¼šæž¯ç‡¥,å°æž—æœŸå¾…ä½ çš„å…³æ³¨,å’Œæˆ‘ä¸€èµ·çƒæ°”腾腾的æˆé•¿ã€‚


å‘布评论