網(wǎng)絡通信三要素
通過 “IP” 找服務器姻报,通過 “端口” 找進程泉唁, 通過 “協(xié)議” 確定如何傳輸數(shù)據(jù)
IP地址(主機名)
端口號
用于標示進程的邏輯地址毕骡,不同進程的標示
有效端口:0~65535
其中 0~1024由系統(tǒng)使用或者保留端口
開發(fā)中不要使用 1024 以下的端口
注意 : 跟HTTP相關的端口一定是80.服務器上有個進程是專門處理HTTP請求的,端口號是80
傳輸協(xié)議
TCP(傳輸控制協(xié)議) 相當于打電話,必須先建立好鏈接才能傳輸數(shù)據(jù).
UDP(數(shù)據(jù)報文協(xié)議) 相當于發(fā)電報,不用關心對方是否能夠收到躏哩,不太安全
HTTP協(xié)議底層是基于TCP/IP協(xié)議的毙沾,網(wǎng)絡傳輸協(xié)議在傳輸層選擇的是TCP/IP協(xié)議
TCP 協(xié)議了解
TCP的全稱是Transmission Control Protocol,傳輸控制協(xié)議骗卜。TCP并不是基于UDP協(xié)議構建的,和UDP協(xié)議一樣是基于IP協(xié)議構建的左胞。
TCP主要解決下面的三個問題:
- 數(shù)據(jù)的可靠傳輸寇仓。發(fā)送方如何知道發(fā)出的數(shù)據(jù),接收方已經(jīng)收到烤宙。
- 接收方的流量控制遍烦。因為各種原因,接收方可能來不及處理發(fā)送方發(fā)送的數(shù)據(jù)躺枕,而造成沒有及時回應發(fā)送方服猪,造成發(fā)送方不斷的重發(fā)數(shù)據(jù)供填,最后造成接收方的主機宕機。
- 計算機網(wǎng)絡的擁塞控制罢猪。數(shù)據(jù)在計算機網(wǎng)絡之上傳輸近她,當出現(xiàn)數(shù)據(jù)擁塞時如何進行處理
TCP協(xié)議
特性
1、面向連接的傳輸協(xié)議
應用程序在使用TCP之前膳帕,必須先建立TCP傳輸連接
2粘捎、僅支持單播傳輸
TCP傳輸連接只能有兩個端點,這里的端點指套接字危彩,也就是IP地址和端口號的組合攒磨。不支持廣播和組播。
3汤徽、數(shù)據(jù)的可靠傳輸(可靠交付)
現(xiàn)實生活中娩缰,我們在打電話的時候,當我們自己根對方說了一句話或者一段話之后谒府,我們都會等待對方的回應拼坎,譬如她們會回答"哦"、"嗯"完疫、"知道了"演痒,這時我們就會知道對方已經(jīng)聽到我們自己剛才說的話,如果她們不給予回應則會以為她們沒有在聽我講話趋惨,也就是沒有收到我發(fā)送的消息鸟顺。
TCP可靠傳輸?shù)膶崿F(xiàn)正是基于這樣的例子,對于發(fā)送方發(fā)送的數(shù)據(jù)器虾,接收方在接受到數(shù)據(jù)之后必須要給予確認讯嫂,確認它收到了數(shù)據(jù)。如果在規(guī)定時間內兆沙,沒有給予確認則意味著接收方?jīng)]有接受到數(shù)據(jù)欧芽,然后發(fā)送方對數(shù)據(jù)進行重發(fā)。
TCP的可靠傳輸是通過確認和超時重傳的機制來實現(xiàn)的葛圃,而確認和超時重傳的具體的實現(xiàn)是通過以字節(jié)為單位的滑動窗口機制來完成
4千扔、傳輸單位為數(shù)據(jù)段
數(shù)據(jù)段大小受應用層傳送的報文大小和所途經(jīng)網(wǎng)絡中的MTU值決定。MSS:最大數(shù)據(jù)段大小库正,最小數(shù)據(jù)段可能僅有21字節(jié)曲楚,其中20字節(jié)頭部,1字節(jié)數(shù)據(jù)褥符。
5龙誊、支持全雙工傳輸
允許通訊雙方的應用程序在任何時候都能發(fā)送數(shù)據(jù)。
6喷楣、TCP連接是基于字節(jié)流趟大,而非報文流
TCP發(fā)送的數(shù)據(jù)是無界限的鹤树,因而在接收的時候需要根據(jù)長度來確認數(shù)據(jù)接收完成。
7逊朽、每次發(fā)送的TCP數(shù)據(jù)段大小和數(shù)據(jù)段數(shù)都是可變的
需要根據(jù)對方給出的窗口大小和當前網(wǎng)絡的擁塞程度來決定罕伯。
數(shù)據(jù)段大小的兩個決定因素:
1.每個TCP數(shù)據(jù)段的大小必須符合IP數(shù)據(jù)包的65515字節(jié)的有效載荷大小限制要求。
2.每個網(wǎng)絡都有一個MTU值叽讳,因此每個TCP數(shù)據(jù)段必須符合MTU限制要求捣炬。
流量控制
現(xiàn)實生活中,我們去一些熱門的景點或者游樂園的某個娛樂項目時绽榛,都會需要進行排隊,如果是小長假婿屹,則會出現(xiàn)人山人海的場景灭美,這時這些機構就會控制每一次參觀該景點的人數(shù)。
網(wǎng)絡應用程序也是如此昂利,當數(shù)據(jù)到達主機之后届腐,TCP會將該數(shù)據(jù)放入相應的隊列(又稱為緩沖區(qū))(如果讓你自己基于UDP實現(xiàn)一個TCP模塊供自己的應用程序使用,你也會采用這種方式)蜂奸,等待監(jiān)聽該端口的應用程序從隊列中獲取數(shù)據(jù)犁苏,應用程序一次所能處理的數(shù)據(jù)有限,因此不可能一次性取出隊列中的所有數(shù)據(jù)扩所,當隊列已經(jīng)滿了围详,則無法再存放新的數(shù)據(jù),只能將接受到的數(shù)據(jù)丟棄祖屏,因此TCP協(xié)議需要提供流量控制的能力助赞,控制發(fā)送方每次發(fā)送數(shù)據(jù)的大小。
擁塞控制
現(xiàn)實生活中袁勺,高速公路也會堵車雹食,在一段高速公路上,每輛車都在以很快的速度在運行期丰,彼此并沒有慢下來群叶,但是為什么還是會出現(xiàn)堵車呢?通常都是因為每段道路的承載能力不一樣钝荡,譬如當一段8車道公路上的汽車行駛到4車道公路上時街立,在這兩段道路交匯的地方就會出現(xiàn)堵車。
計算機網(wǎng)絡是由無數(shù)的數(shù)據(jù)鏈路組成的埠通,每一段鏈路的承載能力不一樣几晤,也會出現(xiàn)數(shù)據(jù)擁堵的情況,這通常是由路由器和交換機的處理能力不同造成的植阴。我們還需要知道蟹瘾,這種情況下的擁塞是不能避免的圾浅,因為我們無法要求所有鏈路的承載能力一樣,因此我們只能對擁塞進行控制憾朴。TCP協(xié)議對擁塞控制也提出了響應的解決方案狸捕,這也是為什么TCP叫做傳輸控制協(xié)議而不叫做可靠傳輸協(xié)議的原因吧,同時也解釋了為什么在計算機網(wǎng)絡可靠性能大大提供的今天众雷,TCP還繼續(xù)發(fā)揮著其作用的原因灸拍。
慢啟動機制
慢啟動通過逐步增大擁塞窗口的值來控制網(wǎng)絡擁塞。
通常在剛開始發(fā)送報文段時砾省,先把擁塞窗口cwnd設置為一個最大報文段的數(shù)值鸡岗。而在每收到一個對新報文段的確認后,把擁塞窗口增加至多一個MSS數(shù)值编兄。
也就是說轩性,第一次時發(fā)送1個報文,在收到接收端確認之后狠鸳,第二次時發(fā)送2個報文揣苏,同樣都確認后,第三次時發(fā)送4個報文件舵,2倍指數(shù)增長卸察。
它的名字雖然叫慢啟動,但實際上一點不慢铅祸,因為指數(shù)增長是很快的坑质,所以它需要一個上限值,默認為64k临梗。
慢啟動的作用就是最大限度使用網(wǎng)絡資源洪乍。
連接管理
連接的作用就是讓通訊雙方知道并準備好通訊。
建立連接夜焦,TCP三次握手
客戶端與服務端建立一個 TCP 連接共計需要發(fā)送 3 個包才能完成壳澳,這個過程稱為三次握手(Three-way Handshake)。如上面所述茫经,數(shù)據(jù)段的序號巷波、確認序號以及滑動窗口大小都在這個過程中完成。socket 編程中卸伞,客戶端執(zhí)行 connect() 時抹镊,將觸發(fā)三次握手。
注:
- 1415531521:1415531521(0) 表示分組的序號是 1415531521荤傲,而報文的數(shù)據(jù)字節(jié)數(shù)為 0垮耳。
- WIN:4096 表示發(fā)送端通告的窗口大小為 4096,上圖中由于沒有交換任何數(shù)據(jù)所以窗口維持在 4096。
- < MSS 1024> 表示由發(fā)端指明的最大報文段長度選項為 1024终佛。
- ACK 1415531521 表示確認序號俊嗽,只在首部 ACK 標志位設置為 1 時才有效。
如上圖所示完成 TCP 連接的建立铃彰,客戶端與服務端共計發(fā)送了 3 個報文段:
1绍豁、報文段1:客戶端發(fā)送一個 SYN 報文段(握手信號)指明客戶打算連接的服務器的端口,以及ISN(Initial Sequence Number 初始序號牙捉,這個例子中 ISN=1415531521)竹揍。ISN 的實現(xiàn)目前會隨著時間的變化而變化,所以每次建立連接時的 ISN 都不同邪铲。這一步告訴服務器芬位,我要訪問你了。
1带到、報文段2:服務器發(fā)回包含服務器的 ISN (初始序號)的 SYN 報文段(這個例子中 ISN=1823083521)作為應答昧碉。同時,將確認序號設置為客戶的 ISN + 1 以報文段 1 進行確認阴孟。一個 SYN 將占用一個序號。服務器告訴客戶端税迷,我收到了你的訪問請求永丝。
3、報文段3:客戶必須將確認序號設置為服務器的 ISN+1(1823083522) 以對服務器的 SYN 報文段進行確認箭养,客戶端又告訴服務器慕嚷,我收到了你的確認。
4毕泌、連接建立喝检,開始進行數(shù)據(jù)通信。
為什么要使用三次握手
為了防止已經(jīng)失效的連接請求報文段突然又傳到服務端撼泛,因而產生錯誤
比如:
一端(client)A發(fā)出去的第一個連接請求報文并沒有丟失挠说,而是因為某些未知的原因在某個網(wǎng)絡節(jié)點上發(fā)生滯留,導致延遲到連接釋放以后的某個時間才到達另一端(server)B愿题。本來這是一個早已失效的報文段损俭,但是B收到此失效的報文之后,會誤認為是A再次發(fā)出的一個新的連接請求潘酗,于是B端就向A又發(fā)出確認報文,表示同意建立連接。如果不采用“三次握手”饮潦,那么只要B端發(fā)出確認報文就會認為新的連接已經(jīng)建立了南捂,但是A端并沒有發(fā)出建立連接的請求,因此不會去向B端發(fā)送數(shù)據(jù),B端沒有收到數(shù)據(jù)就會一直等待日裙,這樣B端就會白白浪費掉很多資源
問題的本質是吹艇,信道是不可靠的,但是我們要建立可靠的連接發(fā)送可靠的數(shù)據(jù)阅签,也就是數(shù)據(jù)傳輸是需要可靠的掐暮。在這個時候三次握手是一個理論上的最小值,并不是說是tcp協(xié)議要求的政钟,而是為了滿足在不可靠的信道上傳輸可靠的數(shù)據(jù)所要求的路克。
三次握手牽扯到的狀態(tài)轉換
LISTEN 表示socket已經(jīng)處于listen狀態(tài)了,可以建立連接
SYN_SENT 表示socket在發(fā)出connect連接的時候养交,會首先發(fā)送SYN報文精算,然后等待另一端發(fā)送的確認報文(ACK),表示這端已經(jīng)發(fā)送完SYN報文了
SYN_RCVD 表示一端已經(jīng)接收到SYN報文了
ESTABLISHED 表示已經(jīng)建立連接了碎连,可以發(fā)送數(shù)據(jù)了
超時重傳機制
(1) 如果第一個包灰羽,A發(fā)送給B請求建立連接的報文(SYN)如果丟掉了,A會周期性的超時重傳鱼辙,直到B發(fā)出確認(SYN+ACK)廉嚼;
(2) 如果第二個包,B發(fā)送給A的確認報文(SYN+ACK)如果丟掉了倒戏,B會周期性的超時重傳怠噪,直到A發(fā)出確認(ACK);
(3) 如果第三個包杜跷,A發(fā)送給B的確認報文(ACK)如果丟掉了傍念,
A在發(fā)送完確認報文之后,單方面會進入ESTABLISHED的狀態(tài)葛闷,B還是SYN_RCVD狀態(tài)
如果此時雙方都沒有數(shù)據(jù)需要發(fā)送憋槐,B會周期性的超時發(fā)送(SYN+ACK),直到收到A的確認報文(ACK)淑趾,此時B也進入ESTABLISHED狀態(tài)阳仔,雙方可以發(fā)送數(shù)據(jù);
如果A有數(shù)據(jù)發(fā)送扣泊,A發(fā)送的是(ACK+DATA)驳概,B會在收到這個數(shù)據(jù)包的時候自動切換到ESTABLISHED狀態(tài),并接受數(shù)據(jù)(DATA)旷赖;
如果這個時候B要發(fā)送數(shù)據(jù)顺又,B是發(fā)送不了數(shù)據(jù)的,會周期性的超時重傳(SYN+ACK)直到收到A的確認(ACK)B才能發(fā)送數(shù)據(jù)等孵。
同時連接
上面所示的是一方主動連接另外一方的情況稚照,實際上 TCP 也允許雙方同時主動連接,這種情況下就要求連接雙方提前知道對方的端口。實際中很少出現(xiàn)這種需求果录。這種情況下上枕,要發(fā)送 4 個報文段才能建立起連接。
附:SYN 洪水攻擊
由三次握手可以看出弱恒,當服務器收到 SYN 數(shù)據(jù)報文段后將為連接分配資源辨萍,如果服務器沒有收到 ACK 報文段就會造成半開連接,浪費服務器資源返弹。SYN 洪水攻擊就是利用 TCP 的這個缺陷锈玉,通過向服務器發(fā)送海量的 SYN 報文段而耗盡服務器資源。
一般有兩種方式:
1义起、客戶端惡意不發(fā)送 ACK拉背;
2、在發(fā)送給服務器的 SYN 報文段中提供虛假的 IP 地址默终,造成服務器永遠收不到 ACK椅棺。
終止連接,TCP 四次揮手
建立一個連接需要 3 次握手齐蔽,而終止一個連接要經(jīng)過 4 次握手两疚。這由 TCP 的半關閉(half-close,連接的一端在結束它的發(fā)送后還能接收來自另一端數(shù)據(jù)的能力含滴。具體的請查閱 TCP 半關閉的相關資料)造成的诱渤。 TCP 連接是全雙工,因此每個方向必須單獨地進行關閉蛙吏。也就是當一方完成它的數(shù)據(jù)發(fā)送任務后就能發(fā)送一個 FIN 來終止這個方向連接源哩,當一端收到一個 FIN鞋吉,它必須通知應用層另一端已經(jīng)終止了那個方向的數(shù)據(jù)傳送鸦做。發(fā)送 FIN 通常是應用層進行關閉的結果。
連接雙方都可發(fā)起這個操作谓着,socket 編程中泼诱,任何一方執(zhí)行 close() 觸發(fā)揮手操作。
上圖的 4 次揮手示意圖是接著上面 3 次握手進行的赊锚,假設沒有應用數(shù)據(jù)傳輸治筒,所以報文段4的序號緊接著報文段1的序號(ACK 的發(fā)送是沒有任何代價的,不會消耗序號)舷蒲。圖中所示的是一方主動關閉(首先發(fā)送 FIN 數(shù)據(jù)報)耸袜,另一方被動關閉,實際上 TCP 也允許雙方同時主動關閉牲平。
同時關閉
為什么TCP建立連接協(xié)議是三次握手堤框,而關閉連接卻是四次握手呢?
- 這是因為服務端的LISTEN狀態(tài)下的SOCKET當收到SYN報文的建連請求后,它可以把ACK和SYN(ACK起應答作用蜈抓,而SYN起同步作用)放在一 個報文里來發(fā)送启绰。
- 但關閉連接時,當收到對方的FIN報文通知時沟使,它僅僅表示對方?jīng)]有數(shù)據(jù)發(fā)送給你了委可;但未必你所有的數(shù)據(jù)都全部發(fā)送給對方了,所以你可以未 必會馬上會關閉SOCKET,也即你可能還需要發(fā)送一些數(shù)據(jù)給對方之后腊嗡,再發(fā)送FIN報文給對方來表示你同意現(xiàn)在可以關閉連接了着倾,所以它這里的ACK報文 和FIN報文多數(shù)情況下都是分開發(fā)送的。
為什么TIME_WAIT狀態(tài)還需要等2MSL后才能返回到CLOSED狀態(tài)叽唱?
因為雖然雙方都同意關閉連接了屈呕,而且握手的4個報文也都發(fā)送完畢,按理可以直接回到CLOSED 狀態(tài)(就好比從SYN_SENT 狀態(tài)到ESTABLISH 狀態(tài)那樣)棺亭,但是我們必須假想網(wǎng)絡是不可靠的虎眨,你無法保證你(客戶端)最后發(fā)送的ACK報文一定會被對方收到,就是說對方處于LAST_ACK 狀態(tài)下的SOCKET可能會因為超時未收到ACK報文镶摘,而重發(fā)FIN報文嗽桩,所以這個TIME_WAIT 狀態(tài)的作用就是用來重發(fā)可能丟失的ACK報文。
關閉TCP連接一定需要4次揮手嗎?
- 不一定凄敢,4次揮手關閉TCP連接是最安全的做法碌冶。但在有些時候,我們不喜歡TIME_WAIT 狀態(tài)(如當MSL數(shù)值設置過大導致服務器端有太多TIME_WAIT狀態(tài)的TCP連接涝缝,減少這些條目數(shù)可以更快地關閉連接扑庞,為新連接釋放更多資源)
- 我們可以通過設置SOCKET變量的SO_LINGER標志來避免SOCKET在close()之后進入TIME_WAIT狀態(tài),這時將通過發(fā)送RST強制終止TCP連接(取代正常的TCP四次握手的終止方式)拒逮。但這并不是一個很好的主意罐氨,TIME_WAIT 對于我們來說往往是有利的
TCP的有限狀態(tài)機
狀態(tài) | 描述 |
---|---|
CLOSED | 呈阻塞,關閉狀態(tài)滩援,表示當前主機沒有活動的傳輸連接或沒有正在進行傳輸連接 |
LISTEN | 呈監(jiān)聽狀態(tài)栅隐,表示服務器正在等待新的傳輸連接進入 |
SYNRCVD | 表示服務器已經(jīng)收到一個傳輸連接請求,但尚未確認 |
SYNSENT | 表示客戶端已經(jīng)發(fā)出一個傳輸連接請求玩徊,等待服務器的確認 |
ESTABLISHED | 傳輸連接建立 |
FIN_WAIT_1 | 主動關閉方的主機已經(jīng)發(fā)送關閉連接請求租悄,等待對方確認 |
CLOSE_WAIT | 被動關閉方的主機收到主動關閉方的關閉連接請求,并已確認 |
FIN_WAIT_2 | 主動關閉方的主機已經(jīng)收到對方對主動關閉連接請求的確認恩袱,等待對方發(fā)送關閉傳輸連接請求 |
LAST_ACT | 被動關閉方的主機已經(jīng)發(fā)送關閉連接請求泣棋,等到主動方確認 |
TIME_WAIT | 主動關閉方的主機收到對方發(fā)送的關閉連接請求 |
更詳細內容
UDP協(xié)議了解
UDP(User Datagram Protocol 用戶數(shù)據(jù)報協(xié)議)協(xié)議全稱是用戶數(shù)據(jù)報協(xié)議,在網(wǎng)絡中它與TCP協(xié)議一樣用于處理數(shù)據(jù)包畔塔,是一種無連接的協(xié)議潭辈,提供面向事務的簡單不可靠信息傳送服務。
在OSI模型中萎胰,在第四層——傳輸層技竟,處于IP協(xié)議的上一層冰肴。UDP有不提供數(shù)據(jù)包分組熙尉、組裝和不能對數(shù)據(jù)包進行排序的缺點,也就是說搓扯,當報文發(fā)送之后锨推,是無法得知其是否安全完整到達的椎椰。
UDP是與TCP相對應的協(xié)議慨飘。它是面向非連接的協(xié)議瓤的,它不與對方建立連接,而是直接就把數(shù)據(jù)包發(fā)送過去本辐。
特點
1、UDP是無連接的
即發(fā)送數(shù)據(jù)之前不需要建立連接,因此減少了開銷和發(fā)送數(shù)據(jù)之前的時延忽刽。
2跪帝、盡最大努力交付
UDP使用盡最大努力交付斑唬,即不保證可靠交付恕刘,因此主機不需要維持復雜的鏈接狀態(tài)表(這里面有許多參數(shù))。
3含蓉、面向報文
發(fā)送方的UDP對應用程序交下來的報文,在添加首部后就向下交付給IP層岂嗓。既不拆分厌殉,也不合并,而是保留這些報文的邊界楼眷,因此,應用程序需要選擇合適的報文大小张吉。
4勺择、支持一對一省核、一對多、多對一和多對多的交互通信
5笔刹、首部開銷小
只有8個字節(jié),比TCP 的20個字節(jié)的首部要短
6日月、沒有擁塞控制系統(tǒng)、沒有超時重發(fā)精拟、所以速度快
7、當應用程序使用廣播或多播時只能使用UDP協(xié)議
UDP 和 TCP 的區(qū)別
特征點 | TCP | UDP |
---|---|---|
是否連接 | 面向連接 | 面向非連接 |
傳輸可靠性 | 可靠 | 會丟包师枣,不可靠 |
應用場景 | 傳輸數(shù)據(jù)量大 | 傳輸量小 |
速度 | 慢 | 快 |
1、TCP協(xié)議是提供面向連接的陨倡、可靠的字節(jié)流服務矛缨;傳輸數(shù)據(jù)量大灵妨,傳輸速度相對較慢泌霍;
2蟹地、UDP是提供面向事務的簡單不可靠信息傳送服務;傳輸數(shù)據(jù)量小分别,傳輸速度相對較快
Http協(xié)議了解
HTTP 協(xié)議(HyperText Transfer Protocol,超文本傳輸協(xié)議):是客戶端瀏覽器或其他程序與Web服務器之間的應用層通信協(xié)議 括授。HTTP是一個基于TCP/IP通信協(xié)議來傳遞數(shù)據(jù),可用于將超文本服務器中文本曲管、圖片、音視頻等內容傳輸?shù)娇蛻舳藶g覽器檬某。
特點
1、簡單快速
客戶向服務器請求服務時场斑,只需傳送請求方法和路徑喧半。請求方法常用的有GET、HEAD扁耐、POST。每種方法規(guī)定了客戶與服務器聯(lián)系的類型不同。由于HTTP協(xié)議簡單瘫筐,使得HTTP服務器的程序規(guī)模小,因而通信速度很快之众。
2、靈活
HTTP允許傳輸任意類型的數(shù)據(jù)對象膘婶。正在傳輸?shù)念愋陀蒀ontent-Type加以標記
3、無連接
無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求割捅,并收到客戶的應答后巫糙,即斷開連接醉锄。采用這種方式可以節(jié)省傳輸時間檩小。
4、無狀態(tài)
無狀態(tài)是指協(xié)議對于事務處理沒有記憶能力阻肿。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數(shù)據(jù)量增大姥敛。另一方面,在服務器不需要先前信息時它的應答就較快。
5渠概、支持 B/S 和 C/S模式
請求方法
根據(jù)HTTP標準贮喧,HTTP請求可以使用多種請求方法。
HTTP1.0定義了三種請求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五種請求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法寒跳。
GET 請求指定的頁面信息,并返回實體主體。
HEAD 類似于get請求爆惧,只不過返回的響應中沒有具體的內容,用于獲取報頭
POST 向指定資源提交數(shù)據(jù)進行處理請求(例如提交表單或者上傳文件)。數(shù)據(jù)被包含在請求體中饺律。POST請求可能會導致新的資源的建立和/或已有資源的修改。
PUT 從客戶端向服務器傳送的數(shù)據(jù)取代指定的文檔的內容。
DELETE 請求服務器刪除指定的頁面砸泛。
CONNECT HTTP/1.1協(xié)議中預留給能夠將連接改為管道方式的代理服務器。
OPTIONS 允許客戶端查看服務器的性能围俘。
TRACE 回顯服務器收到的請求,主要用于測試或診斷陶耍。
URI 和 URL
URI 統(tǒng)一資源標識符
URI(uniform resource identifier泊碑,統(tǒng)一資源標識符)酗钞,用來唯一的標識一個資源砚作。URI是以一種抽象的,高層次概念定義統(tǒng)一資源標識骇扇,而URL和URN則是具體的資源標識的方式熬苍。
Web上可用的資源(HTML文檔钱磅、圖像年柠、適配、代碼等等)都是一個URI來定位的,URI 一般由三部分組成:
1傲武、訪問資源的命名機制
2狠持、存放資源的主機名
3甜刻、資源自身的名稱,由路徑表示,著重強調資源
URL(uniform resource locator就谜,統(tǒng)一資源定位器)喧枷,一種具體的URI车荔,可以標識一個資源,同時還指明了如何定位這個資源忧便。URL 一般由三部分組成:
1族吻、協(xié)議
2、資源所在主機的IP地址(端口號)
3珠增、資源在主機上的具體地址,目錄蒂教。
URN(uniform resource name巍举,統(tǒng)一資源命名),通過名字來標識資源凝垛,比如
mailto:java-net@java.sun.com
請求消息(Request)
客戶端發(fā)送的一個Http請求到服務器懊悯,請求消息由四個部分組成:
請求行(request line)、請求頭部(header)苔严、空行和請求數(shù)據(jù)四個部分組成定枷。
下面看兩個請求消息的例子孤澎,分別是GET 和 POST請求:
GET /build/styles.css HTTP/1.1
Host xilankong.com
Connection keep-alive
Accept text/css,*/*;q=0.1
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15
Accept-Language zh-cn
Referer http://xilankong.com/
Accept-Encoding gzip, deflate
POST / HTTP1.1
Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive
name=Professional%20Ajax&publisher=Wiley
第一部分:請求行届氢,用來說明請求的類型、要訪問的資源以及所使用的協(xié)議版本
GET說明請求類型為GET覆旭, /build/styles.css 為要訪問的資源退子,該行的最后一部分說明使用的是HTTP1.1版本。
POST說明請求類型為POST
第二部分:請求頭部型将,緊接著請求行之后的部分寂祥,用來說明服務器要使用的附加信息
從第二行起為請求頭部
///////GET請求
Host
Connection
Accept
User-Agent
Accept-Language
Referer
Accept-Encoding
///////POST請求
Host
User-Agent
Content-Type
Content-Length
Connection
第三部分:空行,請求頭部后需要保證空行
GET請求后續(xù)由于沒有主體數(shù)據(jù)了七兜,最后一行是空行
POST請求 Connection 下面行就是空行
第四部分:請求數(shù)據(jù)丸凭,也叫主體,可以添加任何數(shù)據(jù)腕铸,沒有數(shù)據(jù)就為空
GET請求沒有主體
POST請求最后一行就是主體
響應消息(Response)
一般情況下惜犀,服務器接收并處理客戶端發(fā)過來的請求后會返回一個HTTP的響應消息。
HTTP響應也由四個部分組成狠裹,分別是:狀態(tài)行虽界、消息報頭、空行和響應正文
看個例子:
HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8
<html>
<head></head>
<body>
<!--body goes here-->
</body>
</html>
第一部分:狀態(tài)行涛菠,由協(xié)議版本號莉御、狀態(tài)碼撇吞、狀態(tài)消息構成
(HTTP/1.1)表明HTTP版本為1.1版本,狀態(tài)碼為200礁叔,狀態(tài)消息為(ok)
第二部分:消息報頭牍颈,用來說明客戶端需要使用的一些附加信息
第二行和第三行就是消息報頭,這里包括了響應日期時間和響應類型以及編碼
第三部分:空行琅关,消息報頭后面必須空行
第四部分:響應正文颂砸,服務器返回給客戶端的文本信息
<html>
<head></head>
<body>
<!--body goes here-->
</body>
</html>
狀態(tài)碼
1xx:指示信息--表示請求已接收,繼續(xù)處理
2xx:成功--表示請求已被成功接收死姚、理解人乓、接受
3xx:重定向--要完成請求必須進行更進一步的操作
4xx:客戶端錯誤--請求有語法錯誤或請求無法實現(xiàn)
5xx:服務器端錯誤--服務器未能實現(xiàn)合法的請求
常見的狀態(tài)碼
200 OK //客戶端請求成功
400 Bad Request //客戶端請求有語法錯誤,不能被服務器所理解
401 Unauthorized //請求未經(jīng)授權都毒,這個狀態(tài)代碼必須和WWW-Authenticate報頭域一起使用
403 Forbidden //服務器收到請求色罚,但是拒絕提供服務
404 Not Found //請求資源不存在,eg:輸入了錯誤的URL
500 Internal Server Error //服務器發(fā)生不可預期的錯誤
503 Server Unavailable //服務器當前不能處理客戶端的請求账劲,一段時間后可能恢復正常
工作原理
Http協(xié)議定義Web客戶端如何從Web服務器請求Web頁面戳护,以及服務器如何把Web頁面?zhèn)魉徒o客戶端。Http協(xié)議采用了請求/響應模型瀑焦‰缜遥客戶端向服務器發(fā)送一個請求報文,請求報文包含請求的方法榛瓮、URL铺董、協(xié)議版本、請求頭部禀晓、請求數(shù)據(jù)精续。服務器以一個狀態(tài)行作為響應,響應的內容包括協(xié)議的版本粹懒、成功或者錯誤代碼重付、服務器信息、響應頭部和響應數(shù)據(jù)凫乖。
Http請求 / 響應的步驟
1确垫、客戶端連接到web服務器
一個http客戶端(通常是瀏覽器)與服務器的Http端口(默認 80) 建立一個TCP Socket連接
2、發(fā)送Http請求
通過TCP Socket 客戶端向服務器發(fā)送一個文本的請求報文
3帽芽、服務器接受請求并返回Http響應
服務器解析請求删掀,定位請求資源,響應客戶端
4嚣镜、釋放連接
若connection模式 為 close 則服務器主動關閉TCP連接爬迟,客戶端被動關閉連接,若connection模式 為 keep-alive 則連接會保持一段時間菊匿,在該時間內可以繼續(xù)接收請求
5付呕、客戶端解析響應數(shù)據(jù)
客戶端瀏覽器首先解析狀態(tài)行计福,查看表明請求是否成功的狀態(tài)代碼。然后解析每一個響應頭,響應頭告知以下為若干字節(jié)的HTML文檔和文檔的字符集∪勐穑客戶端瀏覽器讀取響應數(shù)據(jù)HTML,根據(jù)HTML的語法對其進行格式化说订,并在瀏覽器窗口中顯示。
整個請求流程如下
1潮瓶、瀏覽器向 DNS 服務器請求解析該 URL 中的域名所對應的 IP 地址;
2陶冷、解析出 IP 地址后,根據(jù)該 IP 地址和默認端口 80毯辅,和服務器建立TCP連接;
3埂伦、瀏覽器發(fā)出讀取文件(URL 中域名后面部分對應的文件)的HTTP 請求,該請求報文作為 TCP 三次握手的第三個報文的數(shù)據(jù)發(fā)送給服務器;
4思恐、服務器對瀏覽器請求作出響應沾谜,并把對應的 html 文本發(fā)送給瀏覽器;
5、釋放 TCP連接;
6胀莹、瀏覽器將該 html 文本并顯示內容;
GET請求和POST請求的區(qū)別
1基跑、GET請求參數(shù)拼接在URL之后,以?分割URL和傳輸數(shù)據(jù)描焰,多個參數(shù)用&連接媳否;POST提交把提交的數(shù)據(jù)放置在Http請求包的包體中(主體)。
2栈顷、傳輸數(shù)據(jù)大心嫒铡(Http協(xié)議本身并沒有對傳輸數(shù)據(jù)的大小進行限制,協(xié)議規(guī)范也沒有對URL長度進行限制)萄凤。在實際的開發(fā)中,限制來源于瀏覽器和服務器對URL的長度限制搪哪。由此靡努,GET請求會受到URL長度限制。POST不通過URL傳遞參數(shù)晓折,理論上數(shù)據(jù)大小不限制惑朦,但實際上服務器都會對POST提交有數(shù)據(jù)大小限制。
3漓概、安全性漾月,相對而言GET請求通過URL拼接參數(shù),如果使用明文的用戶名胃珍、密碼將很不安全梁肿,除此之外蜓陌,使用GET提交數(shù)據(jù)還可能會造成Cross-site request forgery攻擊
Http請求的缺點
1、明文通信吩蔑,內容能輕易被竊聽
2钮热、不驗證通信雙方的身份,可能遭遇偽裝烛芬、冒充
3隧期、無法證明報文的完整性,數(shù)據(jù)可能遭篡改
Https協(xié)議了解
Https 協(xié)議(HyperText Transfer Protocol over Secure Socket Layer):可以理解為 Http + TLS / SSL赘娄, 即 Http 下加入 SSL 層仆潮,Https 的安全基礎是 SSL,因此加密的詳細內容就需要 SSL遣臼,用于安全的 Http 數(shù)據(jù)傳輸鸵闪。
關鍵名詞解釋
TLS / SSL
Http的基礎上添加 TLS / SSL 構成了更安全的Https,那么這個TLS / SSL到底是什么暑诸?
SSL:(Secure Socket Layer蚌讼,安全套接字層),位于可靠的面向連接的網(wǎng)絡層協(xié)議和應用層協(xié)議之間的一種協(xié)議層个榕。SSL通過互相認證篡石、使用數(shù)字簽名確保完整性、使用加密確保私密性西采,以實現(xiàn)客戶端和服務器之間的安全通訊凰萨。
SSL協(xié)議可分為兩層: SSL記錄協(xié)議(SSL Record Protocol):它建立在可靠的傳輸協(xié)議(如TCP)之上,為高層協(xié)議提供數(shù)據(jù)封裝械馆、壓縮胖眷、加密等基本功能的支持。 SSL握手協(xié)議(SSL Handshake Protocol):它建立在SSL記錄協(xié)議之上霹崎,用于在實際的數(shù)據(jù)傳輸開始前珊搀,通訊雙方進行身份認證、協(xié)商加密算法尾菇、交換加密密鑰等境析。
SSL提供的服務:
1)認證用戶和服務器,確保數(shù)據(jù)發(fā)送到正確的客戶機和服務器派诬;
2)加密數(shù)據(jù)以防止數(shù)據(jù)中途被竊壤拖;
3)維護數(shù)據(jù)的完整性默赂,確保數(shù)據(jù)在傳輸過程中不被改變沛鸵。
TLS:(Transport Layer Security,傳輸層安全協(xié)議)缆八,用于兩個應用程序之間提供保密性和數(shù)據(jù)完整性曲掰。該協(xié)議由兩層組成:TLS記錄協(xié)議 和 TLS握手協(xié)議疾捍。
SSL是Netscape開發(fā)的專門用戶保護Web通訊的,目前版本為3.0蜈缤。最新版本的TLS 1.0是IETF(工程任務組)制定的一種新的協(xié)議拾氓,它建立在SSL 3.0協(xié)議規(guī)范之上,是SSL 3.0的后續(xù)版本底哥。兩者差別極小咙鞍,可以理解為SSL 3.1,它是寫入了RFC的趾徽。
SSL的工作流程
服務器認證階段:
1续滋、客戶端向服務器發(fā)送一個開始信息 “hello” 指定SSL協(xié)議版本號、生成的隨機數(shù)(Client random)孵奶、客戶端支持的加密算法疲酌,以及其他信息,以便開始一個新的會話連接了袁;
2朗恳、服務器確認雙方使用的加密算法,向客戶端發(fā)送SSL協(xié)議版本號载绿、數(shù)字證書粥诫、生成的隨機數(shù)Server random)、其他信息崭庸;
3怀浆、客戶根據(jù)收到的服務器響應信息,驗證服務器的合法性怕享,包括:證書是否過期执赡,發(fā)行服務器證書的CA是否可靠,發(fā)行者證書的公鑰能否正確解開服務器證書的發(fā)行者數(shù)字簽名函筋,服務器證書上的域名是否和服務器的實際域名相匹配沙合。如果合法性驗證沒有通過,通訊將斷開驻呐;
4灌诅、如果合法性驗證通過,如果服務器要求客戶的身份認證(在握手過程中為可選)含末,客戶端隨機生成一個用于后續(xù)通信的 (預主密碼 Premaster secret),然后用服務器的公鑰(服務器提供的證書中有公鑰)對其加密即舌,隨機產生一個隨機數(shù)并對其進行數(shù)據(jù)簽名佣盒,將這個含有簽名的隨機數(shù)和客戶自己的證書以及“預主密碼” 一起傳遞給服務器。
如果不需要客戶端認證就只需要傳遞加密后的“預主密碼” 就可以
客戶認證階段:
1顽聂、驗證客戶證書和簽名加密的隨機數(shù)的合法性肥惭,包括:客戶的證書是否到期盯仪,為客戶提供證書的CA是否可靠,發(fā)行CA的公鑰能否解開客戶證書的發(fā)行 CA 的數(shù)字簽名蜜葱,檢查客戶的證書是否在證書廢止列表(CRL)中全景。
檢驗如果沒有通過,通訊立刻中斷牵囤;如果驗證通過爸黄,服務器將用自己的私鑰解開加密的 “預主密碼 ”,然后使用前面的三個隨機數(shù)揭鳞,執(zhí)行一系列步驟來生成"對話密鑰"(session key)客戶端也將通過同樣的方法產生相同的"對話密鑰"炕贵;
2、服務器和客戶端生成的相同的主密碼 即 “對話密鑰”野崇。一個對話密鑰用于 SSL 協(xié)議的安全數(shù)據(jù)通訊称开。同時在SSL通信過程中還要完成數(shù)據(jù)通訊的完整性,防止數(shù)據(jù)通訊中的任何變化乓梨。
3鳖轰、客戶端向服務器發(fā)出信息,指明后面的數(shù)據(jù)將使用主密碼為對稱密鑰扶镀,同時通知服務器客戶端的握手過程結束蕴侣。
4、服務器向客戶端發(fā)出信息狈惫,指明后面的數(shù)據(jù)將使用主密碼為對稱密鑰睛蛛,同時通知客戶端服務器的握手過程結束。
通信階段:
SSL握手部分結束后胧谈,SSL 安全通道的數(shù)據(jù)通訊開始忆肾,客戶與服務器開始使用相同的對稱密鑰進行數(shù)據(jù)通訊
握手之后的對話使用"對話密鑰"加密(對稱加密),服務器的公鑰和私鑰只用于加密和解密"對話密鑰"(非對稱加密)菱肖,無其他作用客冈。
TLS:安全傳輸層協(xié)議
TLS(Transport Layer Security Protocol)安全傳輸層協(xié)議,用于在兩個通信應用程序之間提供保密性和數(shù)據(jù)完整性稳强。該協(xié)議由兩層組成: TLS 記錄協(xié)議(TLS Record)和 TLS 握手協(xié)議(TLS Handshake)场仲。較低的層為 TLS 記錄協(xié)議,位于某個可靠的傳輸協(xié)議(例如 TCP)上面退疫。 TLS 記錄協(xié)議提供的連接安全性具有兩個基本特性:
1渠缕、私有 對稱加密用于數(shù)據(jù)加密,對稱加密所產生的密鑰對每個連接都是唯一的褒繁,且此密鑰基于另一個協(xié)議(如握手協(xié)議)協(xié)商亦鳞。記錄協(xié)議也可以不加密使用。
TSL和SSL的差異
1)版本號:TLS記錄格式與SSL記錄格式相同,但版本號的值不同燕差,TLS的版本1.0使用的版本號為SSLv3.1遭笋。
2)報文鑒別碼:SSLv3.0和TLS的MAC算法及MAC計算的范圍不同。TLS使用了RFC-2104定義的HMAC算法徒探。SSLv3.0使用了相似的算法瓦呼,兩者差別在于SSLv3.0中,填充字節(jié)與密鑰之間采用的是連接運算测暗,而HMAC算法采用的是異或運算央串。但是兩者的安全程度是相同的。
3)偽隨機函數(shù):TLS使用了稱為PRF的偽隨機函數(shù)來將密鑰擴展成數(shù)據(jù)塊偷溺,是更安全的方式蹋辅。
4)報警代碼:TLS支持幾乎所有的SSLv3.0報警代碼,而且TLS還補充定義了很多報警代碼挫掏,如解密失斦炝怼(decryption_failed)、記錄溢出(record_overflow)尉共、未知CA(unknown_ca)褒傅、拒絕訪問(access_denied)等。
5)密文族和客戶證書:SSLv3.0和TLS存在少量差別袄友,即TLS不支持Fortezza密鑰交換殿托、加密算法和客戶證書。
6)certificate_verify和finished消息:SSLv3.0和TLS在用certificate_verify和finished消息計算MD5和SHA-1散列碼時剧蚣,計算的輸入有少許差別支竹,但安全性相當。
7)加密計算:TLS與SSLv3.0在計算主密值(master secret)時采用的方式不同鸠按。
8)填充:用戶數(shù)據(jù)加密之前需要增加的填充字節(jié)礼搁。在SSL中,填充后的數(shù)據(jù)長度要達到密文塊長度的最小整數(shù)倍目尖。而在TLS中馒吴,填充后的數(shù)據(jù)長度可以是密文塊長度的任意整數(shù)倍(但填充的最大長度為255字節(jié)),這種方式可以防止基于對報文長度進行分析的攻擊瑟曲。
加解密基礎知識
對傳輸內容進行加密饮戳,是增加http通信安全的第一步。
可以采用對稱加密(DES洞拨,AES)扯罐,也可以采用非對稱加密(RSA,DSA)烦衣。此外還有數(shù)字簽名技術(MD5篮赢,SHA)齿椅。
對稱加密相比非對稱加密琉挖,速度更快启泣,需要的計算量更少。
數(shù)字證書 和 數(shù)字簽名
////////// 數(shù)字證書 ////////////////
數(shù)字證書是一個經(jīng)證書授權中心數(shù)字簽名的包含公開密鑰擁有者信息以及公開密鑰的文件示辈。最簡單的證書包含一個公開密鑰寥茫、名稱以及證書授權中心的數(shù)字簽名。數(shù)字證書還有一個重要的特征就是只在特定的時間段內有效矾麻。
數(shù)字證書是一種權威性的電子文檔纱耻,可以由權威公正的第三方機構,即CA(例如中國各地方的CA公司)中心簽發(fā)的證書险耀,也可以由企業(yè)級CA系統(tǒng)進行簽發(fā)弄喘。
* 證書:.crt .cer .pem
* 私鑰:.key
* 證書請求:.csr
////////// 數(shù)字簽名 ////////////////
將指定報文按雙方約定的HASH算法計算得到一個固定位數(shù)的報文摘要。在數(shù)學上保證:只要改動報文中任何一位甩牺,重新計算出的報文摘要值就會與原先的值不相符蘑志。這樣就保證了報文的不可更改性。
將該報文摘要值用發(fā)送者的私人密鑰加密贬派,然后連同原報文一起發(fā)送給接收者急但,而“加密”后的報文即稱數(shù)字簽名。
接收方收到數(shù)字簽名后搞乏,用同樣的HASH算法對原報文計算出報文摘要值波桩,然后與用發(fā)送者的公開密鑰對數(shù)字簽名進行解密得到的報文摘要值相比較。如相等則說明報文確實來自所稱的發(fā)送者请敦。
由于RSA加解密非常耗時镐躲,被加密的報文越大,耗得時間越多侍筛,因此對報文摘要進行加密萤皂,仍然能夠起到同樣的作用。這就是為什么多了個報文摘要勾笆。
Https通信流程
1敌蚜、當瀏覽器想服務器請求一個安全的網(wǎng)頁時,服務器就把他的數(shù)字證書和公鑰一起發(fā)送給瀏覽器窝爪;
2弛车、瀏覽器檢查證書是不是由可信賴機構頒發(fā)的,檢查證書的數(shù)字簽名是否正確蒲每,確認證書的有效和證書是屬于指定網(wǎng)站的纷跛;
3、瀏覽器使用證書中提供的公鑰加密一個隨機數(shù)生成對稱密鑰邀杏。并用這個對稱密鑰對Http協(xié)議請求內容進行加密贫奠,加密后的信息一起發(fā)送到服務器唬血;
4、服務器自己的私鑰解密瀏覽器發(fā)送過來的對稱密鑰唤崭,然后用這把對稱加密的密鑰解密加密過的Http協(xié)議請求內容
5拷恨、服務器用對稱密鑰對Https協(xié)議響應內容進行加密,并發(fā)送給瀏覽器谢肾,瀏覽器通過對稱密鑰解密信息腕侄,獲得響應內容。
https通信過程中使用到了對稱加密芦疏、非對稱加密和數(shù)字簽名技術冕杠。
其中非對稱加密,在目前已有算力的情況下是無法破解的酸茴。原理是分预,兩個大素數(shù)相乘十分容易,但想要對其乘積進行因式分解卻極其困難薪捍。這兩個大素數(shù)笼痹,就是非對稱加密中的公鑰、私鑰飘诗。
在未使用數(shù)字證書保證服務端的公鑰正確性時与倡,依然存在中間人攻擊的可能性,只要中間人作為客戶端同實際服務端進行通信昆稿,作為服務端同實際客戶端進行通信即可纺座。客戶端沒有任何方法判斷服務端發(fā)送過來的公鑰是否為真正的公鑰溉潭。于是在https通信中净响,使用到了數(shù)字證書,用于保證客戶端可以獲得正確的服務端公鑰喳瓣。
PKI體系
1馋贤、RSA身份驗證的隱患
身份驗證和密鑰協(xié)商是TLS的基礎功能畏陕,要求的前提是合法的服務器掌握著對應的私鑰配乓。但RSA算法無法確保服務器身份的合法性,因為公鑰并不包含服務器的信息惠毁,存在安全隱患:
客戶端C和服務器S進行通信犹芹,中間節(jié)點M截獲了二者的通信;
節(jié)點M自己計算產生一對公鑰pub_M和私鑰pri_M;
C向S請求公鑰時,M把自己的公鑰pub_M發(fā)給了C;
C使用公鑰 pub_M加密的數(shù)據(jù)能夠被M解密鞠绰,因為M掌握對應的私鑰pri_M腰埂,而 C無法根據(jù)公鑰信息判斷服務器的身份,從而 C和 M之間建立了"可信"加密連接;
中間節(jié)點 M和服務器S之間再建立合法的連接蜈膨,因此 C和 S之間通信被M完全掌握屿笼,M可以進行信息的竊聽牺荠、篡改等操作。
另外驴一,服務器也可以對自己的發(fā)出的信息進行否認休雌,不承認相關信息是自己發(fā)出。
因此該方案下至少存在兩類問題:中間人攻擊和信息抵賴蛔趴。
2挑辆、身份驗證CA和證書
解決上述身份驗證問題的關鍵是確保獲取的公鑰途徑是合法的,能夠驗證服務器的身份信息孝情,為此需要引入權威的第三方機構CA。CA 負責核實公鑰的擁有者的信息洒嗤,并頒發(fā)認證"證書"箫荡,同時能夠為使用者提供證書驗證服務,即PKI體系渔隶「岬玻基本的原理為,CA負責審核信息间唉,然后對關鍵信息利用私鑰進行"簽名"绞灼,公開對應的公鑰,客戶端可以利用公鑰驗證簽名呈野。CA也可以吊銷已經(jīng)簽發(fā)的證書低矮,基本的方式包括兩類 CRL 文件和 OCSP。CA使用具體的流程如下:
a.服務方S向第三方機構CA提交公鑰被冒、組織信息军掂、個人信息(域名)等信息并申請認證;
b.CA通過線上、線下等多種手段驗證申請者提供信息的真實性昨悼,如組織是否存在蝗锥、企業(yè)是否合法,是否擁有域名的所有權等;
c.如信息審核通過率触,CA會向申請者簽發(fā)認證文件-證書终议。
證書包含以下信息:申請者公鑰、申請者的組織信息和個人信息葱蝗、簽發(fā)機構 CA的信息穴张、有效時間、證書序列號等信息的明文垒玲,同時包含一個簽名;
簽名的產生算法:首先陆馁,使用散列函數(shù)計算公開的明文信息的信息摘要,然后合愈,采用 CA的私鑰對信息摘要進行加密叮贩,密文即簽名;
d.客戶端 C 向服務器 S 發(fā)出請求時击狮,S 返回證書文件;
e.客戶端 C讀取證書中的相關的明文信息,采用相同的散列函數(shù)計算得到信息摘要益老,然后彪蓬,利用對應 CA的公鑰解密簽名數(shù)據(jù),對比證書的信息摘要捺萌,如果一致档冬,則可以確認證書的合法性,即公鑰合法;
f.客戶端然后驗證證書相關的域名信息桃纯、有效時間等信息;
g.客戶端會內置信任CA的證書信息(包含公鑰)酷誓,如果CA不被信任,則找不到對應 CA的證書态坦,證書也會被判定非法盐数。
在這個過程注意幾點:
a.申請證書不需要提供私鑰,確保私鑰永遠只能服務器掌握;
b.證書的合法性仍然依賴于非對稱加密算法伞梯,證書主要是增加了服務器信息以及簽名;
c.內置 CA 對應的證書稱為根證書玫氢,頒發(fā)者和使用者相同,自己為自己簽名谜诫,即自簽名證書(為什么說"部署自簽SSL證書非常不安全")
d.證書=公鑰+申請者與頒發(fā)者信息+簽名;
★即便有人截取服務器A證書漾峡,再發(fā)給客戶端,想冒充服務器A喻旷,也無法實現(xiàn)生逸。因為證書和url的域名是綁定的。
3掰邢、證書鏈
? 如 CA根證書和服務器證書中間增加一級證書機構牺陶,即中間證書,證書的產生和驗證原理不變辣之,只是增加一層驗證掰伸,只要最后能夠被任何信任的CA根證書驗證合法即可。
a.服務器證書 server.pem 的簽發(fā)者為中間證書機構 inter怀估,inter 根據(jù)證書 inter.pem 驗證 server.pem 確實為自己簽發(fā)的有效證書;
b.中間證書 inter.pem 的簽發(fā) CA 為 root狮鸭,root 根據(jù)證書 root.pem 驗證 inter.pem 為自己簽發(fā)的合法證書;
c.客戶端內置信任 CA 的 root.pem 證書,因此服務器證書 server.pem 的被信任多搀。
服務器證書耍属、中間證書與根證書在一起組合成一條合法的證書鏈盅称,證書鏈的驗證是自下而上的信任傳遞的過程。
二級證書結構存在的優(yōu)勢:
a.減少根證書結構的管理工作量,可以更高效的進行證書的審核與簽發(fā);
b.根證書一般內置在客戶端中舍咖,私鑰一般離線存儲脖含,一旦私鑰泄露,則吊銷過程非常困難,無法及時補救;
c.中間證書結構的私鑰泄露锁蠕,則可以快速在線吊銷,并重新為用戶簽發(fā)新的證書;
d.證書鏈四級以內一般不會對 HTTPS 的性能造成明顯影響懊蒸。
證書鏈有以下特點:
a.同一本服務器證書可能存在多條合法的證書鏈荣倾。
因為證書的生成和驗證基礎是公鑰和私鑰對,如果采用相同的公鑰和私鑰生成不同的中間證書骑丸,針對被簽發(fā)者而言舌仍,該簽發(fā)機構都是合法的 CA,不同的是中間證書的簽發(fā)機構不同;
b.不同證書鏈的層級不一定相同通危,可能二級铸豁、三級或四級證書鏈。
中間證書的簽發(fā)機構可能是根證書機構也可能是另一個中間證書機構黄鳍,所以證書鏈層級不一定相同推姻。
4、證書吊銷
? CA 機構能夠簽發(fā)證書框沟,同樣也存在機制宣布以往簽發(fā)的證書無效。證書使用者不合法增炭,CA 需要廢棄該證書;或者私鑰丟失忍燥,使用者申請讓證書無效。主要存在兩類機制:CRL 與 OCSP隙姿。
a.CRL
? Certificate Revocation List, 證書吊銷列表(什么是證書吊銷列表(CRL)梅垄?吊銷列表起什么作用),一個單獨的文件输玷。該文件包含了 CA 已經(jīng)吊銷的證書序列號(唯一)與吊銷日期队丝,同時該文件包含生效日期并通知下次更新該文件的時間,當然該文件必然包含 CA 私鑰的簽名以驗證文件的合法性欲鹏。
證書中一般會包含一個 URL 地址 CRL Distribution Point机久,通知使用者去哪里下載對應的 CRL 以校驗證書是否吊銷。該吊銷方式的優(yōu)點是不需要頻繁更新赔嚎,但是不能及時吊銷證書膘盖,因為 CRL 更新時間一般是幾天,這期間可能已經(jīng)造成了極大損失尤误。
b.OCSP
Online Certificate Status Protocol, 證書狀態(tài)在線查詢協(xié)議侠畔,一個實時查詢證書是否吊銷的方式。請求者發(fā)送證書的信息并請求查詢损晤,服務器返回正常软棺、吊銷或未知中的任何一個狀態(tài)。證書中一般也會包含一個 OCSP 的 URL 地址尤勋,要求查詢服務器具有良好的性能喘落。部分 CA 或大部分的自簽 CA (根證書)都是未提供 CRL 或 OCSP 地址的茵宪,對于吊銷證書會是一件非常麻煩的事情。
HTTPS性能優(yōu)化
1揖盘、HTTPS性能損耗
前文討論了HTTPS原理與優(yōu)勢:身份驗證眉厨、信息加密與完整性校驗等,且未對TCP和HTTP協(xié)議做任何修改兽狭。但通過增加新協(xié)議以實現(xiàn)更安全的通信必然需要付出代價憾股,HTTPS協(xié)議的性能損耗主要體現(xiàn)如下:
(1).增加延時
? 分析前面的握手過程,一次完整的握手至少需要兩端依次來回兩次通信箕慧,至少增加延時2* RTT服球,利用會話緩存從而復用連接,延時也至少1* RTT*颠焦。
(2).消耗較多的CPU資源
除數(shù)據(jù)傳輸之外斩熊,HTTPS通信主要包括對對稱加解密、非對稱加解密(服務器主要采用私鑰解密數(shù)據(jù));壓測 TS8 機型的單核 CPU:對稱加密算法AES-CBC-256 吞吐量 600Mbps伐庭,非對稱 RSA 私鑰解密200次/s粉渠。不考慮其它軟件層面的開銷,10G 網(wǎng)卡為對稱加密需要消耗 CPU 約17核圾另,24核CPU最多接入 HTTPS 連接 4800;
靜態(tài)節(jié)點當前10G 網(wǎng)卡的 TS8 機型的 HTTP 單機接入能力約為10w/s霸株,如果將所有的HTTP連接變?yōu)镠TTPS連接,則明顯RSA的解密最先成為瓶頸集乔。因此去件,RSA的解密能力是當前困擾HTTPS接入的主要難題。
2扰路、HTTPS接入優(yōu)化
(1).CDN接入
? HTTPS 增加的延時主要是傳輸延時 RTT尤溜,RTT 的特點是節(jié)點越近延時越小,CDN 天然離用戶最近汗唱,因此選擇使用 CDN 作為 HTTPS 接入的入口宫莱,將能夠極大減少接入延時。CDN 節(jié)點通過和業(yè)務服務器維持長連接渡嚣、會話復用和鏈路質量優(yōu)化等可控方法梢睛,極大減少 HTTPS 帶來的延時。
(2).會話緩存
? 雖然前文提到 HTTPS 即使采用會話緩存也要至少1*RTT的延時识椰,但是至少延時已經(jīng)減少為原來的一半绝葡,明顯的延時優(yōu)化;同時,基于會話緩存建立的 HTTPS 連接不需要服務器使用RSA私鑰解密獲取 Pre-master 信息腹鹉,可以省去CPU 的消耗藏畅。如果業(yè)務訪問連接集中,緩存命中率高,則HTTPS的接入能力講明顯提升愉阎。當前TRP平臺的緩存命中率高峰時期大于30%绞蹦,10k/s的接入資源實際可以承載13k/的接入,收效非嘲竦可觀幽七。
(3).硬件加速
? 為接入服務器安裝專用的SSL硬件加速卡,作用類似 GPU溅呢,釋放 CPU澡屡,能夠具有更高的 HTTPS 接入能力且不影響業(yè)務程序的。測試某硬件加速卡單卡可以提供35k的解密能力咐旧,相當于175核 CPU驶鹉,至少相當于7臺24核的服務器,考慮到接入服務器其它程序的開銷铣墨,一張硬件卡可以實現(xiàn)接近10臺服務器的接入能力室埋。
(4).遠程解密
? 本地接入消耗過多的 CPU 資源,浪費了網(wǎng)卡和硬盤等資源伊约,考慮將最消耗 CPU 資源的RSA解密計算任務轉移到其它服務器姚淆,如此則可以充分發(fā)揮服務器的接入能力,充分利用帶寬與網(wǎng)卡資源屡律。遠程解密服務器可以選擇 CPU 負載較低的機器充當肉盹,實現(xiàn)機器資源復用,也可以是專門優(yōu)化的高計算性能的服務器疹尾。當前也是 CDN 用于大規(guī)模HTTPS接入的解決方案之一。
(5).SPDY/HTTP2
? 前面的方法分別從減少傳輸延時和單機負載的方法提高 HTTPS 接入性能骤肛,但是方法都基于不改變 HTTP 協(xié)議的基礎上提出的優(yōu)化方法纳本,SPDY/HTTP2 利用 TLS/SSL 帶來的優(yōu)勢,通過修改協(xié)議的方法來提升 HTTPS 的性能腋颠,提高下載速度等繁成。
Port 和 Socket了解
Port(端口)
伴隨著傳輸層誕生的概念。它可以將網(wǎng)絡層的IP通信分送到各個通信通道淑玫。UDP協(xié)議和TCP協(xié)議盡管在工作方式上有很大的不同巾腕,但它們都建立了從一個端口到另一個端口的通信。
Socket (套接字)
Socket的作用:提供網(wǎng)絡通信的能力
Socket是什么
Socket是對TCP/IP協(xié)議的封裝絮蒿,它的出現(xiàn)只是使得程序員更方便地使用TCP/IP協(xié)議棧而已尊搬。Socket本身并不是協(xié)議,它是應用層與TCP/IP協(xié)議族通信的中間軟件抽象層(在上圖的TCP土涝、IP四層網(wǎng)絡模型中佛寿,Socket就是介于 應用層和傳輸層中間的抽象層。)但壮,是一組調用接口(TCP/IP網(wǎng)絡的API函數(shù))冀泻。
TCP/IP只是一個協(xié)議棧常侣,就像操作系統(tǒng)的運行機制一樣,必須要具體實現(xiàn)弹渔,同時還要提供對外的操作接口胳施。
這個就像操作系統(tǒng)會提供標準的編程接口,比如win32編程接口一樣肢专。
TCP/IP也要提供可供程序員做網(wǎng)絡開發(fā)所用的接口舞肆,這就是Socket編程接口
Socket的實現(xiàn)原理
套接字(socket)是通信的基石,是支持TCP/IP協(xié)議的網(wǎng)絡通信的基本操作單元鸟召。它是網(wǎng)絡通信過程中端點的抽象表示胆绊,包含進行網(wǎng)絡通信必須的五種信息:連接使用的協(xié)議,本地主機的IP地址欧募,本地進程的協(xié)議端口压状,遠地主機的IP地址,遠地進程的協(xié)議端口跟继。一個socket句柄(文件描述符)代表兩個地址對:本地ip:port -- 遠程:port
應用層通過傳輸層進行數(shù)據(jù)通信時种冬,TCP會遇到同時為多個應用程序進程提供并發(fā)服務的問題。多個TCP連接或多個應用程序進程可能需要通過同一個 TCP協(xié)議端口傳輸數(shù)據(jù)舔糖。為了區(qū)別不同的應用程序進程和連接娱两,許多計算機操作系統(tǒng)為應用程序與TCP/IP協(xié)議交互提供了套接字(Socket)接口。應用層可以和傳輸層通過Socket接口金吗,區(qū)分來自不同應用程序進程或網(wǎng)絡連接的通信十兢,實現(xiàn)數(shù)據(jù)傳輸?shù)牟l(fā)服務
Socket之間是如何通信
建立Socket連接至少需要一對套接字,其中一個運行于客戶端摇庙,稱為ClientSocket旱物,另一個運行于服務器端,稱為ServerSocket卫袒。
套接字之間的連接過程分為三個步驟:服務器監(jiān)聽宵呛,客戶端請求,連接確認夕凝。
服務器監(jiān)聽:服務器端套接字并不定位具體的客戶端套接字宝穗,而是處于等待連接的狀態(tài),實時監(jiān)控網(wǎng)絡狀態(tài)码秉,等待客戶端的連接請求逮矛。
客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字泡徙。為此橱鹏,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然后就向服務器端套接字提出連接請求莉兰。
連接確認:當服務器端套接字監(jiān)聽到或者說接收到客戶端套接字的連接請求時挑围,就響應客戶端套接字的請求,建立一個新的線程糖荒,把服務器端套接字的描述發(fā)給客戶端杉辙,一旦客戶端確認了此描述,雙方就正式建立連接捶朵。而服務器端套接字繼續(xù)處于監(jiān)聽狀態(tài)蜘矢,繼續(xù)接收其他客戶端套接字的連接請求。
iOS中的Socket編程
sys/socket中的用法
客戶端:
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@implementation ViewController {
int _clientSocket; //nc -lk 1024
IBOutlet UITextField *mytext;
}
- (void)viewDidLoad {
[super viewDidLoad];
//建立連接
_clientSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(1024);
addr.sin_addr.s_addr = inet_addr(@"192.168.0.116".UTF8String);
int connectResult = connect(_clientSocket, (const struct sockaddr *)&addr, sizeof(addr));
if (connectResult == 0) {
mytext.text = @"連接成功";
} else {
mytext.text = @"連接失敗";
}
}
- (IBAction)send:(id)sender {
//發(fā)送消息和監(jiān)聽消息
dispatch_queue_t queue = dispatch_queue_create("并發(fā)", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
const char *str = @"我是小白".UTF8String;
ssize_t sendLen = send(_clientSocket, str, strlen(str), 0);
char *buf[1024];
ssize_t recvLen = recv(_clientSocket, buf, sizeof(buf), 0);
NSString *recvStr = [[NSString alloc]initWithBytes:buf length:recvLen encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
mytext.text = recvStr;
});
});
}
@end
//服務器
#import "ViewController.h"
#import <arpa/inet.h>
#import <netinet/in.h>
#import <sys/socket.h>
@interface ViewController ()
//監(jiān)聽到的客戶端ip地址
@property (weak, nonatomic) IBOutlet UILabel *client_ip;
//監(jiān)聽到的客戶端端口
@property (weak, nonatomic) IBOutlet UILabel *client_port;
//服務器手動發(fā)送消息
@property (weak, nonatomic) IBOutlet UITextField *server_sendMSG;
//顯示客戶端發(fā)來的消息
@property (weak, nonatomic) IBOutlet UITextView *client_showMSG;
//連接狀態(tài)
@property (nonatomic, weak) IBOutlet UILabel * status;
//監(jiān)聽按鈕點擊
@property (weak, nonatomic) IBOutlet UIButton *connectBtn;
//記錄按鈕狀態(tài)
@property (nonatomic,assign) int flag;
@end
@implementation ViewController
{
int _serverSocket;
int _clientSocket;
}
- (void)viewDidLoad {
[super viewDidLoad];
//開啟服務
[self startServer];
}
- (void)startServer{
//按鈕監(jiān)聽是否啟動服務
[self.connectBtn addTarget:self action:@selector(connectBtnEvent:) forControlEvents:UIControlEventTouchUpInside];
}
#pragma mark - 建立監(jiān)聽
- (void)connectAndlistenPort:(int)port{
_serverSocket=socket(AF_INET, SOCK_STREAM , IPPROTO_TCP);
//如果返回值不為-1,則成功
if(_serverSocket != -1){
NSLog(@"socket success");
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));//清零操作
addr.sin_len=sizeof(addr);
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=INADDR_ANY;
//綁定地址和端口號
int bindAddr = bind(_serverSocket, (const struct sockaddr *)&addr, sizeof(addr));
//開始監(jiān)聽
if (bindAddr == 0) {
NSLog(@"bind(綁定) success");
int startListen = listen(_serverSocket, 5);//5為等待連接數(shù)目
if(startListen == 0){
NSLog(@"listen success");
//回到主線程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.status.text = @"監(jiān)聽成功";
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
self.status.text = @"監(jiān)聽失敗";
});
}
}
}
}
#pragma mark - 阻塞直到客戶端連接
- (void)accept{
struct sockaddr_in peeraddr;
socklen_t addrLen;
addrLen=sizeof(peeraddr);
NSLog(@"prepare accept");
//接受到客戶端clientSocket連接,獲取到地址和端口
int clientSocket=accept(_serverSocket, (struct sockaddr *)&peeraddr, &addrLen);
_clientSocket = clientSocket;
if (clientSocket != -1) {
NSLog(@"accept success,remote address:%s,port:%d",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
//回到主線程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.client_ip.text =[NSString stringWithUTF8String:inet_ntoa(peeraddr.sin_addr)];
self.client_port.text = [NSString stringWithFormat:@"%d",ntohs(peeraddr.sin_port)];
});
char buf[1024];
size_t len=sizeof(buf);
//接受到客戶端消息
recv(clientSocket, buf, len, 0);
NSString* str = [NSString stringWithCString:buf encoding:NSUTF8StringEncoding];
//主線程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.client_showMSG.text = str;
NSLog(@"%@",str);
});
}
}
#pragma mark - 關閉socket
- (void)connectBtnEvent:(UIButton *)sender {
if (self.flag==0) {
[self.connectBtn setTitle:@"斷開連接" forState:UIControlStateNormal];
dispatch_queue_t SERIAL_QUEUE = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);
dispatch_async(SERIAL_QUEUE, ^{
self.flag=1;
[self connectAndlistenPort:1024];
while (self.flag) {
//掃描客戶端連接
[self accept];
}
});
}else{
[self.connectBtn setTitle:@"啟動服務器" forState:UIControlStateNormal];
self.status.text = @"監(jiān)聽失敗";
shutdown(_clientSocket, SHUT_RDWR);
shutdown(_serverSocket, SHUT_RDWR);
close(_clientSocket);
close(_serverSocket);
self.flag=0;
}
}
#pragma mark - 發(fā)送消息
- (IBAction)sendBtn:(UIButton *)sender {
[self sentAndRecv:_clientSocket msg:_server_sendMSG.text];
}
//發(fā)送數(shù)據(jù)并等待返回數(shù)據(jù)
- (void)sentAndRecv:(int)clientSocket msg:(NSString *)msg {
dispatch_queue_t q_con = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q_con, ^{
const char *str = msg.UTF8String;
send(clientSocket, str, strlen(str), 0);
char *buf[1024];
ssize_t recvLen = recv(clientSocket, buf, sizeof(buf), 0);
NSString *recvStr = [[NSString alloc] initWithBytes:buf length:recvLen encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
self.client_showMSG.text = recvStr;
});
});
}
@end
GCDAsynSocket
//客戶端综看,具體使用百度一下
@implementation ViewController {
IBOutlet UITextField *mytext;
GCDAsyncSocket *clientSocket;
}
- (void)viewDidLoad {
[super viewDidLoad];
clientSocket = [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError * error = nil;
BOOL result = [clientSocket connectToHost:@"192.168.0.116" onPort:1024 error:&error];
if (result) {
mytext.text = @"連接成功";
} else {
mytext.text = @"連接失敗";
}
[clientSocket readDataWithTimeout:- 1 tag:0];
}
- (IBAction)send:(id)sender {
NSData *data = [@"測試數(shù)據(jù)" dataUsingEncoding:NSUTF8StringEncoding];
[clientSocket writeData:data withTimeout: -1 tag:0];
}
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSString *text = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
mytext.text = text;
}
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
mytext.text = [NSString stringWithFormat:@"服務器IP: %@-------端口: %d", host,port];
[clientSocket readDataWithTimeout:- 1 tag:0];
}
@end
Socket連接和Http連接的區(qū)別
1品腹、Http 是基于 Tcp 的,而Socket是一套編程接口讓我們更方便的使用Tcp/Ip協(xié)議
2红碑、Http是基于"請求-響應"的舞吭,服務器不能主動向客戶端推送數(shù)據(jù),只能借助客戶端請求到后向客戶端推送數(shù)據(jù)析珊,而Sokcet雙方隨時可以互發(fā)數(shù)據(jù)
3羡鸥、Http不是持久連接的,Socket用Tcp是持久連接 (聊天室功能)
4忠寻、Http基于Tcp惧浴,Socket可以基于Tcp/Udp
5、Http連接是通過Socket實現(xiàn)的
6奕剃、Http連接后發(fā)送的數(shù)據(jù)必須滿足Http協(xié)議規(guī)定的格式:請求頭衷旅、請求頭和請求體,而Socket連接后發(fā)送的數(shù)據(jù)沒有格式要求