一 基本概念
1,HTTP 超文本傳輸協(xié)議
訪問遠(yuǎn)程的網(wǎng)絡(luò)資源糙及,規(guī)定了客戶端和服務(wù)端的數(shù)據(jù)傳輸格式。格式http://
http1.0和1.1的區(qū)別
-
默認(rèn)長連接(持續(xù)連接)
HTTP 1.0需要使用keep-alive參數(shù)來告知服務(wù)器端要建立一個(gè)長連接,而HTTP1.1默認(rèn)支持長連接祈匙。HTTP是基于TCP/IP協(xié)議的吐限,創(chuàng)建一個(gè)TCP連接是需要經(jīng)過三次握手的,有一定的開銷鲜侥,如果每次通訊都要重新建立連接的話,對(duì)性能有影響诸典。因此最好能維持一個(gè)長連接描函,可以用個(gè)長連接來發(fā)多個(gè)請(qǐng)求。 -
節(jié)約帶寬
HTTP 1.1支持只發(fā)送header信息(不帶任何body信息)狐粱,如果服務(wù)器認(rèn)為客戶端有權(quán)限請(qǐng)求服務(wù)器舀寓,則返回100,否則返回401肌蜻』ツ梗客戶端如果接受到100,才開始把請(qǐng)求body發(fā)送到服務(wù)器蒋搜。
這樣當(dāng)服務(wù)器返回401的時(shí)候篡撵,客戶端就可以不用發(fā)送請(qǐng)求body了判莉,節(jié)約了帶寬。 -
HOST域
設(shè)置虛擬站點(diǎn)是非常常見的育谬,即web server上的多個(gè)虛擬站點(diǎn)可以共享同一個(gè)ip和端口券盅。HTTP1.0沒有host域,HTTP1.1才支持這個(gè)參數(shù)膛檀。
http1.1和2.0的區(qū)別
-
多路復(fù)用
HTTP2.0使用了多路復(fù)用的技術(shù)锰镀,做到同一個(gè)連接并發(fā)處理多個(gè)請(qǐng)求,而且并發(fā)請(qǐng)求的數(shù)量比HTTP1.1大了好幾個(gè)數(shù)量級(jí)宿刮。
當(dāng)然HTTP1.1也可以多建立幾個(gè)TCP連接互站,來支持處理更多并發(fā)的請(qǐng)求,但是創(chuàng)建TCP連接本身也是有開銷的僵缺。
TCP連接有一個(gè)預(yù)熱和保護(hù)的過程胡桃,先檢查數(shù)據(jù)是否傳送成功,一旦成功過磕潮,則慢慢加大傳輸速度翠胰。因此對(duì)應(yīng)瞬時(shí)并發(fā)的連接,服務(wù)器的響應(yīng)就會(huì)變慢自脯。所以最好能使用一個(gè)建立好的連接之景,并且這個(gè)連接可以支持瞬時(shí)并發(fā)的請(qǐng)求。 -
數(shù)據(jù)壓縮
HTTP1.1不支持header數(shù)據(jù)的壓縮膏潮,HTTP2.0使用HPACK算法對(duì)header的數(shù)據(jù)進(jìn)行壓縮锻狗,這樣數(shù)據(jù)體積小了,在網(wǎng)絡(luò)上傳輸就會(huì)更快焕参。 -
服務(wù)器推送
當(dāng)我們對(duì)支持HTTP2.0的web server請(qǐng)求數(shù)據(jù)的時(shí)候轻纪,服務(wù)器會(huì)順便把一些客戶端需要的資源一起推送到客戶端,免得客戶端再次創(chuàng)建連接發(fā)送請(qǐng)求到服務(wù)器端獲取叠纷。這種方式非常合適加載靜態(tài)資源刻帚。
服務(wù)器端推送的這些資源其實(shí)存在客戶端的某處地方,客戶端直接從本地加載這些資源就可以了涩嚣,不用走網(wǎng)絡(luò)崇众,速度自然是快很多的。
發(fā)送HTTP請(qǐng)求的方法
http/1.1協(xié)議中定義了8中發(fā)送http請(qǐng)求的方法航厚,分別是get,post,options,head,put,delete,tarce,connect,patch.一般只用get和post顷歌。
GET和POST的區(qū)別
- GET 在請(qǐng)求URL后面以?的形式跟上發(fā)送給服務(wù)器的參數(shù)幔睬,多個(gè)參數(shù)用&隔開眯漩,但是由于瀏覽器和服務(wù)器對(duì)URL長度有限制,因此URL后附帶的參數(shù)是有限制的溪窒,通常不能超過1kb坤塞。
- POST 發(fā)送給服務(wù)器的參數(shù)全部放在請(qǐng)求體中,且理論上POST傳遞的數(shù)據(jù)沒有長度限制澈蚌。
HTTP通信過程
http協(xié)議規(guī)定摹芙,1個(gè)完整的http請(qǐng)求中包含以下內(nèi)容:
請(qǐng)求頭
:包含了對(duì)客戶端的環(huán)境描述,客戶端請(qǐng)求信息等
GET: /1.png HTTP/1.1 //包含請(qǐng)求方法宛瞄,請(qǐng)求資源路徑浮禾,HTTP協(xié)議版本
Host:10.62.232.226:8081 //客戶端要訪問的服務(wù)器主機(jī)地址
User-Agent:Mozilla/5.0 //客戶端的類型,客戶端的軟件環(huán)境
Accept:text/html, */* //客戶端所能接收的數(shù)據(jù)類型
Accept-Language:zh-cn //客戶端的語言環(huán)境
Accept-Encoding:gzip //客戶端支持的數(shù)據(jù)壓縮格式
請(qǐng)求體
:客戶端發(fā)服務(wù)器的具體數(shù)據(jù)份汗。只有在POST請(qǐng)求中才使用盈电,GET沒有
響應(yīng)頭
:包含了對(duì)服務(wù)器的描述,對(duì)返回?cái)?shù)據(jù)的描述
HTTP/1.1 200 OK //包含=HTTP協(xié)議版本,狀態(tài)碼杯活,狀態(tài)英文名稱
Server:Apache-Coyote/1.1 //服務(wù)器類型
Content-Type:image/jpeg //返回?cái)?shù)據(jù)的類型
Content-Length:56811 //返回?cái)?shù)據(jù)的長度
Date:Mon匆帚,23,Jun旁钧,2018 //響應(yīng)的時(shí)間
響應(yīng)體
:服務(wù)器返回給客戶端的具體數(shù)據(jù)吸重,比如文件數(shù)據(jù)
常見響應(yīng)狀態(tài)碼
* 200 成功
* 3XX 表示重定向相關(guān)
* 400 客戶端請(qǐng)求的語法錯(cuò)誤,服務(wù)器無法解析
* 404 服務(wù)器無法根據(jù)客戶端的請(qǐng)求找到資源
* 500 服務(wù)器內(nèi)部錯(cuò)誤歪今,無法完成請(qǐng)求
2嚎幸,TCP
TCP特點(diǎn)
- TCP是面向連接的
通信前需要建立連接,通信結(jié)束需要釋放連接寄猩。 - TCP提供可靠交付服務(wù)
所謂『可靠』指的是:TCP發(fā)送的數(shù)據(jù)無重復(fù)嫉晶、無丟失、無錯(cuò)誤田篇、與發(fā)送端順序一致替废。 - TCP是面向字節(jié)流的
所謂『面向字節(jié)流』指的是:TCP以字節(jié)為單位。雖然傳輸?shù)倪^程中數(shù)據(jù)被劃分成一個(gè)個(gè)數(shù)據(jù)報(bào)斯辰,但這只是為了方便傳輸舶担,接收端最終接受到的數(shù)據(jù)將與發(fā)送端的數(shù)據(jù)一模一樣。 - TCP提供全雙工通信
所謂『全雙工通信』指的是:TCP的兩端既可以作為發(fā)送端彬呻,也可以作為接收端衣陶。 - 一條TCP連接的兩端只能有兩個(gè)端點(diǎn)
TCP只能提供點(diǎn)到點(diǎn)的通信,而UDP可以任意方式的通信闸氮。
TCP連接與套接字
什么是『TCP連接』剪况?
TCP連接是一種抽象的概念,表示一條可以通信的鏈路蒲跨。
每條TCP連接有且僅有兩個(gè)端點(diǎn)译断,表示通信的雙方。且雙發(fā)在任意時(shí)刻都可以作為發(fā)送者和接收者或悲。什么是『套接字』孙咪?
一條TCP連接的兩端就是兩個(gè)套接字堪唐。
套接字=IP地址:端口號(hào)。
因此翎蹈,TCP連接=(套接字1淮菠,套接字2)=(IP1:端口號(hào)1,IP2:端口號(hào)2)
TCP頭部
tcp頭部至少由20個(gè)字節(jié)組成(最多60字節(jié)荤堪,因?yàn)檫x項(xiàng)字段最多40字節(jié))合陵,如下所示
各字段含義
源端口和目的端口
傳輸層和網(wǎng)絡(luò)層一大重要區(qū)別就是傳輸層指定了數(shù)據(jù)報(bào)發(fā)往的應(yīng)用進(jìn)程,因此需要端口號(hào)標(biāo)識(shí)澄阳。序號(hào)
當(dāng)前TCP數(shù)據(jù)報(bào)數(shù)據(jù)部分的第一個(gè)字節(jié)的序號(hào)拥知。
我們知道,TCP是面向字節(jié)的碎赢,它會(huì)對(duì)發(fā)送的每一個(gè)字節(jié)進(jìn)行編號(hào)低剔,而且不同數(shù)據(jù)報(bào)之間是連續(xù)編號(hào)的。
由于本字段4字節(jié)肮塞,可以給[0,232-1]個(gè)字節(jié)進(jìn)行編號(hào)(大約4G)户侥,而且序號(hào)循環(huán)使用,當(dāng)發(fā)送完232-1個(gè)字節(jié)后峦嗤,序號(hào)又從0開始蕊唐。
一般來說,當(dāng)2^32-1個(gè)字節(jié)被發(fā)送的時(shí)候烁设,前面的字節(jié)早就發(fā)送成功了替梨,因此序號(hào)可以循環(huán)使用。確認(rèn)號(hào)
表示當(dāng)前主機(jī)作為接收端時(shí)装黑,期望接收的下一個(gè)字節(jié)的編號(hào)是多少副瀑。
也表示,當(dāng)前主機(jī)已經(jīng)正確接收的最后一個(gè)字節(jié)序號(hào)+1恋谭。數(shù)據(jù)偏移(報(bào)頭長度)
它表明了數(shù)據(jù)報(bào)頭部的長度糠睡。保留字段
標(biāo)識(shí)符
TCP有7種標(biāo)識(shí)符,用于表示TCP報(bào)文的性質(zhì)疚颊。它們只能為0或1姑原。
URG=1
當(dāng)URG字段被置1滋迈,表示本數(shù)據(jù)報(bào)的數(shù)據(jù)部分包含緊急信息渣淤,此時(shí)緊急指針有效挺据。
緊急數(shù)據(jù)一定位于當(dāng)前數(shù)據(jù)包數(shù)據(jù)部分的最前面,緊急指針標(biāo)明了緊急數(shù)據(jù)的尾部其掂。
如control+c:這個(gè)命令要求操作系統(tǒng)立即停止當(dāng)前進(jìn)程油挥。此時(shí),這條命令就會(huì)存放在數(shù)據(jù)包數(shù)據(jù)部分的開頭,并由緊急指針標(biāo)識(shí)命令的位置深寥,并URG字段被置1攘乒。
ACK=1
ACK被置1后確認(rèn)號(hào)字段才有效。
此外惋鹅,TCP規(guī)定持灰,在連接建立后傳送的所有報(bào)文段都必須把ACK置1。
PSH=1
當(dāng)接收方收到PSH=1的報(bào)文后负饲,會(huì)立即將數(shù)據(jù)交付給應(yīng)用程序,而不會(huì)等到緩沖區(qū)滿后再提交喂链。
一些交互式應(yīng)用需要這樣的功能返十,降低命令的響應(yīng)時(shí)間。
RST=1
當(dāng)該值為1時(shí)椭微,表示當(dāng)前TCP連接出現(xiàn)嚴(yán)重問題洞坑,必須要釋放重連。
SYN=1
SYN在建立連接時(shí)使用蝇率。
當(dāng)SYN=1迟杂,ACK=0時(shí),表示當(dāng)前報(bào)文段是一個(gè)連接請(qǐng)求報(bào)文本慕。
當(dāng)SYN=1排拷,ACK=1時(shí),表示當(dāng)前報(bào)文段是一個(gè)同意建立連接的應(yīng)答報(bào)文锅尘。
FIN=1
FIN=1表示此報(bào)文段是一個(gè)釋放連接的請(qǐng)求報(bào)文监氢。
接收窗口大小
該字段用于實(shí)現(xiàn)TCP的流量控制。
它表示當(dāng)前接收方的接收窗口的剩余容量藤违,發(fā)送方收到該值后會(huì)將發(fā)送窗口調(diào)整成該值的大小浪腐。發(fā)送窗口的大小又決定了發(fā)送速率,所以接收方通過設(shè)置該值就可以控制發(fā)送放的發(fā)送速率顿乒。
發(fā)送方每收到一個(gè)數(shù)據(jù)報(bào)都要調(diào)整當(dāng)前的發(fā)送窗口议街。
檢驗(yàn)和
用于接收端檢驗(yàn)整個(gè)數(shù)據(jù)包在傳輸過程中是否出錯(cuò)。
緊急指針
用于標(biāo)識(shí)緊急數(shù)據(jù)的尾部璧榄。
選項(xiàng)字段
上述字段都是每個(gè)TCP頭部必須要有的特漩,而選項(xiàng)字段是可選的,且長度可變骨杂,最長40字節(jié)拾稳。
最常用的選項(xiàng)字段為MMS:最大報(bào)文長度。
TCP三次握手
起初腊脱,服務(wù)器和客戶端都為CLOSED狀態(tài)访得。在通信開始前,雙方都得創(chuàng)建各自的傳輸控制塊(TCB)。
服務(wù)器創(chuàng)建完TCB后遍進(jìn)入LISTEN狀態(tài)悍抑,此時(shí)準(zhǔn)備接收客戶端發(fā)來的連接請(qǐng)求鳄炉。
第一次握手
客戶端向服務(wù)端發(fā)送連接請(qǐng)求報(bào)文段。該報(bào)文段的頭部中SYN=1搜骡,ACK=0拂盯,seq=x。請(qǐng)求發(fā)送后记靡,客戶端便進(jìn)入SYN-SENT狀態(tài)谈竿。
PS1:SYN=1,ACK=0表示該報(bào)文段為連接請(qǐng)求報(bào)文摸吠。
PS2:x為本次TCP通信的字節(jié)流的初始序號(hào)空凸。
TCP規(guī)定:SYN=1的報(bào)文段不能有數(shù)據(jù)部分,但要消耗掉一個(gè)序號(hào)寸痢。
第二次握手
服務(wù)端收到連接請(qǐng)求報(bào)文段后呀洲,如果同意連接,則會(huì)發(fā)送一個(gè)應(yīng)答:SYN=1啼止,ACK=1道逗,seq=y,ack=x+1献烦。
該應(yīng)答發(fā)送完成后便進(jìn)入SYN-RCVD狀態(tài)滓窍。
PS1:SYN=1,ACK=1表示該報(bào)文段為連接同意的應(yīng)答報(bào)文巩那。
PS2:seq=y表示服務(wù)端作為發(fā)送者時(shí)贰您,發(fā)送字節(jié)流的初始序號(hào)。
PS3:ack=x+1表示服務(wù)端希望下一個(gè)數(shù)據(jù)報(bào)發(fā)送序號(hào)從x+1開始的字節(jié)拢操。
第三次握手
當(dāng)客戶端收到連接同意的應(yīng)答后锦亦,還要向服務(wù)端發(fā)送一個(gè)確認(rèn)報(bào)文段,表示:服務(wù)端發(fā)來的連接同意應(yīng)答已經(jīng)成功收到令境。
該報(bào)文段的頭部為:ACK=1杠园,seq=x+1,ack=y+1舔庶。
客戶端發(fā)完這個(gè)報(bào)文段后便進(jìn)入ESTABLISHED狀態(tài)抛蚁,服務(wù)端收到這個(gè)應(yīng)答后也進(jìn)入ESTABLISHED狀態(tài),此時(shí)連接的建立完成惕橙!
為什么一定要是三次握手瞧甩?
防止失效的連接請(qǐng)求報(bào)文段被服務(wù)端接收,從而產(chǎn)生錯(cuò)誤弥鹦。
想象這樣一種情況肚逸,客戶端給服務(wù)端發(fā)送了連接請(qǐng)求報(bào)文段爷辙,但是因?yàn)榫W(wǎng)絡(luò)原因,報(bào)文段被阻塞朦促,客戶端收不到服務(wù)端的回應(yīng)便超時(shí)重傳膝晾,之后連接建立,雙方便開始通信务冕,通信結(jié)束后釋放連接血当。此時(shí)第一次被網(wǎng)絡(luò)阻塞的請(qǐng)求又到了服務(wù)端,因?yàn)橹挥袃纱挝帐仲饕洌?wù)端以為客戶端又重新請(qǐng)求建立連接臊旭,便進(jìn)入連接建立狀態(tài)等待客戶端發(fā)送數(shù)據(jù),但此時(shí)客戶端并沒有新的數(shù)據(jù)要發(fā)送箩退,是關(guān)閉狀態(tài)的离熏,服務(wù)端將會(huì)一直等待下去,這樣浪費(fèi)服務(wù)端連接資源乏德。
TCP四次揮手
TCP連接是雙向的,因此在四次揮手中吠昭,前兩次揮手用于斷開一個(gè)方向的連接喊括,后兩次揮手用于斷開另一方向的連接。
第一次揮手
若A認(rèn)為數(shù)據(jù)發(fā)送完成矢棚,則它需要向B發(fā)送連接釋放請(qǐng)求郑什。該請(qǐng)求只有報(bào)文頭,頭中攜帶的主要參數(shù)為:
FIN=1蒲肋,seq=u蘑拯。此時(shí),A將進(jìn)入FIN-WAIT-1狀態(tài)兜粘。
PS1:FIN=1表示該報(bào)文段是一個(gè)連接釋放請(qǐng)求申窘。
PS2:seq=u,u-1是A向B發(fā)送的最后一個(gè)字節(jié)的序號(hào)孔轴。
第二次揮手
B收到連接釋放請(qǐng)求后剃法,會(huì)通知相應(yīng)的應(yīng)用程序,告訴它A向B這個(gè)方向的連接已經(jīng)釋放路鹰。此時(shí)B進(jìn)入CLOSE-WAIT狀態(tài)贷洲,并向A發(fā)送連接釋放的應(yīng)答,其報(bào)文頭包含:
ACK=1晋柱,seq=v优构,ack=u+1。
PS1:ACK=1:除TCP連接請(qǐng)求報(bào)文段以外雁竞,TCP通信過程中所有數(shù)據(jù)報(bào)的ACK都為1钦椭,表示應(yīng)答。
PS2:seq=v,v-1是B向A發(fā)送的最后一個(gè)字節(jié)的序號(hào)玉凯。
PS3:ack=u+1表示希望收到從第u+1個(gè)字節(jié)開始的報(bào)文段势腮,并且已經(jīng)成功接收了前u個(gè)字節(jié)。
A收到該應(yīng)答漫仆,進(jìn)入FIN-WAIT-2狀態(tài)捎拯,等待B發(fā)送連接釋放請(qǐng)求。
第二次揮手完成后盲厌,A到B方向的連接已經(jīng)釋放署照,B不會(huì)再接收數(shù)據(jù),A也不會(huì)再發(fā)送數(shù)據(jù)吗浩。但B到A方向的連接仍然存在建芙,B可以繼續(xù)向A發(fā)送數(shù)據(jù)。
第三次揮手
當(dāng)B向A發(fā)完所有數(shù)據(jù)后懂扼,向A發(fā)送連接釋放請(qǐng)求禁荸,請(qǐng)求頭:FIN=1,ACK=1阀湿,seq=w赶熟,ack=u+1。B便進(jìn)入LAST-ACK狀態(tài)陷嘴。
第四次揮手
A收到釋放請(qǐng)求后映砖,向B發(fā)送確認(rèn)應(yīng)答,此時(shí)A進(jìn)入TIME-WAIT狀態(tài)灾挨。該狀態(tài)會(huì)持續(xù)2MSL時(shí)間邑退,若該時(shí)間段內(nèi)沒有B的重發(fā)請(qǐng)求的話,就進(jìn)入CLOSED狀態(tài)劳澄,撤銷TCB地技。當(dāng)B收到確認(rèn)應(yīng)答后,也便進(jìn)入CLOSED狀態(tài)秒拔,撤銷TCB乓土。
為什么A要先進(jìn)入TIME-WAIT狀態(tài),等待2MSL時(shí)間后才進(jìn)入CLOSED狀態(tài)溯警?
為了保證服務(wù)端能收到客戶端的應(yīng)答趣苏。
如果客戶端第四次揮手后直接關(guān)閉,但是應(yīng)答丟失了梯轻,那么服務(wù)端等待超時(shí)后會(huì)重發(fā)請(qǐng)求食磕,但此時(shí)客戶端已經(jīng)關(guān)閉了,所以服務(wù)端無法正常關(guān)閉喳挑。
TCP可靠傳輸?shù)膶?shí)現(xiàn)
TCP采用了流量控制彬伦、擁塞控制滔悉、連續(xù)ARQ等技術(shù)來保證它的可靠性。(PS:網(wǎng)絡(luò)層傳輸?shù)臄?shù)據(jù)單元為『數(shù)據(jù)報(bào)』单绑,傳輸層的數(shù)據(jù)單元為『報(bào)文段』回官,但為了方便起見,可以統(tǒng)稱為『分組』搂橙。)
停止等待協(xié)議(ARQ協(xié)議)
TCP保證其可靠性采用的是更為復(fù)雜的滑動(dòng)窗口協(xié)議歉提,但停止等待協(xié)議是它的簡化版,為了方便理解区转,這里先介紹停止等待協(xié)議苔巨。
AQR協(xié)議
ARQ(Automatic Repeat reQuest)自動(dòng)重傳請(qǐng)求。
顧名思義废离,當(dāng)請(qǐng)求失敗時(shí)它會(huì)自動(dòng)重傳侄泽,直到請(qǐng)求被正確接收為止。這種機(jī)制保證了每個(gè)分組都能被正確接收蜻韭。停止等待協(xié)議是一種ARQ協(xié)議悼尾。
停止等待協(xié)議的原理
無差錯(cuò)的情況
A向B每發(fā)送一個(gè)分組,都要停止發(fā)送肖方,等待B的確認(rèn)應(yīng)答闺魏;A只有收到了B的確認(rèn)應(yīng)答后才能發(fā)送下一個(gè)分組。分組丟失和出現(xiàn)差錯(cuò)的情況
發(fā)送者擁有超時(shí)計(jì)時(shí)器窥妇。每發(fā)送一個(gè)分組便會(huì)啟動(dòng)超時(shí)計(jì)時(shí)器舷胜,等待B的應(yīng)答娩践。若超時(shí)仍未收到應(yīng)答活翩,則A會(huì)重發(fā)剛才的分組。
分組出現(xiàn)差錯(cuò):若B收到分組翻伺,但通過檢查和字段發(fā)現(xiàn)分組在運(yùn)輸途中出現(xiàn)差錯(cuò)材泄,它會(huì)直接丟棄該分組,并且不會(huì)有任何其他動(dòng)作吨岭。A超時(shí)后便會(huì)重新發(fā)送該分組拉宗,直到B正確接收為止。
分組丟失:若分組在途中丟失辣辫,B并沒有收到分組旦事,因此也不會(huì)有任何響應(yīng)。當(dāng)A超時(shí)后也會(huì)重傳分組急灭,直到正確接收該分組的應(yīng)答為止姐浮。
綜上所述:當(dāng)分組丟失 或 出現(xiàn)差錯(cuò) 的情況下,A都會(huì)超時(shí)重傳分組葬馋。應(yīng)答丟失 和 應(yīng)答遲到 的情況
TCP會(huì)給每個(gè)字節(jié)都打上序號(hào)卖鲤,用于判斷該分組是否已經(jīng)接收肾扰。
應(yīng)答丟失:若B正確收到分組,并已經(jīng)返回應(yīng)答蛋逾,但應(yīng)答在返回途中丟失了集晚。此時(shí)A也收不到應(yīng)答,從而超時(shí)重傳区匣。緊接著B又收到了該分組偷拔。接收者根據(jù)序號(hào)來判斷當(dāng)前收到的分組是否已經(jīng)接收,若已接收則直接丟棄沉颂,并補(bǔ)上一個(gè)確認(rèn)應(yīng)答条摸。
應(yīng)答遲到:若由于網(wǎng)絡(luò)擁塞,A遲遲收不到B發(fā)送的應(yīng)答铸屉,因此會(huì)超時(shí)重傳钉蒲。B收到該分組后,發(fā)現(xiàn)已經(jīng)接收彻坛,便丟棄該分組顷啼,并向A補(bǔ)上確認(rèn)應(yīng)答。A收到應(yīng)答后便繼續(xù)發(fā)送下一個(gè)分組昌屉。但經(jīng)過了很長時(shí)間后钙蒙,那個(gè)失效的應(yīng)答最終抵達(dá)了A,此時(shí)A可根據(jù)序號(hào)判斷該分組已經(jīng)接收间驮,此時(shí)只需簡單丟棄即可躬厌。
停止等待協(xié)議的注意點(diǎn)
- 每發(fā)送完一個(gè)分組,該分組必須被保留竞帽,直到收到確認(rèn)應(yīng)答為止扛施。
- 必須給每個(gè)分組進(jìn)行編號(hào)。以便按序接收屹篓,并判斷該分組是否已被接收疙渣。
- 必須設(shè)置超時(shí)計(jì)時(shí)器。每發(fā)送一個(gè)分組就要啟動(dòng)計(jì)時(shí)器堆巧,超時(shí)就要重發(fā)分組妄荔。
- 計(jì)時(shí)器的超時(shí)時(shí)間要大于應(yīng)答的平均返回時(shí)間,否則會(huì)出現(xiàn)很多不必要的重傳谍肤,降低傳輸效率啦租。但超時(shí)時(shí)間也不能太長。
滑動(dòng)窗口協(xié)議(連續(xù)ARQ協(xié)議)
連續(xù)ARQ協(xié)議
在ARQ協(xié)議發(fā)送者每次只能發(fā)送一個(gè)分組荒揣,在應(yīng)答到來前必須等待篷角。而連續(xù)ARQ協(xié)議的發(fā)送者擁有一個(gè)發(fā)送窗口,發(fā)送者可以在沒有得到應(yīng)答的情況下連續(xù)發(fā)送窗口中的分組乳附。這樣降低了等待時(shí)間内地,提高了傳輸效率伴澄。
累計(jì)確認(rèn)
在連續(xù)ARQ協(xié)議中,接收者也有個(gè)接收窗口阱缓,接收者并不需要每收到一個(gè)分組就返回一個(gè)應(yīng)答非凌,可以連續(xù)收到分組之后統(tǒng)一返回一個(gè)應(yīng)答。這樣能節(jié)省流量荆针。
TCP頭部的ack字段就是用來累計(jì)確認(rèn)敞嗡,它表示已經(jīng)確認(rèn)的字節(jié)序號(hào)+1,也表示期望發(fā)送者發(fā)送的下一個(gè)分組的起始字節(jié)號(hào)航背。
發(fā)送窗口
發(fā)送窗口的大小由接收窗口的剩余大小決定喉悴。接收者會(huì)把當(dāng)前接收窗口的剩余大小寫入應(yīng)答TCP報(bào)文段的頭部,發(fā)送者收到應(yīng)答后根據(jù)該值和當(dāng)前網(wǎng)絡(luò)擁塞情況設(shè)置發(fā)送窗口的大小玖媚。發(fā)送窗口的大小是不斷變化的箕肃。
發(fā)送窗口由三個(gè)指針構(gòu)成:
- p1
p1指向發(fā)送窗口的后沿,它后面的字節(jié)表示已經(jīng)發(fā)送且已收到應(yīng)答今魔。 - p2
p2指向尚未發(fā)送的第一個(gè)字節(jié)勺像。
p1-p2間的字節(jié)表示已經(jīng)發(fā)送,但還沒收到確認(rèn)應(yīng)答错森。這部分的字節(jié)仍需保留吟宦,因?yàn)榭赡苓€要超時(shí)重發(fā)。
p2-p3間的字節(jié)表示可以發(fā)送涩维,但還沒有發(fā)送的字節(jié)殃姓。 - p3
p3指向發(fā)送窗口的前沿,它前面的字節(jié)尚未發(fā)送瓦阐,且不允許發(fā)送蜗侈。
發(fā)送者每收到一個(gè)應(yīng)答,后沿就可以向前移動(dòng)指定的字節(jié)垄分。此時(shí)若窗口大小仍然沒變宛篇,前沿也可以向前移動(dòng)指定字節(jié)娃磺。
當(dāng)p2和前沿重合時(shí)薄湿,發(fā)送者必須等待確認(rèn)應(yīng)答。
接收窗口
接收者收到的字節(jié)會(huì)存入接收窗口偷卧,接收者會(huì)對(duì)已經(jīng)正確接收的有序字節(jié)進(jìn)行累計(jì)確認(rèn)豺瘤,發(fā)送完確認(rèn)應(yīng)答后,接收窗口就可以向前移動(dòng)指定字節(jié)听诸。
如果某些字節(jié)并未按序收到坐求,接收者只會(huì)確認(rèn)最后一個(gè)有序的字節(jié),從而亂序的字節(jié)就會(huì)被重新發(fā)送晌梨。
連續(xù)ARQ的注意點(diǎn)
同一時(shí)刻發(fā)送窗口的大小并不一定和接收窗口一樣大桥嗤。
雖然發(fā)送窗口的大小是根據(jù)接收窗口的大小來設(shè)定的须妻,但應(yīng)答在網(wǎng)絡(luò)中傳輸是有時(shí)間的,有可能t1時(shí)間接收窗口大小為m泛领,但當(dāng)確認(rèn)應(yīng)答抵達(dá)發(fā)送者時(shí)荒吏,接收窗口的大小已經(jīng)發(fā)生了變化。
此外發(fā)送窗口的大小還隨網(wǎng)絡(luò)擁塞情況影響渊鞋。當(dāng)網(wǎng)絡(luò)出現(xiàn)擁塞時(shí)绰更,發(fā)送窗口將被調(diào)小。TCP標(biāo)準(zhǔn)并未規(guī)定未按序到達(dá)的字節(jié)的處理方式锡宋。但TCP一般都會(huì)緩存這些字節(jié)儡湾,等缺少的字節(jié)到達(dá)后再交給應(yīng)用層處理。這比直接丟棄亂序的字節(jié)要節(jié)約帶寬执俩。
TCP標(biāo)準(zhǔn)規(guī)定接收方必須要有累計(jì)確認(rèn)功能徐钠。接收方可以對(duì)多個(gè)TCP報(bào)文段同時(shí)確認(rèn),但不能拖太長時(shí)間役首,一般是0.5S以內(nèi)丹皱。
此外,TCP允許接收者在有數(shù)據(jù)要發(fā)送的時(shí)候捎帶上確認(rèn)應(yīng)答宋税。但這種情況一般較少摊崭,因?yàn)橐话愫苌儆袃蓚€(gè)方向都要發(fā)送數(shù)據(jù)的情況。
流量控制和擁塞控制
- 擁塞控制:擁塞控制是作用于網(wǎng)絡(luò)的杰赛,它是防止過多的數(shù)據(jù)注入到網(wǎng)絡(luò)中呢簸,避免出現(xiàn)網(wǎng)絡(luò)負(fù)載過大的情況; 通過慢開始和擁塞避免算法來控制
- 流量控制:流量控制是作用于接收者的乏屯,它是控制發(fā)送者的發(fā)送速度從而使接收者來得及接收根时。 由滑動(dòng)窗口協(xié)議實(shí)現(xiàn)。
3辰晕,UDP
UDP的特點(diǎn)
UDP只在IP數(shù)據(jù)報(bào)服務(wù)的基礎(chǔ)上增加了少量的功能:復(fù)用與分用蛤迎、對(duì)整個(gè)報(bào)文的差錯(cuò)檢測。
- UDP是無連接的
通信前不需要建立連接含友,通信結(jié)束也無需釋放連接替裆。 - UDP是不可靠的
它是盡力而為交付,不能確保每一個(gè)數(shù)據(jù)報(bào)都送達(dá)窘问。 - UDP是面向報(bào)文的
所謂『面向報(bào)文』就是指:UDP數(shù)據(jù)傳輸?shù)膯挝皇菆?bào)文辆童,且不會(huì)對(duì)數(shù)據(jù)作任何 拆分 和 拼接 操作。
在發(fā)送端惠赫,應(yīng)用程序給傳輸層的UDP什么樣的數(shù)據(jù)把鉴,UDP不會(huì)對(duì)數(shù)據(jù)進(jìn)行切分,只增加一個(gè)UDP頭并交給網(wǎng)絡(luò)層儿咱。
在接收端庭砍,UDP收到網(wǎng)絡(luò)層的數(shù)據(jù)報(bào)后场晶,去除IP數(shù)據(jù)報(bào)頭部后遍交給應(yīng)用層,不會(huì)作任何拼接操作怠缸。 - UDP沒有擁塞控制
UDP始終以恒定的速率發(fā)送數(shù)據(jù)峰搪,并不會(huì)根據(jù)網(wǎng)絡(luò)擁塞情況對(duì)發(fā)送速率作調(diào)整。這種方式有利有弊凯旭。
弊端:網(wǎng)絡(luò)擁塞時(shí)有些報(bào)文可能會(huì)丟失概耻,因此UDP不可靠。
優(yōu)點(diǎn):有些使用場景允許報(bào)文丟失罐呼,如:直播鞠柄、語音通話,但對(duì)實(shí)時(shí)性要求很高嫉柴,此時(shí)UDP還是很有用武之地的厌杜。 - UDP支持一對(duì)一、一對(duì)多计螺、多對(duì)多夯尽、多對(duì)一通信
而TCP只支持一對(duì)一通信。 - UDP首部開銷小登馒,只有8字節(jié)匙握。
而TCP頭部至少由20字節(jié),相比于TCP要高效很多陈轿。
UDP報(bào)文頭
- 源端口
- 目的端口
- 長度:整個(gè)數(shù)據(jù)報(bào)的長度
- 檢驗(yàn)和:整個(gè)數(shù)據(jù)報(bào)的檢驗(yàn)和圈纺。
二 iOS中網(wǎng)絡(luò)請(qǐng)求
1,NSURLConnection
同步請(qǐng)求代碼示例—GET
//1.確定請(qǐng)求路徑
NSURL *url = [NSURL URLWithString:@"http://10.25.226.186:8081/login麦射?username=a&pwd=123&type=XML"];
//2.創(chuàng)建一個(gè)請(qǐng)求對(duì)象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.把請(qǐng)求發(fā)送給服務(wù)器
//sendSynchronousRequest 阻塞式的方法蛾娶,會(huì)卡住線程
NSHTTPURLResponse *response = nil;
NSError *error = nil;
/*
第一個(gè)參數(shù):請(qǐng)求對(duì)象
第二個(gè)參數(shù):響應(yīng)頭信息,當(dāng)該方法執(zhí)行完畢之后潜秋,該參數(shù)被賦值
第三個(gè)參數(shù):錯(cuò)誤信息蛔琅,如果請(qǐng)求失敗,則error有值
*/
//該方法是阻塞式的峻呛,會(huì)卡住線程
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//4.解析服務(wù)器返回的數(shù)據(jù)
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
異步請(qǐng)求代碼示例—GET
//1.確定請(qǐng)求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it"];
//2.創(chuàng)建一個(gè)請(qǐng)求對(duì)象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.把請(qǐng)求發(fā)送給服務(wù)器,發(fā)送一個(gè)異步請(qǐng)求
/*
第一個(gè)參數(shù):請(qǐng)求對(duì)象
第二個(gè)參數(shù):回調(diào)方法在哪個(gè)線程中執(zhí)行罗售,如果是主隊(duì)列則block在主線程中執(zhí)行,非主隊(duì)列則在子線程中執(zhí)行
第三個(gè)參數(shù):completionHandlerBlock塊:接受到響應(yīng)的時(shí)候執(zhí)行該block中的代碼
response:響應(yīng)頭信息
data:響應(yīng)體
connectionError:錯(cuò)誤信息杀饵,如果請(qǐng)求失敗莽囤,那么該參數(shù)有值
*/
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc]init] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//4.解析服務(wù)器返回的數(shù)據(jù)
NSString *str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//轉(zhuǎn)換并打印響應(yīng)頭信息
NSHTTPURLResponse *r = (NSHTTPURLResponse *)response;
NSLog(@"--%zd---%@--",r.statusCode,r.allHeaderFields);
}];
通過代理實(shí)現(xiàn)異步請(qǐng)求—GET
1)步驟
1. 確定請(qǐng)求路徑
2. 創(chuàng)建請(qǐng)求對(duì)象
3. 創(chuàng)建NSURLConnection對(duì)象并設(shè)置代理
4. 遵守NSURLConnectionDataDelegate協(xié)議谬擦,并實(shí)現(xiàn)相應(yīng)的代理方法
5. 在代理方法中監(jiān)聽網(wǎng)絡(luò)請(qǐng)求的響應(yīng)
2)設(shè)置代理的幾種方法
/*
設(shè)置代理的第一種方式:自動(dòng)發(fā)送網(wǎng)絡(luò)請(qǐng)求
[[NSURLConnection alloc]initWithRequest:request delegate:self];
*/
/*
設(shè)置代理的第二種方式:
第一個(gè)參數(shù):請(qǐng)求對(duì)象
第二個(gè)參數(shù):誰成為NSURLConnetion對(duì)象的代理
第三個(gè)參數(shù):是否馬上發(fā)送網(wǎng)絡(luò)請(qǐng)求切距,如果該值為YES則立刻發(fā)送,如果為NO則不會(huì)發(fā)送網(wǎng)路請(qǐng)求
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//調(diào)用該方法控制網(wǎng)絡(luò)請(qǐng)求的發(fā)送
[conn start];
*/
//設(shè)置代理的第三種方式:使用類方法設(shè)置代理惨远,會(huì)自動(dòng)發(fā)送網(wǎng)絡(luò)請(qǐng)求
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
//取消網(wǎng)絡(luò)請(qǐng)求
//[conn cancel];
3)相關(guān)的代理方法
/*
1.當(dāng)接收到服務(wù)器響應(yīng)的時(shí)候調(diào)用
第一個(gè)參數(shù)connection:監(jiān)聽的是哪個(gè)NSURLConnection對(duì)象
第二個(gè)參數(shù)response:接收到的服務(wù)器返回的響應(yīng)頭信息
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveResponse:(nonnull NSURLResponse *)response
/*
2.當(dāng)接收到數(shù)據(jù)的時(shí)候調(diào)用谜悟,該方法會(huì)被調(diào)用多次
第一個(gè)參數(shù)connection:監(jiān)聽的是哪個(gè)NSURLConnection對(duì)象
第二個(gè)參數(shù)data:本次接收到的服務(wù)端返回的二進(jìn)制數(shù)據(jù)(可能是片段)
*/
- (void)connection:(nonnull NSURLConnection *)connection didReceiveData:(nonnull NSData *)data
/*
3.當(dāng)服務(wù)端返回的數(shù)據(jù)接收完畢之后會(huì)調(diào)用
通常在該方法中解析服務(wù)器返回的數(shù)據(jù)
*/
-(void)connectionDidFinishLoading:(nonnull NSURLConnection *)connection
/*4.當(dāng)請(qǐng)求錯(cuò)誤的時(shí)候調(diào)用(比如請(qǐng)求超時(shí))
第一個(gè)參數(shù)connection:NSURLConnection對(duì)象
第二個(gè)參數(shù):網(wǎng)絡(luò)請(qǐng)求的錯(cuò)誤信息话肖,如果請(qǐng)求失敗,則error有值
*/
- (void)connection:(nonnull NSURLConnection *)connection didFailWithError:(nonnull NSError *)error
異步請(qǐng)求—POST
1)發(fā)送POST請(qǐng)求步驟
1. 確定URL路徑
2. 創(chuàng)建請(qǐng)求對(duì)象(可變對(duì)象)
3. 修改請(qǐng)求對(duì)象的方法為POST葡幸,設(shè)置請(qǐng)求體(Data)
4. 發(fā)送一個(gè)異步請(qǐng)求
5. 補(bǔ)充:設(shè)置請(qǐng)求超時(shí)最筒,處理錯(cuò)誤信息,設(shè)置請(qǐng)求頭(如獲取客戶端的版本等等,請(qǐng)求頭是可設(shè)置可不設(shè)置的)
//1.確定請(qǐng)求路徑
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//2.創(chuàng)建請(qǐng)求對(duì)象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.1更改請(qǐng)求方法
request.HTTPMethod = @"POST";
//2.2設(shè)置請(qǐng)求體
request.HTTPBody = [@"username=520it&pwd=520it" dataUsingEncoding:NSUTF8StringEncoding];
//2.3請(qǐng)求超時(shí)
request.timeoutInterval = 5;
//2.4設(shè)置請(qǐng)求頭
[request setValue:@"ios 9.0" forHTTPHeaderField:@"User-Agent"];
//3.發(fā)送請(qǐng)求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * __nullable response, NSData * __nullable data, NSError * __nullable connectionError) {
//4.解析服務(wù)器返回的數(shù)據(jù)
if (connectionError) {
NSLog(@"--請(qǐng)求失敗-");
}else{
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}
}];
2蔚叨,NSURLSession
- 使用步驟
使用NSURLSession創(chuàng)建task,然后執(zhí)行task
- 關(guān)于task
NSURLSessionTask是一個(gè)抽象類床蜘,本身不能使用,只能使用它的子類
NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask
- 發(fā)送get請(qǐng)求
//1.創(chuàng)建NSURLSession對(duì)象(可以獲取單例對(duì)象)
NSURLSession *session = [NSURLSession sharedSession];
//2.根據(jù)NSURLSession對(duì)象創(chuàng)建一個(gè)Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=ss&pwd=ss&type=JSON"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//方法參數(shù)說明
/*
注意:該block是在子線程中調(diào)用的蔑水,如果拿到數(shù)據(jù)之后要做一些UI刷新操作邢锯,那么需要回到主線程刷新
第一個(gè)參數(shù):需要發(fā)送的請(qǐng)求對(duì)象
block:當(dāng)請(qǐng)求結(jié)束拿到服務(wù)器響應(yīng)的數(shù)據(jù)時(shí)調(diào)用block
block-NSData:該請(qǐng)求的響應(yīng)體
block-NSURLResponse:存放本次請(qǐng)求的響應(yīng)信息,響應(yīng)頭搀别,真實(shí)類型為NSHTTPURLResponse
block-NSErroe:請(qǐng)求錯(cuò)誤信息
*/
NSURLSessionDataTask * dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
//拿到響應(yīng)頭信息
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//4.解析拿到的響應(yīng)數(shù)據(jù)
NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
}];
//3.執(zhí)行Task
//注意:剛創(chuàng)建出來的task默認(rèn)是掛起狀態(tài)的丹擎,需要調(diào)用該方法來啟動(dòng)任務(wù)(執(zhí)行任務(wù))
[dataTask resume];
- 發(fā)送get請(qǐng)求的第二種方式
//注意:該方法內(nèi)部默認(rèn)會(huì)把URL對(duì)象包裝成一個(gè)NSURLRequest對(duì)象(默認(rèn)是GET請(qǐng)求)
//方法參數(shù)說明
/*
//第一個(gè)參數(shù):發(fā)送請(qǐng)求的URL地址
//block:當(dāng)請(qǐng)求結(jié)束拿到服務(wù)器響應(yīng)的數(shù)據(jù)時(shí)調(diào)用block
//block-NSData:該請(qǐng)求的響應(yīng)體
//block-NSURLResponse:存放本次請(qǐng)求的響應(yīng)信息,響應(yīng)頭歇父,真實(shí)類型為NSHTTPURLResponse
//block-NSErroe:請(qǐng)求錯(cuò)誤信息
*/
- (nullable NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error))completionHandler;
- 發(fā)送POST請(qǐng)求
//1.創(chuàng)建NSURLSession對(duì)象(可以獲取單例對(duì)象)
NSURLSession *session = [NSURLSession sharedSession];
//2.根據(jù)NSURLSession對(duì)象創(chuàng)建一個(gè)Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"];
//創(chuàng)建一個(gè)請(qǐng)求對(duì)象蒂培,并設(shè)置請(qǐng)求方法為POST,把參數(shù)放在請(qǐng)求體中傳遞
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
//拿到響應(yīng)頭信息
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
//解析拿到的響應(yīng)數(shù)據(jù)
NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
}];
//3.執(zhí)行Task
//注意:剛創(chuàng)建出來的task默認(rèn)是掛起狀態(tài)的榜苫,需要調(diào)用該方法來啟動(dòng)任務(wù)(執(zhí)行任務(wù))
[dataTask resume];
3护戳,第三方框架 AFN
將單獨(dú)寫一篇文章來解析AFN源碼分析...