關(guān)注我的微信公眾號:后端技術(shù)漫談
不定期推送關(guān)于后端開發(fā)、爬蟲、算法題、數(shù)據(jù)結(jié)構(gòu)方面的原創(chuàng)技術(shù)文章,以及生活中的逸聞趣事。
我目前是一名后端開發(fā)工程師。主要關(guān)注后端開發(fā),數(shù)據(jù)安全,網(wǎng)絡(luò)爬蟲,物聯(lián)網(wǎng),邊緣計(jì)算等方向。
原創(chuàng)博客主要內(nèi)容
Java知識點(diǎn)復(fù)習(xí)全手冊Leetcode算法題解析劍指offer算法題解析SpringCloud菜鳥入門實(shí)戰(zhàn)系列SpringBoot菜鳥入門實(shí)戰(zhàn)系列Python爬蟲相關(guān)技術(shù)文章后端開發(fā)相關(guān)技術(shù)文章
TIM截圖20190218204943.png
前言本文快速回顧了計(jì)算機(jī)網(wǎng)絡(luò)書本中??嫉牡闹R點(diǎn),用作面試復(fù)習(xí),事半功倍。
主要內(nèi)容有:計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu),TCP與UDP,UDP/TCP實(shí)現(xiàn)DEMO代碼
-----正文開始-----基礎(chǔ)計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)

在這里插入圖片描述
1. 五層協(xié)議
應(yīng)用層 :為特定應(yīng)用程序提供數(shù)據(jù)傳輸服務(wù),例如 HTTP、DNS 等。數(shù)據(jù)單位為報(bào)文。運(yùn)輸層 :提供的是進(jìn)程間的通用數(shù)據(jù)傳輸服務(wù)。由于應(yīng)用層協(xié)議很多,定義通用的運(yùn)輸層協(xié)議就可以支持不斷增多的應(yīng)用層協(xié)議。運(yùn)輸層包括兩種協(xié)議: 傳輸控制協(xié)議TCP,提供面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)單位為報(bào)文段;TCP 主要提供完整性服務(wù). 用戶數(shù)據(jù)報(bào)協(xié)議UDP,提供無連接、盡最大努力的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)單位為用戶數(shù)據(jù)報(bào)。UDP 主要提供及時(shí)性服務(wù)。網(wǎng)絡(luò)層:為主機(jī)間提供數(shù)據(jù)傳輸服務(wù),而運(yùn)輸層協(xié)議是為主機(jī)中的進(jìn)程提供服務(wù)。網(wǎng)絡(luò)層把運(yùn)輸層傳遞下來的報(bào)文段或者用戶數(shù)據(jù)報(bào)封裝成分組。數(shù)據(jù)鏈路層:網(wǎng)絡(luò)層針對的還是主機(jī)之間的數(shù)據(jù)傳輸服務(wù),而主機(jī)之間可以有很多鏈路,鏈路層協(xié)議就是為同一鏈路的節(jié)點(diǎn)提供服務(wù)。數(shù)據(jù)鏈路層把網(wǎng)絡(luò)層傳來的分組封裝成幀。物理層 :考慮的是怎樣在傳輸媒體上傳輸數(shù)據(jù)比特流,而不是指具體的傳輸媒體。物理層的作用是盡可能屏蔽傳輸媒體和通信手段的差異,使數(shù)據(jù)鏈路層感覺不到這些差異。數(shù)據(jù)報(bào)->分組->幀->比特流
2. 七層協(xié)議
其中表示層和會(huì)話層用途如下:
表示層 :數(shù)據(jù)壓縮、加密以及數(shù)據(jù)描述。這使得應(yīng)用程序不必?fù)?dān)心在各臺(tái)主機(jī)中表示/存儲(chǔ)的內(nèi)部格式不同的問題。會(huì)話層 :建立及管理會(huì)話。五層協(xié)議沒有表示層和會(huì)話層,而是將這些功能留給應(yīng)用程序開發(fā)者處理。
3. 數(shù)據(jù)在各層之間的傳遞過程
在向下的過程中,需要添加下層協(xié)議所需要的首部或者尾部,而在向上的過程中不斷拆開首部和尾部。
路由器只有下面三層協(xié)議,因?yàn)槁酚善魑挥诰W(wǎng)絡(luò)核心中,不需要為進(jìn)程或者應(yīng)用程序提供服務(wù),因此也就不需要運(yùn)輸層和應(yīng)用層。
4. TCP/IP
它只有四層,相當(dāng)于五層協(xié)議中數(shù)據(jù)鏈路層和物理層合并為網(wǎng)絡(luò)接口層。
現(xiàn)在的 TCP/IP 體系結(jié)構(gòu)不嚴(yán)格遵循 OSI 分層概念,應(yīng)用層可能會(huì)直接使用 IP 層或者網(wǎng)絡(luò)接口層
TCP/IP 協(xié)議族是一種沙漏形狀,中間小兩邊大,IP 協(xié)議在其中占用舉足輕重的地位。

在這里插入圖片描述
物理層/數(shù)據(jù)鏈路層/網(wǎng)絡(luò)層知識點(diǎn)偏通信理論的多一些,可以放在后面復(fù)習(xí)
傳輸層TCP與UDP的特點(diǎn)
用戶數(shù)據(jù)報(bào)協(xié)議 UDP(User Datagram Protocol):無連接的,盡最大可能交付,沒有擁塞控制,面向報(bào)文
對于應(yīng)用程序傳下來的報(bào)文不合并也不拆分,只是添加 UDP 首部,支持一對一、一對多、多對一和多對多的交互通信。
傳輸控制協(xié)議 TCP(Transmission Control Protocol)是有連接的,提供可靠交付,有流量控制,擁塞控制,面向字節(jié)流
把應(yīng)用層傳下來的報(bào)文看成字節(jié)流,把字節(jié)流組織成大小不等的數(shù)據(jù)塊,每一條 TCP連接只能是點(diǎn)對點(diǎn)的(一對一)。
總結(jié)(TCP和UDP的區(qū)別):
1)TCP提供面向連接的傳輸;UDP提供無連接的傳輸
2)TCP提供可靠的傳輸(有序,無差錯(cuò),不丟失,不重復(fù));UDP提供不可靠的傳輸。
3)TCP面向字節(jié)流的傳輸,因此它能將信息分割成組,并在接收端將其重組;UDP是面向數(shù)據(jù)報(bào)的傳輸,沒有分組開銷。
4)TCP提供擁塞控制和流量控制機(jī)制;UDP不提供擁塞控制和流量控制機(jī)制。
5)TCP只能是點(diǎn)對點(diǎn)的(一對一)。UDP支持一對一、一對多、多對一和多對多的交互通信。
首部格式
UDP

在這里插入圖片描述
首部字段只有 8 個(gè)字節(jié),包括源端口、目的端口、長度、檢驗(yàn)和。12 字節(jié)的偽首部是為了計(jì)算檢驗(yàn)和臨時(shí)添加的。
TCP

在這里插入圖片描述
序號 :用于對字節(jié)流進(jìn)行編號,例如序號為 301,表示第一個(gè)字節(jié)的編號為 301,如果攜帶的數(shù)據(jù)長度為 100 字節(jié),那么下一個(gè)報(bào)文段的序號應(yīng)為 401。確認(rèn)號 :期望收到的下一個(gè)報(bào)文段的序號。例如 B 正確收到 A 發(fā)送來的一個(gè)報(bào)文段,序號為 501,攜帶的數(shù)據(jù)長度為 200 字節(jié),因此 B 期望下一個(gè)報(bào)文段的序號為 701,B 發(fā)送給 A 的確認(rèn)報(bào)文段中確認(rèn)號就為 701。數(shù)據(jù)偏移 :指的是數(shù)據(jù)部分距離報(bào)文段起始處的偏移量,實(shí)際上指的是首部的長度。確認(rèn) ACK :當(dāng) ACK=1 時(shí)確認(rèn)號字段有效,否則無效。TCP 規(guī)定,在連接建立后所有傳送的報(bào)文段都必須把 ACK 置 1。同步 SYN :在連接建立時(shí)用來同步序號。當(dāng) SYN=1,ACK=0 時(shí)表示這是一個(gè)連接請求報(bào)文段。若對方同意建立連接,則響應(yīng)報(bào)文中 SYN=1,ACK=1。終止 FIN :用來釋放一個(gè)連接,當(dāng) FIN=1 時(shí),表示此報(bào)文段的發(fā)送方的數(shù)據(jù)已發(fā)送完畢,并要求釋放連接。窗口 :窗口值作為接收方讓發(fā)送方設(shè)置其發(fā)送窗口的依據(jù)。之所以要有這個(gè)限制,是因?yàn)榻邮辗降臄?shù)據(jù)緩存空間是有限的。TCP拆包粘包
如果客戶端連續(xù)不斷的向服務(wù)端發(fā)送數(shù)據(jù)包時(shí),服務(wù)端接收的數(shù)據(jù)會(huì)出現(xiàn)兩個(gè)數(shù)據(jù)包粘在一起的情況。
分包機(jī)制一般有兩個(gè)通用的解決方法:
1,特殊字符控制
2,在包頭首都添加數(shù)據(jù)包的長度
如果使用netty的話,就有專門的編碼器和解碼器解決拆包和粘包問題了。
tips:
UDP沒有粘包問題,但是有丟包和亂序。不完整的包是不會(huì)有的,收到的都是完全正確的包。傳送的數(shù)據(jù)單位協(xié)議是UDP報(bào)文或用戶數(shù)據(jù)報(bào),發(fā)送的時(shí)候既不合并,也不拆分。
三次握手四次揮手:
https://blog.csdn.net/qzcsu/article/details/72861891
三次握手
(1)第一步:源主機(jī)A的TCP向主機(jī)B發(fā)出連接請求報(bào)文段,其首部中的SYN(同步)標(biāo)志位應(yīng)置為1,表示想與目標(biāo)主機(jī)B進(jìn)行通信,**并發(fā)送一個(gè)同步序列號X(例:SEQ=100)進(jìn)行同步,表明在后面?zhèn)魉蛿?shù)據(jù)時(shí)的第一個(gè)數(shù)據(jù)字節(jié)的序號是X+1(即101)**。SYN同步報(bào)文會(huì)指明客戶端使用的端口以及TCP連接的初始序號。 (2)第二步:目標(biāo)主機(jī)B的TCP收到連接請求報(bào)文段后,如同意,則發(fā)回確認(rèn)。在確認(rèn)報(bào)中應(yīng)將ACK位和SYN位置1,表示客戶端的請求被接受。確認(rèn)號應(yīng)為X+1(圖中為101),同時(shí)也為自己選擇一個(gè)序號Y。 (3)第三步:源主機(jī)A的TCP收到目標(biāo)主機(jī)B的確認(rèn)后要向目標(biāo)主機(jī)B給出確認(rèn),其ACK置1,確認(rèn)號為Y+1,而自己的序號為X+1。**TCP的標(biāo)準(zhǔn)規(guī)定,SYN置1的報(bào)文段要消耗掉一個(gè)序號。** 運(yùn)行客戶進(jìn)程的源主機(jī)A的TCP通知上層應(yīng)用進(jìn)程,連接已經(jīng)建立。當(dāng)源主機(jī)A向目標(biāo)主機(jī)B發(fā)送第一個(gè)數(shù)據(jù)報(bào)文段時(shí),**其序號仍為X+1,因?yàn)榍耙粋€(gè)確認(rèn)報(bào)文段并不消耗序號。** 當(dāng)運(yùn)行服務(wù)進(jìn)程的目標(biāo)主機(jī)B的TCP收到源主機(jī)A的確認(rèn)后,也通知其上層應(yīng)用進(jìn)程,連接已經(jīng)建立。至此建立了一個(gè)全雙工的連接。
在這里插入圖片描述
三次握手的原因
如果采用的是三次握手,就算是那一次失效的報(bào)文傳送過來了,服務(wù)端接受到了那條失效報(bào)文并且回復(fù)了確認(rèn)報(bào)文,但是客戶端不會(huì)再次發(fā)出確認(rèn)。由于服務(wù)器收不到確認(rèn),就知道客戶端并沒有請求連接。
如果使用的是兩次握手建立連接,假設(shè)有這樣一種場景,客戶端發(fā)送了第一個(gè)請求連接并且沒有丟失,只是因?yàn)樵诰W(wǎng)絡(luò)結(jié)點(diǎn)中滯留的時(shí)間太長了,由于TCP的客戶端遲遲沒有收到確認(rèn)報(bào)文,以為服務(wù)器沒有收到,此時(shí)重新向服務(wù)器發(fā)送這條報(bào)文,此后客戶端和服務(wù)器經(jīng)過兩次握手完成連接,傳輸數(shù)據(jù),然后關(guān)閉連接。此時(shí)此前滯留的那一次請求連接,網(wǎng)絡(luò)通暢了到達(dá)了服務(wù)器,這個(gè)報(bào)文本該是失效的,但是,兩次握手的機(jī)制將會(huì)讓客戶端和服務(wù)器再次建立連接,這將導(dǎo)致不必要的錯(cuò)誤和資源的浪費(fèi)。
四次揮手
客戶端進(jìn)程發(fā)出連接釋放報(bào)文,并且停止發(fā)送數(shù)據(jù)。釋放數(shù)據(jù)報(bào)文首部,F(xiàn)IN=1,其序列號為seq=u(等于前面已經(jīng)傳送過來的數(shù)據(jù)的最后一個(gè)字節(jié)的序號加1),此時(shí),客戶端進(jìn)入FIN-WAIT-1(終止等待1)狀態(tài)。 TCP規(guī)定,F(xiàn)IN報(bào)文段即使不攜帶數(shù)據(jù),也要消耗一個(gè)序號。服務(wù)器收到連接釋放報(bào)文,發(fā)出確認(rèn)報(bào)文,ACK=1,ack=u+1,并且?guī)献约旱男蛄刑杝eq=v,此時(shí),服務(wù)端就進(jìn)入了CLOSE-WAIT(關(guān)閉等待)狀態(tài)。TCP服務(wù)器通知高層的應(yīng)用進(jìn)程,客戶端向服務(wù)器的方向就釋放了,這時(shí)候處于半關(guān)閉狀態(tài),即客戶端已經(jīng)沒有數(shù)據(jù)要發(fā)送了,但是服務(wù)器若發(fā)送數(shù)據(jù),客戶端依然要接受。這個(gè)狀態(tài)還要持續(xù)一段時(shí)間,也就是整個(gè)CLOSE-WAIT狀態(tài)持續(xù)的時(shí)間??蛻舳耸盏椒?wù)器的確認(rèn)請求后,此時(shí),客戶端就進(jìn)入FIN-WAIT-2(終止等待2)狀態(tài),等待服務(wù)器發(fā)送連接釋放報(bào)文(在這之前還需要接受服務(wù)器發(fā)送的最后的數(shù)據(jù))。服務(wù)器將最后的數(shù)據(jù)發(fā)送完畢后,就向客戶端發(fā)送連接釋放報(bào)文,F(xiàn)IN=1,ack=u+1,由于在半關(guān)閉狀態(tài),服務(wù)器很可能又發(fā)送了一些數(shù)據(jù),假定此時(shí)的序列號為seq=w,此時(shí),服務(wù)器就進(jìn)入了LAST-ACK(最后確認(rèn))狀態(tài),等待客戶端的確認(rèn)。客戶端收到服務(wù)器的連接釋放報(bào)文后,必須發(fā)出確認(rèn),ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時(shí),客戶端就進(jìn)入了TIME-WAIT(時(shí)間等待)狀態(tài)。注意此時(shí)TCP連接還沒有釋放,必須經(jīng)過2?MSL(最長報(bào)文段壽命)的時(shí)間后,當(dāng)客戶端撤銷相應(yīng)的TCB后,才進(jìn)入CLOSED狀態(tài)。服務(wù)器只要收到了客戶端發(fā)出的確認(rèn),立即進(jìn)入CLOSED狀態(tài)。同樣,撤銷TCB后,就結(jié)束了這次的TCP連接??梢钥吹剑?wù)器結(jié)束TCP連接的時(shí)間要比客戶端早一些。
在這里插入圖片描述
等待 2MSL(最長報(bào)文段壽命) 的原因
書中解釋:

在這里插入圖片描述
TCP采用四次揮手關(guān)閉連接如圖所示為什么建立連接協(xié)議是三次握手,而關(guān)閉連接卻是四次握手呢?
建立連接的時(shí)候, 服務(wù)器在LISTEN狀態(tài)下,收到建立連接請求的SYN報(bào)文后,把ACK和SYN放在一個(gè)報(bào)文里發(fā)送給客戶端。
而關(guān)閉連接時(shí),服務(wù)器收到對方的FIN報(bào)文時(shí),僅僅表示對方不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù),而自己也未必全部數(shù)據(jù)都發(fā)送給對方了,所以己方可以立即關(guān)閉,也可以發(fā)送一些數(shù)據(jù)給對方后,再發(fā)送FIN報(bào)文給對方來表示同意現(xiàn)在關(guān)閉連接,因此,己方ACK和FIN一般都會(huì)分開發(fā)送,從而導(dǎo)致多了一次。
如果已經(jīng)建立了連接,但是客戶端突然出現(xiàn)故障了怎么辦?
TCP還設(shè)有一個(gè)?;钣?jì)時(shí)器,顯然,客戶端如果出現(xiàn)故障,服務(wù)器不能一直等下去,白白浪費(fèi)資源。服務(wù)器每收到一次客戶端的請求后都會(huì)重新復(fù)位這個(gè)計(jì)時(shí)器,時(shí)間通常是設(shè)置為2小時(shí),若兩小時(shí)還沒有收到客戶端的任何數(shù)據(jù),服務(wù)器就會(huì)發(fā)送一個(gè)探測報(bào)文段,以后每隔75分鐘發(fā)送一次。若一連發(fā)送10個(gè)探測報(bào)文仍然沒反應(yīng),服務(wù)器就認(rèn)為客戶端出了故障,接著就關(guān)閉連接。
Time-wait狀態(tài)好處和壞處
好處
上方所述兩點(diǎn)
壞處
高并發(fā)下,端口都處在timewait很快就用完端口。
解決方法:
(阿里手冊)調(diào)小 TCP 協(xié)議的 time_ wait 超時(shí)時(shí)間。net . ipv 4. tcp _ fin _ timeout = 30tcp_tw_reuse這個(gè)參數(shù)作用是當(dāng)新的連接進(jìn)來的時(shí)候,可以復(fù)用處于TIME_WAIT的socket。默認(rèn)值是0。tcp_tw_recycle和tcp_timestamps默認(rèn)TIME_WAIT的超時(shí)時(shí)間是2倍的MSL,但是MSL一般會(huì)設(shè)置的非常長。如果tcp_timestamps是關(guān)閉的,開啟tcp_tw_recycle是沒用的。但是一般情況下tcp_timestamps是默認(rèn)開啟的,所以直接開啟就有用了。http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28541347&id=5748888
對于客戶端 作為客戶端因?yàn)橛卸丝?5535問題,TIME_OUT過多直接影響處理能力,打開tw_reuse 即可解決,不建議同時(shí)打開tw_recycle,幫助不大。對于服務(wù)端 1) 打開tw_reuse無效 2) 線上環(huán)境 tw_recycle 最好不要打開 服務(wù)器處于NAT 負(fù)載后,或者客戶端處于NAT后(這是一定的事情,基本公司家庭網(wǎng)絡(luò)都走NAT);公網(wǎng)服務(wù)打開就可能造成部分連接失敗,內(nèi)網(wǎng)的話到時(shí)可以視情況打開;像我所在公司對外服務(wù)都放在負(fù)載后面,負(fù)載會(huì)把timestamp 選項(xiàng)都給關(guān)閉,所以就算打開也不起作用。 3) 服務(wù)器TIME_WAIT 高怎么辦 不像客戶端有端口限制,處理大量TIME_WAIT Linux已經(jīng)優(yōu)化很好了,每個(gè)處于TIME_WAIT 狀態(tài)下連接內(nèi)存消耗很少,而且也能通過tcp_max_tw_buckets = 262144 配置最大上限,現(xiàn)代機(jī)器一般也不缺這點(diǎn)內(nèi)存。 4) 高并發(fā)服務(wù)器建議調(diào)小 TCP 協(xié)議的 time_wait 超時(shí)時(shí)間。240s調(diào)整至30s(阿里Java規(guī)約)什么情況下會(huì)出現(xiàn)RST包
看這篇就夠了:https://blog.csdn.net/eric0318/article/details/51113018
TCP 滑動(dòng)窗口

在這里插入圖片描述
窗口是緩存的一部分,用來暫時(shí)存放字節(jié)流。
發(fā)送方和接收方各有一個(gè)窗口,接收方通過 TCP 報(bào)文段中的窗口字段告訴發(fā)送方自己的窗口大小,發(fā)送方根據(jù)這個(gè)值和其它信息設(shè)置自己的窗口大小。
發(fā)送窗口內(nèi)的字節(jié)都允許被發(fā)送,接收窗口內(nèi)的字節(jié)都允許被接收。
如果發(fā)送窗口左部的字節(jié)已經(jīng)發(fā)送并且收到了確認(rèn),那么就將發(fā)送窗口向右滑動(dòng)一定距離,直到左部第一個(gè)字節(jié)不是已發(fā)送并且已確認(rèn)的狀態(tài);接收窗口的滑動(dòng)類似,接收窗口左部字節(jié)已經(jīng)發(fā)送確認(rèn)并交付主機(jī),就向右滑動(dòng)接收窗口。接收窗口只會(huì)對窗口內(nèi)最后一個(gè)按序到達(dá)的字節(jié)進(jìn)行確認(rèn),例如接收窗口已經(jīng)收到的字節(jié)為 {31, 34, 35},其中 {31} 按序到達(dá),而 {32, 33} 就不是,因此只對字節(jié) 31 進(jìn)行確認(rèn)。發(fā)送方得到一個(gè)字節(jié)的確認(rèn)之后,就知道這個(gè)字節(jié)之前的所有字節(jié)都已經(jīng)被接收。
TCP 可靠傳輸(超時(shí)重傳)
TCP 使用超時(shí)重傳來實(shí)現(xiàn)可靠傳輸:如果一個(gè)已經(jīng)發(fā)送的報(bào)文段在超時(shí)時(shí)間內(nèi)沒有收到確認(rèn),那么就重傳這個(gè)報(bào)文段。
一個(gè)報(bào)文段從發(fā)送再到接收到確認(rèn)所經(jīng)過的時(shí)間稱為往返時(shí)間 RTT,加權(quán)平均往返時(shí)間 RTTs 計(jì)算如下:
image
超時(shí)時(shí)間 RTO 應(yīng)該略大于 RTTs,TCP 使用的超時(shí)時(shí)間計(jì)算如下:
image
其中 RTTd 為偏差。
TCP 流量控制
流量控制是為了控制發(fā)送方發(fā)送速率,保證接收方來得及接收。
接收方發(fā)送的確認(rèn)報(bào)文中的窗口字段可以用來控制發(fā)送方窗口大小,從而影響發(fā)送方的發(fā)送速率。將窗口字段設(shè)置為 0,則發(fā)送方不能發(fā)送數(shù)據(jù)。
TCP 擁塞控制

在這里插入圖片描述

在這里插入圖片描述
擁塞控制主要包含以下2個(gè)內(nèi)容: (1)慢開始,擁塞避免 (2)快重傳,快恢復(fù)發(fā)送方需要維護(hù)一個(gè)叫做擁塞窗口(cwnd)的狀態(tài)變量,注意擁塞窗口與發(fā)送方窗口的區(qū)別:擁塞窗口只是一個(gè)狀態(tài)變量,實(shí)際決定發(fā)送方能發(fā)送多少數(shù)據(jù)的是發(fā)送方窗口。
為了便于討論,做如下假設(shè):
接收方有足夠大的接收緩存,因此不會(huì)發(fā)生流量控制;雖然 TCP 的窗口基于字節(jié),但是這里設(shè)窗口的大小單位為報(bào)文段。1. 慢開始與擁塞避免
發(fā)送的最初執(zhí)行慢開始,令 cwnd=1,發(fā)送方只能發(fā)送 1 個(gè)報(bào)文段;當(dāng)收到確認(rèn)后,將 cwnd 加倍,因此之后發(fā)送方能夠發(fā)送的報(bào)文段數(shù)量為:2、4、8 …
注意到慢開始每個(gè)輪次都將 cwnd 加倍,這樣會(huì)讓 cwnd 增長速度非???,從而使得發(fā)送方發(fā)送的速度增長速度過快,網(wǎng)絡(luò)擁塞的可能也就更高。設(shè)置一個(gè)慢開始門限 ssthresh,當(dāng) cwnd >= ssthresh 時(shí),進(jìn)入擁塞避免,每個(gè)輪次只將 cwnd 加 1。
如果出現(xiàn)了超時(shí),則令 ssthresh = cwnd/2,然后重新執(zhí)行慢開始。
2. 快重傳與快恢復(fù)
在接收方,要求每次接收到報(bào)文段都應(yīng)該發(fā)送對已收到有序報(bào)文段的確認(rèn),例如已經(jīng)接收到 M1 和 M2,此時(shí)收到 M4,應(yīng)當(dāng)發(fā)送對 M2 的確認(rèn)。
快重傳
在發(fā)送方,如果收到三個(gè)重復(fù)確認(rèn),那么可以確認(rèn)下一個(gè)報(bào)文段丟失,例如收到三個(gè) M2 ,則 M3 丟失。此時(shí)執(zhí)行快重傳,立即重傳下一個(gè)報(bào)文段。
快恢復(fù)
在快重傳情況下,只是丟失個(gè)別報(bào)文段,而不是網(wǎng)絡(luò)擁塞,因此執(zhí)行快恢復(fù),令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此時(shí)直接進(jìn)入擁塞避免。
慢開始和快恢復(fù)的快慢指的是 cwnd 的設(shè)定值,而不是 cwnd 的增長速率。慢開始 cwnd 設(shè)定為 1,而快恢復(fù) cwnd 設(shè)定為 ssthresh。
UDP/TCP代碼DEMO
經(jīng)典:https://blog.csdn.net/column/details/socket.html
TCP:https://blog.csdn.net/ns_code/article/details/14105457
UDP:https://blog.csdn.net/ns_code/article/details/14128987
TCP
TCP連接的建立步驟
客戶端向服務(wù)器端發(fā)送連接請求后,就被動(dòng)地等待服務(wù)器的響應(yīng)。典型的TCP客戶端要經(jīng)過下面三步操作:
1、創(chuàng)建一個(gè)Socket實(shí)例:構(gòu)造函數(shù)向指定的遠(yuǎn)程主機(jī)和端口建立一個(gè)TCP連接;2.通過套接字的I/O流與服務(wù)端通信;3、使用Socket類的close方法關(guān)閉連接。服務(wù)端的工作是建立一個(gè)通信終端,并被動(dòng)地等待客戶端的連接。典型的TCP服務(wù)端執(zhí)行如下兩步操作:
1、創(chuàng)建一個(gè)ServerSocket實(shí)例并指定本地端口,用來監(jiān)聽客戶端在該端口發(fā)送的TCP連接請求;2、重復(fù)執(zhí)行: 1)調(diào)用ServerSocket的accept()方法以獲取客戶端連接,并通過其返回值創(chuàng)建一個(gè)Socket實(shí)例; 2)為返回的Socket實(shí)例開啟新的線程,并使用返回的Socket實(shí)例的I/O流與客戶端通信; 3)通信完成后,使用Socket類的close()方法關(guān)閉該客戶端的套接字連接。Demo
ServerDemo.java
/** * sinture.com Inc. * Copyright (c) 2016-2018 All Rights Reserved. */ package test.socketDemo.TCP; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * @author yzd * @version Id: ServerDemo.java, v 0.1 2018年07月10日 12:36 yzd Exp $ */ public class ServerDemo { public static void main(String[] args) throws IOException { // 服務(wù)端在20006端口監(jiān)聽客戶端請求的TCP連接 ServerSocket server = new ServerSocket(20000); Socket client = null; boolean f = true; while(f){ // 等待客戶端的連接,如果沒有獲取連接 client = server.accept(); System.out.println("與客戶端連接成功!"); // 為每個(gè)客戶端連接開啟一個(gè)線程 new Thread(new ServerThread(client)).start(); } } }ServerThread.java
/** * sinture.com Inc. * Copyright (c) 2016-2018 All Rights Reserved. */ package test.socketDemo.TCP; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; /** * @author yzd * @version Id: ServerThread.java, v 0.1 2018年07月10日 13:41 yzd Exp $ */ public class ServerThread implements Runnable { private Socket client = null; public ServerThread(Socket client){ this.client = client; } @Override public void run() { try{ //獲取Socket的輸出流,用來向客戶端發(fā)送數(shù)據(jù) PrintStream out = new PrintStream(client.getOutputStream()); //獲取Socket的輸入流,用來接收從客戶端發(fā)送過來的數(shù)據(jù) BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream())); boolean flag = true; while (flag){ //接收從客戶端發(fā)送過來的數(shù)據(jù) String str = buf.readLine(); if(str == null || "".equals(str)){ flag = false; }else { if("bye".equals(str)){ flag = false; }else{ //將接收到的字符串前面加上echo,發(fā)送到對應(yīng)的客戶端 out.println("echo:" + str); } } } out.close(); client.close(); } catch (IOException e) { e.printStackTrace(); } } }ClientDemo.java
/** * sinture.com Inc. * Copyright (c) 2016-2018 All Rights Reserved. */ package test.socketDemo.TCP; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.net.SocketTimeoutException; /** * @author yzd * @version Id: ClientDemo.java, v 0.1 2018年07月10日 14:05 yzd Exp $ */ public class ClientDemo { public static void main(String[] args) throws IOException { //客戶端請求與本機(jī)在20006端口建立TCP連接 Socket client = new Socket("127.0.0.1", 20000); client.setSoTimeout(10000); //獲取鍵盤輸入 BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); //獲取Socket的輸出流,用來發(fā)送數(shù)據(jù)到服務(wù)端 PrintStream out = new PrintStream(client.getOutputStream()); //獲取Socket的輸入流,用來接收從服務(wù)端發(fā)送過來的數(shù)據(jù) BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream())); boolean flag = true; while(flag){ System.out.print("輸入信息:"); String str = input.readLine(); //發(fā)送數(shù)據(jù)到服務(wù)端 out.println(str); if("bye".equals(str)){ flag = false; }else{ try{ //從服務(wù)器端接收數(shù)據(jù)有個(gè)時(shí)間限制(系統(tǒng)自設(shè),也可以自己設(shè)置),超過了這個(gè)時(shí)間,便會(huì)拋出該異常 String echo = buf.readLine(); System.out.println(echo); }catch(SocketTimeoutException e){ System.out.println("Time out, No response"); } } } input.close(); if(client != null){ //如果構(gòu)造函數(shù)建立起了連接,則關(guān)閉套接字,如果沒有建立起連接,自然不用關(guān)閉 client.close(); //只關(guān)閉socket,其關(guān)聯(lián)的輸入輸出流也會(huì)被關(guān)閉 } } }UDP
UDP的通信建立的步驟
客戶端要經(jīng)過下面三步操作:
1、創(chuàng)建一個(gè)DatagramSocket實(shí)例,可以有選擇地對本地地址和端口號進(jìn)行設(shè)置,如果設(shè)置了端口號,則客戶端會(huì)在該端口號上監(jiān)聽從服務(wù)器端發(fā)送來的數(shù)據(jù);2、使用DatagramSocket實(shí)例的send()和receive()方法來發(fā)送和接收DatagramPacket實(shí)例,進(jìn)行通信;3、通信完成后,調(diào)用DatagramSocket實(shí)例的close()方法來關(guān)閉該套接字。UDP服務(wù)端要經(jīng)過下面三步操作:
1、創(chuàng)建一個(gè)DatagramSocket實(shí)例,指定本地端口號,并可以有選擇地指定本地地址,此時(shí),服務(wù)器已經(jīng)準(zhǔn)備好從任何客戶端接收數(shù)據(jù)報(bào)文;2、使用DatagramSocket實(shí)例的receive()方法接收一個(gè)DatagramPacket實(shí)例,當(dāng)receive()方法返回時(shí),數(shù)據(jù)報(bào)文就包含了客戶端的地址,這樣就知道了回復(fù)信息應(yīng)該發(fā)送到什么地方;3、使用DatagramSocket實(shí)例的send()方法向服務(wù)器端返回DatagramPacket實(shí)例。Demo
這里有一點(diǎn)需要注意:
UDP程序在receive()方法處阻塞,直到收到一個(gè)數(shù)據(jù)報(bào)文或等待超時(shí)。由于UDP協(xié)議是不可靠協(xié)議,如果數(shù)據(jù)報(bào)在傳輸過程中發(fā)生丟失,那么程序?qū)?huì)一直阻塞在receive()方法處,這樣客戶端將永遠(yuǎn)都接收不到服務(wù)器端發(fā)送回來的數(shù)據(jù),但是又沒有任何提示。為了避免這個(gè)問題,我們在客戶端使用DatagramSocket類的setSoTimeout()方法來制定receive()方法的最長阻塞時(shí)間,并指定重發(fā)數(shù)據(jù)報(bào)的次數(shù),如果每次阻塞都超時(shí),并且重發(fā)次數(shù)達(dá)到了設(shè)置的上限,則關(guān)閉客戶端。
ClientDemo.java
/** * sinture.com Inc. * Copyright (c) 2016-2018 All Rights Reserved. */ package test.socketDemo.UDP; import java.io.IOException; import java.io.InterruptedIOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * @author yzd * @version Id: ClientDemo.java, v 0.1 2018年07月10日 14:46 yzd Exp $ */ public class ClientDemo { public static final int TIMEOUT = 5000; public static final int MAXNUM = 5; public static void main(String[] args) throws IOException { String str_send = "Hello UDPServer"; byte[] buf = new byte[1024]; //客戶端在9000端口監(jiān)聽接收到的數(shù)據(jù) DatagramSocket ds = new DatagramSocket(9000); InetAddress loc = InetAddress.getLocalHost(); //定義用來發(fā)送數(shù)據(jù)的DatagramPacket實(shí)例 DatagramPacket dp_send= new DatagramPacket(str_send.getBytes(),str_send.length(),loc,3000); //定義用來接收數(shù)據(jù)的DatagramPacket實(shí)例 DatagramPacket dp_receive = new DatagramPacket(buf, 1024); //數(shù)據(jù)發(fā)向本地3000端口 ds.setSoTimeout(TIMEOUT); //設(shè)置接收數(shù)據(jù)時(shí)阻塞的最長時(shí)間 int tries = 0; //重發(fā)數(shù)據(jù)的次數(shù) boolean receivedResponse = false; //是否接收到數(shù)據(jù)的標(biāo)志位 //直到接收到數(shù)據(jù),或者重發(fā)次數(shù)達(dá)到預(yù)定值,則退出循環(huán) while(!receivedResponse && tries<MAXNUM){ //發(fā)送數(shù)據(jù) ds.send(dp_send); System.out.println("Client send message succeed."); try{ //接收從服務(wù)端發(fā)送回來的數(shù)據(jù) ds.receive(dp_receive); //如果接收到的數(shù)據(jù)不是來自目標(biāo)地址,則拋出異常 if(!dp_receive.getAddress().equals(loc)){ throw new IOException("Received packet from an unknown source"); } //如果接收到數(shù)據(jù)。則將receivedResponse標(biāo)志位改為true,從而退出循環(huán) receivedResponse = true; }catch(InterruptedIOException e){ //如果接收數(shù)據(jù)時(shí)阻塞超時(shí),重發(fā)并減少一次重發(fā)的次數(shù) tries += 1; System.out.println("Time out," + (MAXNUM - tries) + " more tries..." ); } } if(receivedResponse){ System.out.println("client received data from server:"); String str_receive = new String(dp_receive.getData(),0,dp_receive.getLength()) + " from " + dp_receive.getAddress().getHostAddress() + ":" + dp_receive.getPort(); System.out.println(str_receive); //由于dp_receive在接收了數(shù)據(jù)之后,其內(nèi)部消息長度值會(huì)變?yōu)閷?shí)際接收的消息的字節(jié)數(shù), //所以這里要將dp_receive的內(nèi)部消息長度重新置為1024 dp_receive.setLength(1024); }else{ //如果重發(fā)MAXNUM次數(shù)據(jù)后,仍未獲得服務(wù)器發(fā)送回來的數(shù)據(jù),則打印如下信息 System.out.println("No response -- give up."); } ds.close(); } }ServerDemo.java
/** * sinture.com Inc. * Copyright (c) 2016-2018 All Rights Reserved. */ package test.socketDemo.UDP; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; /** * @author yzd * @version Id: ServerDemo.java, v 0.1 2018年07月10日 15:12 yzd Exp $ */ public class ServerDemo { public static void main(String[] args) throws IOException { String str_send = "Hello UDPclient"; byte[] buf = new byte[1024]; //服務(wù)端在3000端口監(jiān)聽接收到的數(shù)據(jù) DatagramSocket ds = new DatagramSocket(3000); //接收從客戶端發(fā)送過來的數(shù)據(jù) DatagramPacket dp_receive = new DatagramPacket(buf, 1024); System.out.println("Server is on,Waiting for client to send data......"); boolean f = true; while(f){ //服務(wù)器端接收來自客戶端的數(shù)據(jù) ds.receive(dp_receive); System.out.println("Server received data from client:"); String str_receive = new String(dp_receive.getData(),0,dp_receive.getLength()) + " from " + dp_receive.getAddress().getHostAddress() + ":" + dp_receive.getPort(); System.out.println(str_receive); //數(shù)據(jù)發(fā)動(dòng)到客戶端的3000端口 DatagramPacket dp_send= new DatagramPacket(str_send.getBytes(),str_send.length(),dp_receive.getAddress(),9000); ds.send(dp_send); System.out.println("Server send message succeed."); //由于dp_receive在接收了數(shù)據(jù)之后,其內(nèi)部消息長度值會(huì)變?yōu)閷?shí)際接收的消息的字節(jié)數(shù), //所以這里要將dp_receive的內(nèi)部消息長度重新置為1024 dp_receive.setLength(1024); } ds.close(); } } 應(yīng)用層瀏覽器從輸入U(xiǎn)RL地址到最終顯示內(nèi)容的過程
DNS查找對應(yīng)ip過程
首先查找瀏覽器自身的DNS緩存,如果有這個(gè)域名映射且沒過期(TTL)則直接向該IP發(fā)送HTTP請求,否則下一步
查找本地操作系統(tǒng)hosts緩存,如果有且沒過期,拿出來使用完成DNS解析,否則下一步
查找本地DNS域名服務(wù)器,
如果不可以由該服務(wù)器解析,則把請求發(fā)至根域名服務(wù)器,解析該域名是由誰來授權(quán)管理,返回頂級域名服務(wù)器的IP地址
本地DNS服務(wù)器聯(lián)系頂級域名服務(wù)器。
頂級域名服務(wù)器如果無法解析,則找下一級DNS服務(wù)器,并把IP發(fā)給本地DNS服務(wù)器。
以此類推,在DNS域名解析的過程中,使用UDP協(xié)議進(jìn)行不可靠傳輸,不需要三次握手,傳輸需要的內(nèi)容較少,使用UDP更快。
在網(wǎng)頁開發(fā)過程中盡量減少對DNS域名的解析,天貓,淘寶等使用進(jìn)行dns延遲緩存
https://www.cnblogs.com/sjm19910902/p/6423181.html
HTTP請求過程
建立TCP連接發(fā)送請求 一旦建立了TCP連接,Web瀏覽器就會(huì)向Web服務(wù)器發(fā)送請求命令。例如:GET/sample/hello.jsp HTTP/1.1。發(fā)送請求頭信息 瀏覽器發(fā)送其請求命令之后,還要以頭信息的形式向Web服務(wù)器發(fā)送一些別的信息,之后瀏覽器發(fā)送了一空白行來通知服務(wù)器,它已經(jīng)結(jié)束了該頭信息的發(fā)送。服務(wù)器應(yīng)答 客戶機(jī)向服務(wù)器發(fā)出請求后,服務(wù)器會(huì)客戶機(jī)回送應(yīng)答, HTTP/1.1 200 OK ,應(yīng)答的第一部分是協(xié)議的版本號和應(yīng)答狀態(tài)碼。服務(wù)器發(fā)送應(yīng)答頭信息 正如客戶端會(huì)隨同請求發(fā)送關(guān)于自身的信息一樣,服務(wù)器也會(huì)隨同應(yīng)答向用戶發(fā)送關(guān)于它自己的數(shù)據(jù)及被請求的文檔。服務(wù)器向?yàn)g覽器發(fā)送數(shù)據(jù) Web服務(wù)器向?yàn)g覽器發(fā)送頭信息后,它會(huì)發(fā)送一個(gè)空白行來表示頭信息的發(fā)送到此為結(jié)束,接著,它就以Content-Type應(yīng)答頭信息所描述的格式發(fā)送用戶所請求的實(shí)際數(shù)據(jù)。*7. Web服務(wù)器關(guān)閉TCP連接
一般情況下,一旦Web服務(wù)器向?yàn)g覽器發(fā)送了請求數(shù)據(jù),它就要關(guān)閉TCP連接,然后如果瀏覽器或者服務(wù)器在其頭信息加入了這行代碼:Connection:keep-alive TCP連接在發(fā)送后將仍然保持打開狀態(tài),于是,瀏覽器可以繼續(xù)通過相同的連接發(fā)送請求。保持連接節(jié)省了為每個(gè)請求建立新連接所需的時(shí)間,還節(jié)約了網(wǎng)絡(luò)帶寬。
如果是第一次訪問請求該網(wǎng)址
瀏覽器發(fā)送HTTP請求,請求頭包括:
請求方法(Request Method)協(xié)議版本客戶端信息(User-Agent)connect請求內(nèi)容等host如果順利訪問:客戶端返回200狀態(tài)碼
返回信息包括:
返回內(nèi)容expires設(shè)置緩存過期時(shí)間contentType返回內(nèi)容類型contentLengthstatusEtage該緩存的版本號contentEncodingDatecache-controlset-cookie設(shè)置本域名下瀏覽器的cookielastModified如果是第二次
瀏覽器則發(fā)出http請求時(shí)
帶上cookie發(fā)送if-Modified-since(匹配前一次請求時(shí)返回的last-modified)if-None-match(匹配前一次請求時(shí)返回的Etag)如果資源沒有被修改則返回304狀態(tài)碼。但是如果前一次請求瀏覽器設(shè)置expires,則瀏覽器首先會(huì)檢查緩存中的資源,如果在設(shè)置的expires時(shí)間之內(nèi)則不會(huì)再次發(fā)送請求。
lastModified代表服務(wù)器最后修改時(shí)間,精確到秒。expires資源過期時(shí)間,精確到秒。Etag則代表資源的版本號,每次修改資源Etag就會(huì)變。不同資源的Etag不同。
如果正確訪問
瀏覽器根據(jù)返回content-type,解析服務(wù)器返回的數(shù)據(jù)
瀏覽器解析html文件時(shí),每次遇到frame、img、link、javascript都會(huì)重新發(fā)送一個(gè)http請求javascript下載完后就會(huì)立即執(zhí)行阻塞瀏覽器的渲染以及繪制。所以一般js鏈接放在最后,但是很多瀏覽器都會(huì)優(yōu)先下載js文件和css文件,所以如果js沒有對dom操作,盡量defer延遲加載js文件。css在文檔頭,防止因?yàn)閏ss樣式改變導(dǎo)致瀏覽器多次重繪或者回流,是頁面閃動(dòng)卡頓。js和css盡量使用外鏈形式,減少DOM結(jié)構(gòu)的長度和復(fù)雜度,減少瀏覽器解析html文件的時(shí)間。
dom節(jié)點(diǎn)盡量別深度嵌套,css少使用多層選擇器。
頁面減少http請求的個(gè)數(shù),多個(gè)圖片使用圖片dataURI編碼或則圖片精靈進(jìn)行合并、css文件壓縮合并、js文件壓縮合并。配置localhost之后就不會(huì)走dns了
-----正文結(jié)束-----本文參考https://github.com/CyC2018/CS-Notes/
https://github.com/linw7/Skill-Tree/blob/master/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C.md
https://www.jianshu.com/p/674fb7ec1e2c
有刪減,修改,補(bǔ)充額外增加內(nèi)容
本作品采用知識共享署名-非商業(yè)性使用 4.0 國際許可協(xié)議進(jìn)行許可。
關(guān)注我我是一名后端開發(fā)工程師。主要關(guān)注后端開發(fā),數(shù)據(jù)安全,網(wǎng)絡(luò)爬蟲,物聯(lián)網(wǎng),邊緣計(jì)算等方向,歡迎交流。
各大平臺(tái)都可以找到我
Github:@qqxx6661CSDN:@Rude3Knife知乎:@Zhendong簡書:@蠻三刀把刀掘金:@蠻三刀把刀原創(chuàng)博客主要內(nèi)容
Java知識點(diǎn)復(fù)習(xí)全手冊Leetcode算法題解析劍指offer算法題解析SpringBoot菜鳥入門實(shí)戰(zhàn)系列SpringCloud菜鳥入門實(shí)戰(zhàn)系列爬蟲相關(guān)技術(shù)文章后端開發(fā)相關(guān)技術(shù)文章逸聞趣事/好書分享/個(gè)人興趣個(gè)人公眾號:后端技術(shù)漫談
如果文章對你有幫助,不妨收藏起來并轉(zhuǎn)發(fā)給您的朋友們~
logo設(shè)計(jì)網(wǎng)(www.just4love.cn),專業(yè)的logo免費(fèi)設(shè)計(jì)在線生成網(wǎng)站,全自動(dòng)智能化logo設(shè)計(jì),商標(biāo)設(shè)計(jì),logo在線生成!
歡迎使用logo設(shè)計(jì)網(wǎng)制作屬于您公司自己的logo,不僅專業(yè)而且經(jīng)濟(jì)實(shí)惠,全方位滿足您公司品牌化、視覺化的需求。