第五章 網絡 之 計算機網絡基礎(一)

文章目錄

(一)計算機網絡基礎知識:從一次完整的網絡請求過程分析

(1)域名解析

1.1)域名與ip地址

(1)ip地址:ip地址是一個32位(4字節)的二進制數(IPV4),常見格式爲:192.168.1.101.IP地址是IP協議移動的一種統一的地址格式,爲互聯網上每一個網絡和每一臺主機分配一個邏輯地址.
IPV4與IPV6的區別:IPv4 使用32位(4字節)地址,因此只有 4,294,967,296 個,但隨着聯網設備的增加,這些地址顯然是不夠用的,所以需要新的協議和更多的地址。IPv6 便是這個新的協議,IPV6有128位(8字節)地址,有2^128-1個地址,IPv6 的目的皆在解決 IPv4 枯竭的問題。
(2)域名:ip地址與域名是一對多的關係。一個ip地址可以對應多個域名,但是一個域名只有一個ip地址。ip地址是數字組成的,不方便記憶,所以有了域名。

1.2)域名系統(DNS——Domain Name System)

域名雖然便於人們記憶,但機器之間只能互相認識IP地址,它們之間的轉換工作稱爲域名解析,域名解析需要由專門的域名解析服務器來完成,DNS就是進行域名解析的服務器。域名的最終指向是IP。
將主機名和域名轉換爲IP地址。(域名:www.qq.com->119.147.15.13)

a)DNS本質

用於TCP/IP應用程序的數據庫,該數據庫中記錄了域名和IP的對應關係,同時也是一種用於客戶端和服務端通訊的應用層的計算機網絡協議。

b)域名的特點

1、分佈式:將域名服務器分佈式存儲於不同計算機中,目前全球有13臺根服務器
2、階層式:域名格式採用分層結構,類似樹狀結構,一棵獨立的DNS子樹就是一個區域(zone),一個區域被委派了授權機構(唯一授權機構:網絡信息中心NIC),該機構需搭建DNS服務區,記錄該區域下的子域名和IP對應關係,並且該授權機構可以再委派該區域下的子區域的DNS系統,依此向下委派,形成階梯式管理結構
3、建立緩存:當一個DNS服務器查詢到域名和IP的映射關係後,會將該映射數據寫入自己的緩存中,如果其他的主機再來詢問相同的映射關係時,直接讀取自己的緩存,而不需要再去詢問其他服務器了

c)一次請求域名解析過程

迭代查詢(服務端)和遞歸查詢(客戶端)(區別:遞歸是查詢者變化,迭代是查詢者不變)

以查詢 zh.wikipedia.org 爲例:

  • 輸入域名"zh.wikipedia.org",操作系統會先檢查自己的本地hosts文件是否有這個網址映射關係。如果hosts沒有這個域名的映射,則查詢本地DNS解析器緩存。如果hosts與本地DNS服務器緩存都沒有相應的網址映射關係,首先會找TCP/IP參數中設置的首選DNS服務器。
  • 客戶端發送查詢報文"query zh.wikipedia.org"至DNS服務器,DNS服務器首先檢查自身緩存,如果存在記錄則直接返回結果。
  • 如果記錄老化或不存在,則:
    (1)DNS服務器向根域名服務器發送查詢報文"query zh.wikipedia.org",根域名服務器返回頂級域.org 的權威域名服務器地址。
    (2)DNS服務器向 .org 域的權威域名服務器發送查詢報文"query zh.wikipedia.org",得到二級域.wikipedia.org 的權威域名服務器地址。
    (3)DNS服務器向 .wikipedia.org 域的權威域名服務器發送查詢報文"query zh.wikipedia.org",得到主機 zh 的A記錄,存入自身緩存並返回給客戶端。
    在這裏插入圖片描述

在這裏插入圖片描述

1.3)以Chrome瀏覽器爲例,Chrome解析域名對應的IP地址

  • Chrome瀏覽器會首先搜索瀏覽器自身的DNS緩存(可以使用 chrome://net-internals/#dns 來進行查看),瀏覽器自身的DNS緩存有效期比較短,且容納有限,大概是1000條。如果自身的緩存中存在blog.csdn.net 對應的IP地址並且沒有過期,則解析成功。
  • 如果(1)中未找到,那麼Chrome會搜索操作系統自身的DNS緩存(可以在命令行下使用 ipconfig /displaydns 查看)。如果找到且沒有過期則成功。
  • 如果(2)中未找到,那麼嘗試讀取位於C:\Windows\System32\drivers\etc下的hosts文件,如果找到對應的IP地址則解析成功。
  • 如果(3)中未找到,瀏覽器首先會找TCP/IP參數中設置的本地DNS服務器,並請求LDNS服務器來解析這個域名,這臺服務器一般在你的城市的某個角落,距離你不會很遠,並且這臺服務器的性能都很好,一般都會緩存域名解析結果,大約80%的域名解析到這裏就完成了。否則本地DNS服務器會請求根DNS服務器。
    -本地DNS會把請求發至13臺根DNS,根DNS服務器會返回所查詢域的主域名服務器的地址(.net),本地DNS服務器使用該IP信息聯繫負責.net域的這臺服務器。這臺負責.net域的服務器收到請求後,會返回.net域的下一級DNS服務器地址(blog.csdn.net)給本地DNS服務器。以此類推,直至找到。

(2)TCP的三次握手四次揮手

2.1)準備知識

(1)ACK ,TCP協議規定只有ACK=1時有效,也規定連接建立後所有發送的報文的ACK必須爲1。
(2)SYN,在連接建立時用來同步序號。當SYN=1而ACK=0時,表明這是一個連接請求報文。對方若同意建立連接,則應在響應報文中使SYN=1和ACK=1,因此SYN置1就表示這是一個連接請求或連接接受報文。
(3)FIN,用來釋放一個連接。當 FIN = 1 時,表明此報文段的發送方的數據已經發送完畢,並要求釋放連接。

2.2)三次握手

最初兩端的TCP進程都處於CLOSED關閉狀態,A主動打開連接,而B被動打開連接(由客戶端執行connect觸發)並開始監聽。
(1)第一次握手:由Client發出請求連接數據包: SYN=1 ACK=0 seq=x,TCP規定SYN=1時不能攜帶數據,但要消耗一個序號,因此聲明自己的序號是 seq=x,此時Client進入SYN-SENT(同步已發送)狀態,等待Server確認;
(2)第二次握手:Server收到請求報文後,進行回覆確認,即 SYN=1 ACK=1 seq=y,ack=x+1,此時Server進入SYN-RCVD(同步收到)狀態,操作系統爲TCP連接分配TCP緩存和變量;
(3)第三次握手:Client收到Server的確認(SYN+ACK)後,向Server發出確認報文段,即 ACK=1,seq=x+1, ack=y+1,TCP連接已經建立,Client進入ESTABLISHED(已建立連接)狀態;
Server收到Client的確認後,也進入ESTABLISHED狀態,完成三次握手;此時Client和Server可以開始傳輸數據。
在這裏插入圖片描述

2.3)四次揮手

數據傳輸結束後,通信的雙方都可釋放連接,A和B都處於ESTABLISHED狀態。當Client沒有數據再需要發送給服務端時,就需要釋放客戶端的連接,整個過程爲:
(1)第一次揮手:當Client發起終止連接請求的時候,會發送一個(FIN爲1,seq=u)的沒有數據的報文,這時Client停止發送數據(但仍可以接受數據) ,進入FIN_WAIT1(終止等待1)狀態,等待Server確認
(2)第二次揮手:Server收到連接釋放報文後會給Client一個確認報文段(ACK=1,ack=u+1,seq=v), 進入CLOSE-WAIT(關閉等待)狀態
Client收到Server的確認後進入FIN_WAIT2狀態,等待Server請求釋放連接,Server仍可向Client發送數據。
(3)第三次揮手:Server數據發送完成後,向Client發起請求連接釋放報文(FIN=1,ACK=1,seq=w,ack = u+1),Server進入LAST-ACK(最後確認)狀態,等待Client確認
(4)第四次揮手:Client收到連接釋放報文段後,回覆一個確認報文段(ACK=1,seq=u+1,ack=w+1),進入 TIME_WAIT(時間等待) 狀態,Server收到後進入CLOSED(連接關閉)狀態。經過等待2MSL 時間(最大報文生存時間),Client進入CLOSED狀態。
在這裏插入圖片描述

2.4)面試問題

(1)三次握手中爲什麼Client最後還要發送一次確認呢?可以二次握手嗎?=TCP三次握手的主要目的
回答:
爲了實現可靠數據傳輸, TCP 協議的通信雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟
如果只是兩次握手, 至多隻有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認
1.1)爲什麼需要握手?
這裏就引出了 TCP 與 UDP 的一個基本區別, TCP 是可靠通信協議, 而 UDP 是不可靠通信協議。
TCP 的可靠性含義: 接收方收到的數據是完整, 有序, 無差錯的。
UDP 不可靠性含義: 接收方接收到的數據可能存在部分丟失, 順序也不一定能保證。
UDP 和 TCP 協議都是基於同樣的互聯網基礎設施, 且都基於 IP 協議實現, 互聯網基礎設施中對於數據包的發送過程是會發生丟包現象的, 爲什麼 TCP 就可以實現可靠傳輸, 而 UDP 不行?
TCP 協議爲了實現可靠傳輸, 通信雙方需要判斷自己已經發送的數據包是否都被接收方收到, 如果沒收到, 就需要重發。 爲了實現這個需求, 很自然地就會引出序號(sequence number) 和 確認號(acknowledgement number) 的使用。
發送方在發送數據包(假設大小爲 10 byte)時, 同時送上一個序號( 假設爲 500),那麼接收方收到這個數據包以後, 就可以回覆一個確認號(510 = 500 + 10) 告訴發送方 “我已經收到了你的數據包, 你可以發送下一個數據包, 序號從 510 開始” 。
這樣發送方就可以知道哪些數據被接收到,哪些數據沒被接收到, 需要重發。
1.2)爲什麼需要三次握手?
正如上文所描述的,爲了實現可靠傳輸,發送方和接收方始終需要同步( SYNchronize )序號。 需要注意的是, 序號並不是從 0 開始的, 而是由發送方隨機選擇的初始序列號 ( Initial Sequence Number, ISN )開始 。 由於 TCP 是一個雙向通信協議, 通信雙方都有能力發送信息, 並接收響應。 因此, 通信雙方都需要隨機產生一個初始的序列號, 並且把這個起始值告訴對方。
1.3)三次握手(理解版)
我們假設A和B是通信的雙方。我理解的握手實際上就是通信,發一次信息就是進行一次握手。則對於三次握手:
第一次握手: A給B打電話說,你可以聽到我說話嗎?
第二次握手: B收到了A的信息,然後對A說: 我可以聽得到你說話啊,你能聽得到我說話嗎?
第三次握手: A收到了B的信息,然後說可以的,我要給你發信息啦!
在三次握手之後,A和B都能確定這麼一件事: 我說的話,你能聽到; 你說的話,我也能聽到。 這樣,就可以開始正常通信了。
如果兩次,那麼B無法確定B的信息A是否能收到,所以如果B先說話,可能後面的A都收不到,會出現問題 。
如果四次,那麼就造成了浪費,因爲在三次結束之後,就已經可以保證A可以給B發信息,A可以收到B的信息; B可以給A發信息,B可以收到A的信息。
1.4)四次揮手(理解版)
同理,對於四次揮手:
A:“喂,我不說了 (FIN)。”A->FIN_WAIT1
B:“我知道了(ACK)。等下,上一句還沒說完。Balabala……(傳輸數據)”B->CLOSE_WAIT | A->FIN_WAIT2
B:”好了,說完了,我也不說了(FIN)。”B->LAST_ACK
A:”我知道了(ACK)。”A->TIME_WAIT | B->CLOSED
A等待2MSL,保證B收到了消息,否則重說一次”我知道了”,A->CLOSED
這樣,通過四次揮手,可以把該說的話都說完,並且A和B都知道自己沒話說了,對方也沒花說了,然後就掛掉電話(斷開鏈接)了 。
(2)Server端易受到SYN攻擊?
服務器端的資源分配是在二次握手時分配的,而客戶端的資源是在完成三次握手時分配的,所以服務器容易受到SYN洪泛攻擊,SYN攻擊就是Client在短時間內僞造大量不存在的IP地址,並向Server不斷地發送SYN包,Server則回覆確認包,並等待Client確認,由於源地址不存在,因此Server需要不斷重發直至超時,這些僞造的SYN包將長時間佔用未連接隊列,導致正常的SYN請求因爲隊列滿而被丟棄,從而引起網絡擁塞甚至系統癱瘓。
防範SYN攻擊措施:降低主機的等待時間使主機儘快的釋放半連接的佔用,短時間受到某IP的重複SYN則丟棄後續請求。
(3)爲什麼A在TIME-WAIT狀態必須等待2MSL的時間?
MSL最長報文段壽命Maximum Segment Lifetime,MSL=2
兩個理由:1)保證A發送的最後一個ACK報文段能夠到達B。2)防止“防止本次已失效的連接請求報文段出現在新的連接中。
1)這個ACK報文段有可能丟失,使得處於LAST-ACK狀態的B收不到對已發送的FIN+ACK報文段的確認,B超時重傳FIN+ACK報文段,而A能在2MSL時間內收到這個重傳的FIN+ACK報文段,接着A重傳一次確認,重新啓動2MSL計時器,最後A和B都進入到CLOSED狀態,若A在TIME-WAIT狀態不等待一段時間,而是發送完ACK報文段後立即釋放連接,則無法收到B重傳的FIN+ACK報文段,所以不會再發送一次確認報文段,則B無法正常進入到CLOSED狀態。
2)A在發送完最後一個ACK報文段後,再經過2MSL,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失,使下一個新的連接中不會出現這種舊的連接請求報文段。
(4)爲什麼連接的時候是三次握手,關閉的時候卻是四次握手?
因爲當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET(服務端數據未傳輸完畢),FIN報文僅僅表示Client沒有需要發送的數據,但是仍能接受數據,Server的數據未必全部發送出去,需要等待Server的數據發送完畢後發送FIN報文給Client才能表示同意關閉連接。
所以只能先回復一個ACK報文,告訴Client端,“你發的FIN報文我收到了”。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。

建立連接時候ACK和SYN可以放在一個報文裏發送給客戶端。
連接關閉時ACK和FIN一般分開發送。

(5)如果已經建立了連接,但是客戶端突然出現故障了怎麼辦?
TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置爲2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75秒鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉連接。
(6)更多知識點?
5.1通過序列號與確認應答提高可靠性
5.2重發超時的確定提高可靠性
5.3利用窗口控制(滑動窗口控制、窗口控制中重發機制)提高速度

2.5)整體通信流程

在這裏插入圖片描述

(3)建立TCP連接後發起HTTP請求,服務器響應HTTP請求

3.1)網絡協議

網絡協議是網絡上所有設備(網絡服務器、計算機及交換機、路由器、防火牆等)之間通信規則的集合,它規定了進行網絡中的對等實體數據交換而建立的規則。由於大多數網絡採用分層的體系結構,網絡各層發送方與接收方(對等實體)應該用相同的網絡協議才能相互交換信息。
Internet上的計算機採用TCP/IP協議。

3.2)TCP/IP協議(互聯網協議)

在這裏插入圖片描述

(1)TCP/IP四層模型與OSI七層模型

a)OSI七層模型(法定標準)
1、模型架構

在7層模型中,每一層都提供一個特殊的網絡功能。
下面3層(物理層、數據鏈路層、網絡層)主要提供數據傳輸和交換功能,即以節點到節點之間的通信爲主(通信子網)
傳輸層作爲上下兩部分的橋樑,是整個網絡體系結構中最關鍵的部分;
上面3層(會話層、表示層和應用層)則以提供用戶與應用程序之間的信息和數據處理功能爲主(資源子網)

2、通信過程(打包+拆包)
3、各層功能及協議

1)應用層
定義:計算機用戶及各種應用程序和網絡之間的接口
功能:
1.直接向用戶提供服務接口
2.完成用戶請求的各種服務
協議:文件傳輸FTP,電子郵件SMTP,萬維網HTTP…
2)表示層
定義:處理在兩個通信系統中交換信息的表示方式,如編碼、數據格式轉換和加密解密等
功能:
1.數據格式處理2.數據編碼3.壓縮和解壓縮4.數據的加密和解密
3)會話層
定義:向兩個實體的表示層提供建立和使用連接的方法。將不同實體之間的表示層的連接稱爲會話。因此會話層的任務就是組織和協調兩個會話進程之間的通信,並對數據交換進行管理。
功能:
1.會話管理:建立、管理、終止會話
2.出錯控制:使用校驗點可使會話在通信失效時從校驗點/同步點繼續恢復通信,實現數據同步。適用於傳輸大文件
4)傳輸層(通信子網(數據處理)和資源子網(數據傳輸)的橋樑)
定義:向用戶提供可靠的、端到端((每個進程都用一個端口號唯一標識)的差錯和流量控制,保證報文的正確傳輸。傳輸單位是報文段或用戶數據報
功能:
1.傳輸連接管理:提供建立、連接和拆除傳輸連接的功能。傳輸層在網絡層的基礎上,提供“面向連接TCP”和“面向無連接UDP”兩種服務
2.處理傳輸差錯:(1)差錯控制(2)流量控制
協議:TCP\UDP
5)網絡層
定義:通過路由算法,爲報文或分組通過通信子網選擇最適當的路徑,從源端傳到目的端,爲不同的網絡設備提供通信服務。該層控制數據鏈路層與物理層之間的信息轉發,建立、維持與終止網絡的連接。傳輸單位是數據報。
一般的,數據鏈路層是解決統一網絡內節點之間的通信,而網絡層主要解決不同子網之間的通信。例如路由選擇問題。
功能:
1、尋址:數據鏈路層中使用的物理地址(如MAC地址)僅解決網絡內部的尋址問題。在不同子網之間通信時,爲了識別和找到網絡中的設備,每一子網中的設備都會被分配一 個唯一的地址。由於各個子網使用的物理技術可能不同,因此這個地址應當是邏輯地址(如IP地址)
2、交換:規定不同的交換方式。常見的交換技術有:線路交換技術和存儲轉發技術,後者包括報文轉發技術和分組轉發技術。
3、路由算法:當源節點和路由節點之間存在多條路徑時,本層可以根據路由算法,通過網絡爲數據分組選擇最佳路徑,並將信息從最合適的路徑,由發送端傳送的接受端。
4、連接服務:與數據鏈路層的流量控制不同的是,前者控制的是網絡相鄰節點間的流量,後者控制的是從源節點到目的節點間的流量。其目的在於防止阻塞,並進行差錯檢測(包括:流量控制、差錯控制、擁塞控制)
協議:IP
6)數據鏈路層
定義:接受來自物理層的位流形式的數據,並封裝成幀,傳送到上一層;同樣,也將來自上一層的數據幀,拆裝爲位流形式的數據轉發到物理層;並且還負責處理接受端發回的確認幀的信息,以便提供可靠的數據傳輸。
功能:1.成幀(定義幀的開始和結束)2.差錯控制(幀錯+位錯)3.流量控制4.訪問(接入)控制
7)物理層
定義:利用傳輸介質爲數據鏈路層提供屋裏連接,實現比特流的透明傳輸,屏蔽掉具體傳輸介質與物理設備的差異。使其上面的數據鏈路層不必考慮網絡的具體傳輸介質是什麼
透明傳輸的意義就是:不管傳的是什麼,所採用的設備只是起一個通道作用,把要傳輸的內容完好的傳到對方!

b)TCP/IP四層模型(事實標準)
1、模型架構

在這裏插入圖片描述

2、各層功能

a)網絡接口層(數據鏈路層)
(1)作用:實現互連在同一種數據鏈路(傳輸介質)結點之間幀傳遞
(2)傳輸單位:幀
(3)功能:組幀、差錯控制、流量控制和傳輸管理
b)網絡層(網際層)
(1)作用:實現網絡中終端節點之間的通信(點對點)/實現多數據鏈路之間通信
(2)傳輸單位:數據報
(3)功能:1、封裝數據成分組、包,路由選擇2、流量控制、擁塞控制&網際互聯
(4)協議:
a)Ip協議:提供網絡結點之間的報文傳送服務
IP協議三大作用模塊:
(1) IP尋址:IP地址由網絡和主機兩部分標識組成。保證相互連接的整個網絡中每臺主機的IP地址不會重疊。
(2) 路由控制(最終節點爲止的轉發=實現多數據鏈路之間通信)
數據鏈路實現某一區間(一跳:源MAC地址到目標MAC地址之間傳輸幀的區間)內通信
IP層通過路由控制(經過多跳路由)實現源地址到目標地址之間的通信
多跳路由是指路由器或主機在轉發IP數據包時只指定下一個路由器或主機,而不是將到最終目標地址爲止的所有通路全都指定出來。因爲每一個區間(跳)在在轉發IP數據包時會分別指定下一跳的操作,直至包達到最終的目標地址。爲了將數據包發給目標主機,所有主機都維護着一張路由控制表(Routing Table)。該表記錄IP數據在下一步應該發給哪個路由器。IP包將根據這個路由表在各個數據鏈路上傳輸。
在這裏插入圖片描述
(3) IP分包與組包
每種數據鏈路的最大傳輸單元(MTU)都不盡相同,因爲每個不同類型的數據鏈路的使用目的不同。使用目的不同,可承載的 MTU 也就不同。
任何一臺主機都有必要對 IP 分片進行相應的處理。分片往往在網絡上遇到比較大的報文無法一下子發送出去時纔會進行處理。
經過分片之後的 IP 數據報在被重組的時候,只能由目標主機進行。路由器雖然做分片但不會進行重組。
b)ARP協議:實現IP地址向物理地址的映射\ RARP協議:實現物理地址向IP地址的映射
c)ICMP協議:探測&報告傳輸中產生的錯誤\IGMP協議:管理多播組測成員關係
c)傳輸層
(1)作用:主要爲兩臺主機上的應用提供端到端的通信,爲應用層(會話)提供可靠無誤的數據傳輸服務,爲網絡層提供源端與目的端的端口信息。
(2)傳輸單位:報文段(TCP)/用戶數據報(UDP)
(3)協議: TCP和UDP
在這裏插入圖片描述
(2)傳輸層的通信識別
i.端口號及協議
端口號:用來識別同一臺計算機中進行通信的不同應用程序(傳輸層協議)
IP地址:用來識別TCP/IP網絡中互連的主機和路由器(網際層協議)
MAC地址:用來識別同一鏈路中不同計算機(網絡接口層協議)

端口(port)可理解爲計算機與外界通訊交流的出口。TCP/IP協議的端口是指在Internet上,各個主機間通過TCP/IP協議發送和接受數據報,各個數據報根據其目的主機的ip地址進行互聯網絡的路由選擇,從而將數據報傳送到目的主機。由於大多數操作系統都支持多程序(進程)併發運行,那麼目的主機應該根據端口號確定把接受到的數據報傳送給衆多運行的進程中的哪一個。
本地操作系統會給有需求的進程分配協議端口,每個協議端口由一個正整數標識,如80,139,145等等,並且開始監聽這個端口,等待數據返回。當目的主機接受到數據報後,根據報文首部的目的端口號,把數據發送到相應端口,而與此端口相對應的那個進程會領取數據並等待下一組數據的到來。可將端口理解爲隊,操作系統爲各個進程分配了不同的隊,數據報按照目標端口被推入相應的隊中,等待被進程取用。
端口不僅僅用於接受數據報,也用於發送數據報,這樣數據報中會有標識源端口,以方便接收方能夠順利回傳數據報到這個端口。
可理解爲:IP地址是電腦結點的網絡物理地址,相當於你家地址;端口是計算機邏輯通訊接口,不同應用程序使用不同的端口,可看做出入房子的門。一個ip地址的端口有65536個,通過端口號(0~65536)進行標識。(比如用於瀏覽網頁服務的80端口,用於FTP服務的21端口等等)

ii.通信識別
通過IP地址(源IP地址、目標IP地址)、協議號、端口號(源端口號、目標端口號)五個元素識別一個通信
在這裏插入圖片描述
在這裏插入圖片描述
d)應用層
(1)作用:負責處理特定的應用進程(主機運行的程序)間通信&交互規則
(2)協議:HTTP協議:提供Internet網瀏覽服務\DNS協議:負責域名和IP地址的映射\SMTP協議:提供簡單電子右鍵發送服務\POP、IMAP協議:提供郵箱服務器進行遠程存取郵件\FTP協議:提供應用級文件傳輸服務\Telent協議:提供遠程登錄服務(明文)\ssh協議:提供遠程登錄服務(加密)

3、TCP/IP協議族

在TCP/TP協議族中,網絡層IP提供的是一種不可靠的服務。它只是儘可能快地把分組從源節點送到目的節點,但不提供任何可靠性的保證。Tcp在不可靠的ip層上,提供了一個可靠的運輸層,爲了提供這種可靠的服務,TCP採用了超時重傳、發送和接受端到端的確認分組等機制。
在這裏插入圖片描述

4、通信過程(封裝+解封)

• ① 應用程序處理
首先應用程序會進行編碼處理,這些編碼相當於 OSI 的表示層功能;
編碼轉化後,郵件不一定馬上被髮送出去,這種何時建立通信連接何時發送數據的管理功能,相當於 OSI 的會話層功能。
• ② TCP 模塊的處理
TCP 根據應用的指示,負責建立連接、發送數據以及斷開連接。TCP 提供將應用層發來的數據順利發送至對端的可靠傳輸。爲了實現這一功能,需要在應用層數據的前端附加一個 TCP 首部。
• ③ IP 模塊的處理
IP 將 TCP 傳過來的 TCP 首部和 TCP 數據合起來當做自己的數據,並在 TCP 首部的前端加上自己的 IP 首部。IP 包生成後,參考路由控制表決定接受此 IP 包的路由或主機。
• ④ 網絡接口(以太網驅動)的處理
從 IP 傳過來的 IP 包對於以太網來說就是數據。給這些數據附加上以太網首部並進行發送處理,生成的以太網數據包將通過物理層傳輸給接收端。
• ⑤ 網絡接口(以太網驅動)的處理
主機收到以太網包後,首先從以太網包首部找到 MAC 地址判斷是否爲發送給自己的包,若不是則丟棄數據。
如果是發送給自己的包,則從以太網包首部中的類型確定數據類型,再傳給相應的模塊,如 IP、ARP 等。這裏的例子則是 IP 。
• ⑥ IP 模塊的處理
IP 模塊接收到 數據後也做類似的處理。從包首部中判斷此 IP 地址是否與自己的 IP 地址匹配,如果匹配則根據首部的協議類型將數據發送給對應的模塊,如 TCP、UDP。這裏的例子則是 TCP。
另外嗎,對於有路由器的情況,接收端地址往往不是自己的地址,此時,需要藉助路由控制表,在調查應該送往的主機或路由器之後再進行轉發數據。
• ⑦ TCP 模塊的處理
在 TCP 模塊中,首先會計算一下校驗和,判斷數據是否被破壞。然後檢查是否在按照序號接收數據。最後檢查端口號,確定具體的應用程序。數據被完整地接收以後,會傳給由端口號識別的應用程序。
• ⑧ 應用程序的處理
接收端應用程序會直接接收發送端發送的數據。通過解析數據,展示相應的內容。

3.3)HTTP協議

一.TCP/IP通信傳輸流

利用 TCP/IP 協議族進行網絡通信時,會通過分層順序與對方進行通信。
• 首先作爲發送端的客戶端在應用層(HTTP 協議)發出一個想看某個 Web 頁面的 HTTP 請求。
• 接着,爲了傳輸方便,在傳輸層(TCP 協議)把從應用層處收到的數據(HTTP 請求報文)進行分割,並在各個報文上打上標記序號及端口號後轉發給網絡層。
• 在網絡層(IP 協議),增加作爲通信目的地的 MAC 地址後轉發給鏈路層。這樣一來,發往網絡的通信請求就準備齊全了。
• 接收端的服務器在鏈路層接收到數據,按序往上層發送,一直到應用層。當傳輸到應用層,才能算真正接收到由客戶端發送過來的 HTTP請求。
在這裏插入圖片描述
五層網絡模型
五層網絡模型用於理解網絡框架
在這裏插入圖片描述

  • 物理層:主要作用是定義物理設備之間如何傳輸數據。
  • 數據鏈路層:在通信的實體之間建立數據鏈路連接。
  • 網絡層:爲數據在結點之間傳輸創建邏輯鏈路。
  • 傳輸層:向用戶提供端到端(End-to-End)的服務,傳輸層向高層屏蔽了下層數據通信的細節,作爲Android開發者,需要對這一層的TCP/IP,UDP協議非常瞭解
  • 應用層:爲應用軟件提供了很多服務,構建於TCP協議之上,屏蔽網絡的傳輸相關細節。這一層需要了解Http,Ftp等協議。

二.定義

2.1 HTTP協議

HTTP協議(HyperText Transfer Protocol,超文本傳輸協議)是用於從WWW服務器傳輸超文本到本地瀏覽器的傳輸協議。在Internet上的Web服務器上存放的都是超文本信息,客戶機需要通過HTTP協議傳輸所要訪問的超文本信息。
HTTP是一個基於TCP/IP通信協議傳遞數據(HTML、圖片文件,查詢結果等)
HTTP是一個屬於應用層的面向對象的協議
HTTP協議工作於客戶端-服務端(請求-響應 C/S)架構上
HTTP簡單快速,客戶向服務器請求服務時只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST,每種方法規定了客戶與服務器聯繫的類型不同。由於HTTP協議簡單,使HTTP服務器的程序規模小,因而通信速度很快。且HTTP允許傳輸任意類型的數據對象,正在傳輸的類型由Content-Type加以標記。
HTTP是一個無連接的協議:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開連接。採用這種方式可以節省傳輸時間。
HTTP是一個無狀態的協議:無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味着如果後續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。

2.2 URI與URL

(1)URI:uniform resource identifier,統一資源標識符,用來唯一的標識一個資源。Web上可用的每種資源如HTML文檔、圖像、視頻片段、程序等都是一個來URI來定位的。
URI一般由三部組成:①訪問資源的命名機制②存放資源的主機名③資源自身的名稱,由路徑表示,着重強調於資源。
(2)URL:uniform resource locator,統一資源定位器,它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate這個資源。
採用URL可以用一種統一的格式來描述各種信息資源,包括文件、服務器的地址和目錄等。URL一般由三部組成:①協議(或稱爲服務方式)②存有該資源的主機IP地址(有時也包括端口號)③主機資源的具體地址。如目錄和文件名等
一個URL完整格式:
http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name

  1. 協議部分:該URL的協議部分爲“http:”,這代表網頁使用的是HTTP協議。在"HTTP"後面的“//”爲分隔符
  2. 域名部分:該URL的域名部分爲“www.aspxfans.com”。也可以使用IP地址作爲域名使用
  3. 端口部分(可省略):跟在域名後面的是端口"8080",域名和端口之間使用“:”作爲分隔符。如果省略端口部分,將採用默認端口80
  4. 虛擬目錄部分(可省略):從域名後的第一個“/”開始到最後一個“/”爲止,是虛擬目錄部分。本例中的虛擬目錄是“/news/”
  5. 文件名部分(可省略):從域名後的最後一個“/”開始到“?”或"#"或結束爲止
    虛擬目錄和文件名部分統稱爲指定請求資源的URI(WEB上任意可用資源)
  6. 錨部分(可省略):從“#”開始到最後,都是錨部分。本例中的錨部分是“name”
  7. 參數部分:從“?”開始到“#”爲止之間的部分爲參數部分,又稱搜索部分、查詢部分。本例中的參數部分爲“boardID=5&ID=24618&page=1”。參數可以允許有多個參數,參數與參數之間用“&”作爲分隔符。
2.3 HTTP工作過程

(1)客戶端通過網絡與Web服務端的HTTP端口(默認80)建立TCP連接(TCP協議)
(2)客戶端向服務器發送HTTP請求命令,如:GET/sample/hello.jsp HTTP/1.1
(3)客戶端發送請求頭信息,之後發送一空白行通知服務器已結束該頭信息發送
(4)服務器對客戶端的請求進行應答,如:HTTP/1.1 200 OK
(5)服務器返回響應頭信息,之後發送一空白行通知客戶端已結束該頭信息發送
(6)服務器向客戶端發送數據,以 Content-Type 響應頭信息所描述的格式發送用戶所請求的實際數據
(7)服務器關閉TCP連接
如果客戶端或者服務器在其頭信息加入了這行代碼 Connection:keep-alive ,TCP 連接在發送後將仍然保持打開狀態,於是,客戶端可以繼續通過相同的連接發送請求。保持連接節省了爲每個請求建立新連接所需的時間,還節約了網絡帶寬。

三.HTTP協議基礎

3.1 通過請求和響應的交換達成通信

http協議規定,請求從客戶端開始發出,並由服務端響應請求並返回。Http允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type標記

3.2 HTTP是不保存狀態的協議
3.3 使用Cookie的狀態管理

HTTP 是一種無狀態協議。協議自身不對請求和響應之間的通信狀態進行保存。可是隨着 Web 的不斷髮展,我們的很多業務都需要對通信狀態進行保存。於是我們引入了 Cookie 技術。
Cookie 技術通過在請求和響應報文中寫入 Cookie 信息來控制客戶端的狀態。Cookie 會根據從服務器端發送的響應報文內的一個叫做 Set-Cookie 的首部字段信息,通知客戶端保存Cookie。當下次客戶端再往該服務器發送請求時,客戶端會自動在請求報文中加入 Cookie 值後發送出去。服務器端發現客戶端發送過來的 Cookie 後,會去檢查究竟是從哪一個客戶端發來的連接請求,然後對比服務器上的記錄,最後得到之前的狀態信息。
cookie交互流程:
(1)第一次客戶端發送請求報文(沒有cookie)
GET /reader/ HTTP/1.1
Host: hackr.jp
(2)服務端發送響應報文(生成cookie信息)
HTTP/1.1 200 OK
Date: Thu, 12 Jul 2012 05:45:32 GMT
Server: Apache
< Set-Cookie: sid=1342077140226724; path=/; … >
Content-Type: text/plain; charset=UTF-8
(3)第二次客戶端發送請求報文(存在cookie信息)
GET /image/ HTTP/1.1
Host: hackr.jp
Cookie: sid=1342077140226724
(4)請求URI定位互聯網上資源
(5)(告知服務器意圖的)HTTP方法
(6)(非)持久連接
HTTP1.1採用持久連接:只要任意一端沒有明確提出斷開連接,則保持 TCP 連接狀態。旨在建立一次 TCP 連接後進行多次請求和響應的交互
HTTP1.0採用非持久連接:每個連接只能處理一個請求-響應事務
(7)管線化(並行發送多請求,且不用一個接一個等待響應)

四.HTTP協議報文結構

4.1 HTTP報文

用於 HTTP 協議交互的信息被稱爲 HTTP 報文。請求端(客戶端)的 HTTP 報文叫做請求報文;響應端(服務器端)的叫做響應報文。HTTP 報文本身是由多行(用 CR+LF 作換行符)數據構成的字符串文本

4.2 HTTP報文結構
(4.2.1)請求報文結構

在這裏插入圖片描述
通常一個HTTP請求報文由請求行、請求報頭、空行和請求數據4個部分組成。
a)請求行

  1. Method 用於請求的方法(請求訪問服務器類型)一共八種
  2. Request-URI 請求URI(請求訪問的資源對象)是一個統一資源標識符,如:/index.htm
  3. HTTP-Version HTTP版本號HTTP/1.1
  4. CRLF表示回車和換行(除了作爲結尾的CRLF外,不允許出現單獨的CR或LF字符)
GET  /index.htm  HTTP/1.1

請求方法(Method)是HTTP客戶端程序(如瀏覽器)向服務器發送請求時指明的請求類型。
常見的請求類型爲PUT和GET,但HTTP協議共定義了八種請求類型來表明對Request-URI指定的資源的不同操作方式。
HTTP請求方法

請求類型 說明
OPTIONS 返回服務器針對特定資源所支持的HTTP請求方法。也可以利用向Web服務器發送’*'的請求來測試服務器的功能性。
HEAD 向服務器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的元信息。
GET 向特定的資源發出請求。
POST 向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的創建和/或已有資源的修改。
PUT 向指定資源位置上傳其最新內容。
DELETE 請求服務器刪除Request-URI所標識的資源。
TRACE 回顯服務器收到的請求,主要用於測試或診斷。
CONNECT HTTP/1.1協議中預留給能夠將連接改爲管道方式的代理服務器。

POST與GET請求區別
在這裏插入圖片描述
標準答案:

  1. get在瀏覽器回退時是無害的,而post會再次請求
  2. get產生的url地址可以被收藏,而post不會
  3. get請求會被瀏覽器主動緩存,而post不會,除非手動設置
  4. get請求只能進行url編碼,而post支持多種編碼方式
  5. get請求參數會被完整保留在瀏覽歷史記錄裏,而post中的參數不會被保留
  6. get 請求在url中傳送的參數有長度限制,而post沒有
  7. 對參數的數據類型,get只接受ascll字符,而post沒有限制
  8. get比post更安全,因爲參數直接暴露在url上,所以不能用來傳遞敏感信息
  9. get參數通過url傳遞,poet放在request body中

b)請求頭部
服務端需要的附加信息(報文主體大小、所使用的語言、認證信息等內容)。
在請求行之後會有0個或者多個請求報頭,每個請求報頭都包含一個名字和一個值,它們之間用“:”分割。請求頭部會以一個空行,發送回車符和換行符,通知服務器以下不會有請求頭。
具體的請求頭見下請求頭部,典型的請求頭有:

  • Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機
  • User-Agent:發送請求的瀏覽器類型、操作系統等信息
  • Accept:客戶端可識別的內容類型列表,用於指定客戶端接收那些類型的信息
  • Accept-Encoding:客戶端可識別的數據編碼
  • Accept-Language:表示瀏覽器所支持的語言類型
  • Connection:允許客戶端和服務器指定與請求/響應連接有關的選項,例如這是爲Keep-Alive則表示保持連接。
  • Transfer-Encoding:告知接收端爲了保證報文的可靠傳輸,對報文采用了什麼編碼方式。

c)空行
報文首部與報文主體間空行隔開
d)主體(報文實體)
請求數據

舉例

POST http://patientapi.shoujikanbing.com/api/common/getVersion HTTP/1.1       //請求行
Content-Length: 226                                                          //請求報頭
Content-Type: application/x-www-form-urlencoded
Host: patientapi.shoujikanbing.com
Connection: Keep-Alive
User-Agent: Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; MI NOTE LTE Build/KTU84P) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Accept-Encoding: gzip
//不能省略的空格,下面是請求數據
clientversion=2_2.0.0&time=1459069342&appId=android&channel=hjwang&sessionId=0d1cee1f31926ffa8894c64804efa855101d56eb21caf5db5dcb9a4955b7fbc9&token=b191944d680145b5ed97f2f4ccf03058&deviceId=869436020220717&type=2&version=2.0.0

訪問地址爲:http://patientapi.shoujikanbing.com/api/common/getVersion,請求的方法是POST,用於獲取版本信息,

(4.2.2)響應報文結構

在這裏插入圖片描述
a)狀態行

  1. HTTP-Version 服務器HTTP協議版本號HTTP/1.1
  2. Status-Code 服務器返回的響應狀態代碼的狀態碼200
  3. Reason-Phrase 狀態代碼的文本描述

狀態碼
狀態代碼有三位數字組成,第一個數字定義了響應的類別,共分五種類別:

1xx:指示信息–表示請求已接收,繼續處理
2xx:成功–表示請求已被成功接收、理解、接受
3xx:重定向–要完成請求必須進行更進一步的操作
4xx:客戶端錯誤–請求有語法錯誤或請求無法實現
5xx:服務器端錯誤–服務器未能實現合法的請求

3.狀態消息ok

HTTP/1.1  200  OK

b)響應頭部
客戶端需要的附加信息(報文主體大小、所使用的語言、認證信息等內容),用於客戶端傳遞自身信息的響應。響應報頭見下響應頭部,常見的響應報頭有:

  • Location:用於重定向接受者到一個新的位置,常用在更換域名的時候

  • Server:包含可服務器用來處理請求的系統信息,與User-Agent請求報頭是相對應的

    c)空行
    報文首部與報文實體用空行隔開
    c)主體(報文實體)
    響應正文,服務器返回給客戶端的文本信息

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>
4.3 消息頭部(Header)

請求和響應常見的通用Header
在這裏插入圖片描述

(4.3.1)請求頭部

HTTP請求頭中比較重要的字段
在這裏插入圖片描述

(4.3.2) 響應Header

響應頭中比較重要的字段
在這裏插入圖片描述
如何保存Token呢?比如二次登陸,又或者其它接口需要這個Token才能去訪問,那麼我們可能需要使用set-cookie字段,以下是set-cookie中參數的含義。
在這裏插入圖片描述

五.Cookie和Session

均是解決HTTP無狀態協議的一種記錄客戶狀態的機制。

5.1 什麼是Cookie?——客戶端的通行證

Cookie意爲“甜餅”,是由W3C組織提出,最早由Netscape社區發展的一種機制。目前Cookie已經成爲標準,所有的主流瀏覽器如IE、Netscape、Firefox、Opera等都支持Cookie。
  由於HTTP是一種無狀態的協議,服務器單從網絡連接上無從知道客戶身份。怎麼辦呢?就給客戶端們頒發一個通行證吧,每人一個,無論誰訪問都必須攜帶自己通行證。這樣服務器就能從通行證上確認客戶身份了。這就是Cookie的工作原理。
  Cookie實際上是一小段的文本信息。客戶端請求服務器,如果服務器需要記錄該用戶狀態,就使用response向客戶端瀏覽器頒發一個Cookie。客戶端瀏覽器會把Cookie保存起來。當瀏覽器再請求該網站時,瀏覽器把請求的網址連同該Cookie一同提交給服務器。服務器檢查該Cookie,以此來辨認用戶狀態。服務器還可以根據需要修改Cookie的內容。
  如果我們不設置Cookie過期時間,那麼這個Cookie將不存放在硬盤上,當瀏覽器關閉的時候,Cookie就消失了。如果我們設置這個時間爲若干天之後,那麼這個Cookie會保存在客戶端硬盤中,即使瀏覽器關閉,這個值仍然存在,下次訪問相應網站時,同 樣會發送到服務器上。

5.2 什麼是Session?——服務端的客戶檔案

Session是另一種記錄客戶狀態的機制,不同的是Cookie保存在客戶端瀏覽器中,而Session保存在服務器上。客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種方式記錄在服務器上。
  如果說Cookie機制是通過檢查客戶身上的“通行證”來確定客戶身份的話,那麼Session機制就是通過檢查服務器上的“客戶明細表”來確認客戶身份。Session相當於程序在服務器上建立的一份客戶檔案,客戶來訪的時候只需要查詢客戶檔案表就可以了。它的工作流程如下:
(1)客戶第一次訪問時需要爲該客戶創建Session
(2)在創建Session的同時,服務器會爲Session生成一個唯一的Session Id。
(3)在Session創建完成後,就可以調用Session的相關方法往Session中增加內容。
(4)當客戶端再次發送請求的時候,會將這個Session Id帶上,服務器接收到這個請求之後就會依據Session Id找到對應的Session來確認客戶端的身份。
一般情況下,服務器會在一定時間內(默認30分鐘)保存這個 Session,過了時間限制,就會銷燬這個Session。在銷燬之前,程序員可以將用戶的一些數據以Key和Value的形式暫時存放在這個 Session中。當然,也有使用數據庫將這個Session序列化後保存起來的,這樣的好處是沒了時間的限制,壞處是隨着時間的增加,這個數據 庫會急速膨脹,特別是訪問量增加的時候。一般還是採取前一種方式,以減輕服務器壓力。

5.3 Cookie和Session區別

a.存放的位置不同:
  Cookie保存在客戶端,而Session保存在服務端。
  簡單的說,當你登錄一個網站的時候,如果web服務器端使用的是session,那麼所有的數據都保存在服務器上面,客戶端每次請求服務器的時候會發送 當前會話的session_id,服務器根據當前session_id判斷相應的用戶數據標誌,以確定用戶是否登錄,或具有某種權限。由於數據是存儲在服務器 上面,所以你不能僞造,但是如果你能夠獲取某個登錄用戶的session_id,用特殊的瀏覽器僞造該用戶的請求也是能夠成功的。
  如果瀏覽器使用的是 cookie,那麼所有的數據都保存在瀏覽器端,比如你登錄以後,服務器設置了 cookie用戶名(username),那麼,當你再次請求服務器的時候,瀏覽器會將username一塊發送給服務器,這些變量有一定的特殊標記。服 務器會解釋爲 cookie變量。所以只要不關閉瀏覽器,那麼 cookie變量便一直是有效的,所以能夠保證長時間不掉線。如果你能夠截獲某個用戶的 cookie變量,然後僞造一個數據包發送過去,那麼服務器還是認爲你是合法的。所以,使用 cookie被攻擊的可能性比較大。如果設置了的有效時間,那麼它會將 cookie保存在客戶端的硬盤上,下次再訪問該網站的時候,瀏覽器先檢查有沒有 cookie,如果有的話,就讀取該 cookie,然後發送給服務器。如果你在機器上面保存了某個論壇 cookie,有效期是一年,如果有人入侵你的機器,將你的 cookie拷走,然後放在他的瀏覽器的目錄下面,那麼他登錄該網站的時候就是用你的的身份登錄的。所以 cookie是可以僞造的。
  b.存取的方式不同:
  Cookie中只能保存ASCII字符串,Session中可以保存任意類型的數據,甚至Java Bean乃至任何Java類、對象等。
  c.安全性的不同:
  Cookie存儲在客戶端,對客戶端是可見的,可被客戶端窺探、複製、修改。而Session存儲在服務器上,不存在敏感信息泄露的風險
  Cookies的安全性能一直是倍受爭議的。雖然Cookies是保存在本機上的,但是其信息的完全可見性且易於本地編輯性,往往可以引起很多的安全問題。所以Cookies到底該不該用,到底該怎樣用,就有了一個需要給定的底線。
  典型應用:(1)判斷用戶是否登陸過網站,以便下次登錄時能夠直接登錄。(2)購物車的處理與設計。
  d.有效期上的不同:
  Cookie的過期時間可以被設置很長。Session依賴於名爲JSESSIONI的Cookie,其過期時間默認爲-1,只要關閉了瀏覽器窗口,該Session就會過期,因此Session不能完成信息永久有效。如果Session的超時時間過長,服務器累計的Session就會越多,越容易導致內存溢出。
  e.對服務器造成的壓力不同:
  每個用戶都會產生一個session,如果併發訪問的用戶過多,就會產生非常多的session,耗費大量的內存。因此,諸如Google、Baidu這樣的網站,不太可能運用Session來追蹤客戶會話。
  f.瀏覽器支持不同:
  Cookie運行在瀏覽器端,若瀏覽器不支持Cookie,需要運用Session和URL地址重寫。
  g.跨域支持不同:
  Cookie支持跨域訪問(設置domain屬性實現跨子域),Session不支持跨域訪問

Cookies是屬於Session對象的一種。但有不同,Cookies不會佔服務器資源,是存在客服端內存或者一個cookie的文本文件中;而“Session”則會佔用服務器資源。所以,儘量不要使用Session,而使用Cookies。但是我們一般認爲cookie是不可靠的,session是可靠地,但是目前很多著名的站點也都以來cookie。有時候爲了解決禁用cookie後的頁面處理,通常採用url重寫技術,調用session中大量有用的方法從session中獲取數據後置入頁面。

Session是由應用服務器維持的一個服務器端的存儲空間,用戶在連接服務器時,會由服務器生成一個唯一的SessionID,用該SessionID 爲標識符來存取服務器端的Session存儲空間。而SessionID這一數據則是保存到客戶端,用Cookie保存的,用戶提交頁面時,會將這一 SessionID提交到服務器端,來存取Session數據。這一過程,是不用開發人員干預的。所以一旦客戶端禁用Cookie,那麼Session也會失效。

服務器也可以通過URL重寫的方式來傳遞SessionID的值,因此不是完全依賴Cookie。如果客戶端Cookie禁用,則服務器可以自動通過重寫URL的方式來保存Session的值,並且這個過程對程序員透明。

5.4 Token、Cookie&Session聯繫

Cookie
cookie 是一個非常具體的東西,指的就是瀏覽器裏面能永久存儲的一種數據,僅僅是瀏覽器實現的一種數據存儲功能。
cookie由服務器生成,發送給瀏覽器,瀏覽器把cookie以key-value形式保存到某個目錄下的文本文件內,下一次請求同一網站時會把該cookie發送給服務器。由於cookie是存在客戶端上的,所以瀏覽器加入了一些限制確保cookie不會被惡意使用,同時不會佔據太多磁盤空間,所以每個域的cookie數量是有限的。
Session
session 從字面上講,就是會話。這個就類似於你和一個人交談,你怎麼知道當前和你交談的是張三而不是李四呢?對方肯定有某種特徵(長相等)表明他就是張三。
session 也是類似的道理,服務器要知道當前發請求給自己的是誰。爲了做這種區分,服務器就要給每個客戶端分配不同的“身份標識”,然後客戶端每次向服務器發請求的時候,都帶上這個“身份標識”,服務器就知道這個請求來自於誰了。至於客戶端怎麼保存這個“身份標識”,可以有很多種方式,對於瀏覽器客戶端,大家都默認採用 cookie 的方式。
服務器使用session把用戶的信息臨時保存在了服務器上,用戶離開網站後session會被銷燬。這種用戶信息存儲方式相對cookie來說更安全,可是session有一個缺陷:如果web服務器做了負載均衡,那麼下一個操作請求到了另一臺服務器的時候session會丟失。
Token
(1)簡介
在Web領域基於Token的身份驗證隨處可見。在大多數使用Web API的互聯網公司中,tokens 是多用戶下處理認證的最佳方式。大部分你見到過的API和Web應用都使用tokens。例如Facebook, Twitter, Google+, GitHub等。
具有以下幾點特性:
1、無狀態、可擴展
2、支持移動設備
3、跨程序調用
4、安全
(2)舊的驗證方式——Session:基於服務器驗證方式
我們都是知道HTTP協議是無狀態的,這種無狀態意味着程序需要驗證每一次請求,從而辨別客戶端的身份。程序需要通過在服務端存儲的登錄信息來辨別請求的。這種方式一般都是通過存儲Session來完成。
隨着Web,應用程序,已經移動端的興起,這種驗證的方式逐漸暴露出了問題。尤其是在可擴展性方面。
基於服務器驗證方式暴露的一些問題
1.Seesion內存開銷
每次認證用戶發起請求時,服務器需要去創建一個記錄來存儲信息。當越來越多的用戶發請求時,內存的開銷也會不斷增加。
2.可擴展性
在服務端的內存中使用Seesion存儲登錄信息,伴隨而來的是可擴展性問題。
3.CORS(跨域資源共享)
當我們需要讓數據跨多臺移動設備上使用時,跨域資源的共享會是一個讓人頭疼的問題。在使用Ajax抓取另一個域的資源,就可以會出現禁止請求的情況。
4.CSRF(跨站請求僞造)
用戶在訪問銀行網站時,他們很容易受到跨站請求僞造的攻擊,並且能夠被利用其訪問其他的網站。
在這些問題中,可擴展性是最突出的。因此我們有必要去尋求一種更有行之有效的方法。
(3)基於Token的驗證原理
基於Token的身份驗證是無狀態的,我們不將用戶信息存在服務器或Session中。這種概念解決了在服務端存儲信息時的許多問題。NoSession意味着你的程序可以根據需要去增減機器,而不用去擔心用戶是否登錄。
基於Token的身份驗證的過程如下:
1.用戶通過用戶名和密碼發送請求。
2.程序驗證。
3.程序返回一個簽名的token 給客戶端。
4.客戶端儲存token,並且每次用於每次發送請求。
5.服務端驗證token並返回數據。
每一次請求都需要token。token應該在HTTP的頭部發送從而保證了Http請求無狀態。我們同樣通過設置服務器屬性Access-Control-Allow-Origin: ,讓服務器能接受到來自所有域的請求。需要主要的是,在ACAO頭部標明(designating)時,不得帶有像HTTP認證,客戶端SSL證書和cookies的證書。
Token實現思路:
在這裏插入圖片描述
1.用戶登錄校驗,校驗成功後就返回Token給客戶端。
2.客戶端收到數據後保存在客戶端
3.客戶端每次訪問API是攜帶Token到服務器端。
4.服務器端採用filter過濾器校驗。校驗成功則返回請求數據,校驗失敗則返回錯誤碼
當我們在程序中認證了信息並取得token之後,我們便能通過這個Token做許多的事情。我們甚至能基於創建一個基於權限的token傳給第三方應用程序,這些第三方程序能夠獲取到我們的數據(當然只有在我們允許的特定的token)
通過使用Token,便可不用保存session id。服務端只需要生成token,然後驗證token。本質上是使用CPU計算時間來獲取了session的存儲空間。
(4)Token優勢
1、無狀態、可擴展
在客戶端存儲的Tokens是無狀態的,並且能夠被擴展。基於這種無狀態和不存儲Session信息,負載負載均衡器能夠將用戶信息從一個服務傳到其他服務器上。
如果我們將已驗證的用戶的信息保存在Session中,則每次請求都需要用戶向已驗證的服務器發送驗證信息(稱爲Session親和性)。用戶量大時,可能會造成一些擁堵。
但是不要着急。使用tokens之後這些問題都迎刃而解,因爲tokens自己hold住了用戶的驗證信息。
2、安全性
請求中發送token而不再是發送cookie能夠防止CSRF(跨站請求僞造)。即使在客戶端使用cookie存儲token,cookie也僅僅是一個存儲機制而不是用於認證。不將信息存儲在Session中,讓我們少了對session操作。
token是有時效的,一段時間之後用戶需要重新驗證。我們也不一定需要等到token自動失效,token有撤回的操作,通過token revocataion可以使一個特定的token或是一組有相同認證的token無效。
3、可擴展性
Tokens能夠創建與其它程序共享權限的程序。例如,能將一個隨便的社交帳號和自己的大號(Fackbook或是Twitter)聯繫起來。當通過服務登錄Twitter(我們將這個過程Buffer)時,我們可以將這些Buffer附到Twitter的數據流上(we are allowing Buffer to post to our Twitter stream)。
使用tokens時,可以提供可選的權限給第三方應用程序。當用戶想讓另一個應用程序訪問它們的數據,我們可以通過建立自己的API,得出特殊權限的tokens。
4、多平臺跨域
我們提前先來談論一下CORS(跨域資源共享),對應用程序和服務進行擴展的時候,需要介入各種各種的設備和應用程序。只要用戶有一個通過了驗證的token,數據和資源就能夠在任何域上被請求到。

六.瀏覽器訪問一個url所經歷的過程

在這裏插入圖片描述

(4)客戶端瀏覽器解析html代碼

(5)同時請求html代碼中的資源(如js、css、圖片等)

(6)最後瀏覽器對頁面進行渲染並呈現給用戶

(二)socket

1.定義

即套接字,是通信的基石,是應用層 與 TCP/IP 協議族通信的中間軟件抽象層,本質爲一個封裝了 TCP / IP協議族 的編程接口(API)(不是協議,是一個編程調用接口,通過socket才能實現http協議,屬於傳輸層)網絡上的兩個程序通過Socket實現一個雙向的通信連接從而進行數據交換。
Socket是網絡通信過程中端點的抽象表示,包含進行網絡通信必須的五種信息:連接使用協議、(本地主機IP地址,本地進程的協議端口)、(遠地主機IP地址,遠地進程協議端口)。
Socket一般成對出現,一對套接字(其中一個運行在服務端一個運行在客戶端):

Socket ={(IP地址1:PORT端口號),(IP地址2:PORT端口號)}

在這裏插入圖片描述
Socket是一個接口,在用戶進程與TCP/IP協議之間充當中間人,完成TCP/IP協議的書寫,用戶只需理解接口即可。
Socket是應用層與TCP/IP協議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數據,以符合指定的協議。
應用層通過傳輸層進行數據通信時,TCP會遇到同時爲多個應用程序進程提供併發服務的問題。多個TCP連接或多個應用程序進程可能需要通過同一個TCP協議端口傳輸數據。爲了區別不同的應用程序進程和連接,許多計算機操作系統爲應用程序與TCP/IP協議交互提供了Socket接口。應用層可以和傳輸層通過Socket接口,區分來自不同應用程序進程或網絡連接的通信,實現數據傳輸的併發服務。

2.客戶/服務器模型

2.1原理

Socket是面向客戶/服務器模型而設計的,針對客戶和服務器程序提供不同的Socket系統調用。客戶隨機申請一個Socket,系統爲之分配一個Socket號;服務器擁有全局公認的Socket,任何客戶都可以向它發出連接請求和信息請求。
應用層通過傳輸層進行數據通信時,TCP會遇到同時爲多個應用程序提供併發服務的問題,多個TCP連接多個應用程序進程可能需要通過同一個TCP協議端口傳輸數據,爲了區別不同的應用程序進程和連接,許多計算機操作系統爲應用程序與TCP/IP協議交互提供了套接字(socket)接口,應用層可以和傳輸層通過socket接口區分來自於不同應用進程或網絡連接的通信,實現數據傳輸的併發服務。
即Socket相當於一個封裝了TCP/IP協議的API,提供了程序內部與外界通信的端口,併爲通信雙方提供了數據傳輸通道。Socket類型爲流套接字(streamsocket)和數據報套接字(datagramsocket)。流套接字將TCP作爲其端對端協議,提供了一個可信賴的字節流服務。數據報套接字使用UDP協議,提供數據打包發送服務。

2.2連接過程

通信模型
在這裏插入圖片描述
套接字之間的連接過程可以分爲三個步驟:服務器監聽,客戶端請求,連接確認。

  1. 服務器監聽
    是服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態。
  2. 客戶端請求
    是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。爲此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然後就向服務器端套接字提出連接請求。
  3. 連接確認
    是指當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求,它就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,連接就建立好了。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。
    在這裏插入圖片描述

3.Socket與Http區別

(1)定義
Socket是套接字,對TCP/IP協議的封裝。對端(IP地址和端口)的抽象表示。
HTTP協議是通信協議,是利用TCP在Web服務器和瀏覽器之間數據傳輸的協議。
Socket和HTTP協議本質上就不同。SOCKET本質對TCP/IP的封裝,從而提供給程序員做網絡通信的接口(即端口通信)。可理解爲HTTP是一輛轎車模型框架,提供了所有封裝和數據傳遞的形式;Socket是發動機,提供了網絡通信的能力。
(2)功能
Socket:屬於傳輸層,因爲 TCP / IP協議屬於傳輸層,解決的是數據如何在網絡中傳輸的問題。
HTTP協議:屬於 應用層,解決的是如何包裝數據。
(3)工作方式
Http
採用 請求—響應 方式。
即建立網絡連接後,當 客戶端 向 服務器 發送請求後,服務器端才能向客戶端返回數據。
可理解爲:是客戶端有需要才進行通信。

Socket
採用 服務器主動發送數據 的方式。
即建立網絡連接後,通信雙方即可開始發送數據內容,直到雙方連接斷開。即服務器可主動發送消息給客戶端,實現信息的主動推送;而不需要由客戶端向服務器發送請求。
可理解爲:是服務器端有需要才進行通信。
因此Socket鏈接相對來說比較方便。但缺點是,客戶端到服務器之間的通信往往需要穿越多箇中間節點,例如路由器、網關、防火牆等,大部分防火牆默認會關閉長時間處於非活躍狀態的連接而導致 Socket 連接斷連,因此需要通過輪詢告訴網絡,該連接處於活躍狀態。
使用場景爲:
服務器端主動向客戶端推送數據,保持客戶端與服務器數據的實時與同步

4.實現

4.1 基於TCP協議的網絡通信

1. 使用ServerSocket創建TCP服務端

Java中能接受其他通信實體連接請求的類是ServerSocket,ServerSocket對象用於監聽來自客戶端拿到Socket連接,如果沒有連接,它將一直處於等待狀態。

ServerSocket ss =new ServerSocket(30000);
while(true){
	Socket s = ss.accept();
	……
} 

使用步驟:

  1. 創建ServerSocket對象,綁定監聽的端口;
  2. 調用accept()方法監聽客戶端的請求;
  3. 連接建立後,通過輸入流InputStream讀取接收到的數據;
  4. 通過輸出流OutputStream向客戶端發送響應信息;
  5. 調用close()方法關閉相關資源。
public class SocketServer {
    public static void main(String[] args) throws IOException {
        //1.創建一個服務器端Socket,即ServerSocket,指定綁定的端口,並監聽此端口
        ServerSocket serverSocket = new ServerSocket(12345);
        InetAddress address = InetAddress.getLocalHost();
        String ip = address.getHostAddress();
        Socket socket = null;
        //2.調用accept()等待客戶端連接
        System.out.println("~~~服務端已就緒,等待客戶端接入~,服務端ip地址: " + ip);
        socket = serverSocket.accept();
        //3.連接後獲取輸入流,讀取客戶端信息
        InputStream is=null;
        InputStreamReader isr=null;
        BufferedReader br=null;
        OutputStream os=null;
        PrintWriter pw=null;
        is = socket.getInputStream();     //獲取輸入流
        isr = new InputStreamReader(is,"UTF-8");
        br = new BufferedReader(isr);
        String info = null;
        while((info=br.readLine())!=null){
        	//循環讀取客戶端的信息
            System.out.println("客戶端發送過來的信息" + info);
        }
        socket.shutdownInput();//關閉輸入流
        socket.close();
    }
}

2.使用Socket創建TCP客戶端

構造器中指定遠程主機時既可使用InetAddress來指定,也可直接使用String對象來指定,但程序通常使用String對象來指定遠程IP地址。

Socket s = new Socket("192.168.1.88",30000); 

使用步驟:

  1. 創建Socket對象,指明需要連接的服務器的IP地址和端口號;
  2. 連接建立後,通過輸出流OutputStream向服務器發送請求信息;
  3. 通過輸入流InputStream獲取服務器響應的信息;
  4. 調用close()方法關閉套接字。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn_accept = (Button) findViewById(R.id.btn_accept);
        btn_accept.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        new Thread() {
            @Override
            public void run() {
                try {
                    acceptServer();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private void acceptServer() throws IOException {
        //1.創建客戶端Socket,指定服務器地址和端口
        Socket socket = new Socket("172.16.2.54", 12345);
        //2.獲取輸出流,向服務器端發送信息
        OutputStream os = socket.getOutputStream();//字節輸出流
        PrintWriter pw = new PrintWriter(os);//將輸出流包裝爲打印流
        //獲取客戶端的IP地址
        InetAddress address = InetAddress.getLocalHost();
        String ip = address.getHostAddress();
        pw.write("客戶端:~" + ip + "~ 接入服務器!!");
        pw.flush();
        socket.shutdownOutput();//關閉輸出流
        socket.close();
    }}
}}

4.2 基於UDP協議的網絡通信

UDP以數據報作爲數據的傳輸載體,在進行傳輸時首先要把傳輸的數據定義成數據報(Datagram),在數據報中指明數據要到達的Socket(主機IP地址和端口號),然後再將數據以數據報的形式發送出去。

1. 使用DatagramSocket創建TCP服務端

使用步驟:

  1. 創建DatagramSocket對象,指定端口號;
  2. 創建DatagramPacket對象,用於接收客戶端發送的數據;
  3. 調用DatagramSocket的receiver()方法接收信息;
  4. 讀取數據。
  5. 調用DatagramSocket的close()方法關閉資源。
public class UPDServer {
    public static void main(String[] args) throws IOException {
        /*
         * 接收客戶端發送的數據
         */
        // 1.創建服務器端DatagramSocket,指定端口
        DatagramSocket socket = new DatagramSocket(12345);
        // 2.創建數據報,用於接收客戶端發送的數據
        byte[] data = new byte[1024];// 創建字節數組,指定接收的數據包的大小
        DatagramPacket packet = new DatagramPacket(data, data.length);
        // 3.接收客戶端發送的數據
        System.out.println("****服務器端已經啓動,等待客戶端發送數據");
        socket.receive(packet);// 此方法在接收到數據報之前會一直阻塞
        // 4.讀取數據
        String info = new String(data, 0, packet.getLength());
        System.out.println("我是服務器,客戶端說:" + info);

        /*
         * 向客戶端響應數據
         */
        // 1.定義客戶端的地址、端口號、數據
        InetAddress address = packet.getAddress();
        int port = packet.getPort();
        byte[] data2 = "歡迎您!".getBytes();
        // 2.創建數據報,包含響應的數據信息
        DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
        // 3.響應客戶端
        socket.send(packet2);
        // 4.關閉資源
        socket.close();
    }
}

2.使用DatagramSocket創建TCP客戶端

使用步驟:

  1. 創建InetAddress對象等,定義要發送的信息;
  2. 創建DatagramPacket對象,包含將要發送的信息;
  3. 創建DatagramSocket對象;
  4. 調用DatagramSocket對象的send()方法發送數據。
  5. 調用DatagramSocket對象的close()方法關閉資源。
public class UDPClient {
    public static void main(String[] args) throws IOException {
        /*
         * 向服務器端發送數據
         */
        // 1.定義服務器的地址、端口號、數據
        InetAddress address = InetAddress.getByName("localhost");
        int port = 8800;
        byte[] data = "用戶名:admin;密碼:123".getBytes();
        // 2.創建數據報,包含發送的數據信息
        DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
        // 3.創建DatagramSocket對象
        DatagramSocket socket = new DatagramSocket();
        // 4.向服務器端發送數據報
        socket.send(packet);

        /*
         * 接收服務器端響應的數據
         */
        // 1.創建數據報,用於接收服務器端響應的數據
        byte[] data2 = new byte[1024];
        DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
        // 2.接收服務器響應的數據
        socket.receive(packet2);
        // 3.讀取數據
        String reply = new String(data2, 0, packet2.getLength());
        System.out.println("我是客戶端,服務器說:" + reply);
        // 4.關閉資源
        socket.close();
    }
}

(三)websocket

1.定義

WebSocket protocol 是HTML5一種新的協議。它借鑑了socket這種思想,爲web應用程序客戶端和服務端之間(注意是客戶端服務端)提供了一種全雙工通信機制(full-duplex)。同時,它又是一種新的應用層協議。一開始的握手需要藉助HTTP請求完成。
URL格式爲

ws://echo.websocket.org/?encoding=text HTTP/1.1

可以看到除了前面的協議名和http不同之外,它的表示地址就是傳統的url地址。
websocket是基於瀏覽器端的web技術,肯定不能脫離HTTP而單獨存在。websocket建立連接時一開始的握手需要藉助HTTP請求來完成。具體來講:

  1. 客戶端創建一個websocket實例,並綁定一個需要連接到的服務器地址
  2. 客戶端連接到服務端時,會向服務端發送一個HTTP報文
    報文中有一個upgrade首部,用於告訴服務端需要將通信協議切換到websocket
  3. 服務端將通信協議切換到websocket協議,併發送給客戶端一個HTTP響應報文,狀態碼爲101,表示同意客戶端協議轉換請求。
    以上過程稱爲websocket協議握手(websocket handshake),均是利用HTTP通信完成的。經過握手後,客戶端與服務端就建立了websocket連接,可以開始數據傳輸,並且以後通信都走websocket協議。

2.原理

WebSocket同HTTP一樣也是應用層的協議,但是它是一種雙向通信協議,是建立在TCP之上的。連接過程需要進行握手:

  1. 瀏覽器、服務器建立TCP連接,三次握手。這是通信的基礎,傳輸控制層,若失敗後續都不執行。
  2. TCP連接成功後,瀏覽器通過HTTP協議向服務器傳送WebSocket支持的版本號等信息。(開始前的HTTP握手)
  3. 服務器收到客戶端的握手請求後,同樣採用HTTP協議回饋數據。
  4. 當收到了連接成功的消息後,通過TCP通道進行傳輸通信。

websocket握手需要藉助於HTTP協議,建立連接後通信過程使用websocket協議。同時需要了解的是,該websocket連接還是基於我們剛纔發起http連接的那個TCP連接。一旦建立連接之後,我們就可以進行數據傳輸了,websocket提供兩種數據傳輸:文本數據和二進制數據。
基於以上分析,我們可以看到,websocket能夠提供低延遲,高性能的客戶端與服務端的雙向數據通信。它顛覆了之前web開發的請求處理響應模式,並且提供了一種真正意義上的客戶端請求,服務器推送數據的模式,特別適合實時數據交互應用開發。

3.websocket與HTTP

WebSocket 是 HTML5 一種新的協議。它實現了瀏覽器與服務器全雙工通信,能更好的節省服務器資源和帶寬並達到實時通訊,它建立在 TCP 之上,同 HTTP 一樣通過 TCP 來傳輸數據,但與HTTP協議也有不同:
相同點

  1. 都是一樣基於TCP的,都是可靠性傳輸協議。
  2. 都是應用層協議。

不同點

  1. 雙向通信
    WebSocket是雙向通信協議,模擬Socket協議,可以雙向發送或接受信息。在建立連接後,WebSocket 服務器和 Browser/Client Agent 都能主動的向對方發送或接收數據。HTTP是單向的。是一種請求-響應協議,需要客戶端發出請求,服務端進行響應。
  2. 三次握手
    WebSocket是需要握手進行建立連接的。WebSocket在建立握手時,數據是通過HTTP傳輸的。但是建立之後,在真正傳輸時候是不需要HTTP協議的。
  3. 持久連接
    相對於傳統 HTTP 每次請求-應答都需要客戶端與服務端建立連接的模式,WebSocket 是類似 Socket 的 TCP 長連接的通訊模式,一旦 WebSocket 連接建立後,後續數據都以幀序列的形式傳輸。在客戶端斷開 WebSocket 連接或 Server 端斷掉連接前,不需要客戶端和服務端重新發起連接請求。在海量併發及客戶端與服務器交互負載流量大的情況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優勢,且客戶端發送和接受消息是在同一個持久連接上發起,實時性優勢明顯。
    在這裏插入圖片描述

4.websocket與socket

相同點

  1. 均是基於TCP的長連接模式
  2. 均是一種雙向通信協議

不同點
Socket不是協議,是位於應用層和傳輸層之間封裝TCP/IP協議的接口,兩臺主機通信,必須通過socket建立TCP連接
Websocket是應用層協議

5.實現

客戶端

 var ws = new WebSocket(“ws://echo.websocket.org”); //實例化新的WebSocket客戶端對象,WebSocket客戶端對象會自動解析並識別爲WebSocket請求,從而連接服務端端口,執行雙方握手過程
 ws.onopen = function(){ws.send(“Test!”); }; 
 ws.onmessage = function(evt){console.log(evt.data);ws.close();}; 
 ws.onclose = function(evt){console.log(“WebSocketClosed!”);}; 
 ws.onerror = function(evt){console.log(“WebSocketError!”);};

服務端

6.應用

即時通訊,替代輪詢
網站上的即時通訊是很常見的,比如網頁的QQ,聊天系統等。按照以往的技術能力通常是採用輪詢、Comet技術解決。
HTTP協議是非持久化的,單向的網絡協議,在建立連接後只允許瀏覽器向服務器發出請求後,服務器才能返回相應的數據。當需要即時通訊時,通過輪詢在特定的時間間隔(如1秒),由瀏覽器向服務器發送Request請求,然後將最新的數據返回給瀏覽器。這樣的方法最明顯的缺點就是需要不斷的發送請求,而且通常HTTP request的Header是非常長的,爲了傳輸一個很小的數據 需要付出巨大的代價,是很不合算的,佔用了很多的寬帶。
缺點:會導致過多不必要的請求,浪費流量和服務器資源,每一次請求、應答,都浪費了一定流量在相同的頭部信息上
然而WebSocket的出現可以彌補這一缺點。在WebSocket中,只需要服務器和瀏覽器通過HTTP協議進行一個握手的動作,然後單獨建立一條TCP的通信通道進行數據的傳送。

(四)HTTPS協議

一.HTTP傳輸數據的問題

超文本傳輸協議HTTP協議被用於在web瀏覽器和網站服務器之間傳遞信息。HTTP協議以明文方式發送內容,不提供任何方式的數據加密,如果攻擊者截取了web瀏覽器和網站服務器之間的傳輸報文,就可以直接讀懂其中的信息,因此HTTP協議不適合傳輸一些敏感信息,比如信用卡號,密碼等。
爲了解決HTTP協議的這一缺陷,需要使用使用另一種協議,安全套接字層超文本傳輸協議HTTPS。HTTPS在HTTP的基礎上加入SSL(Secure Sockets Layer 安全套接字層)協議,SSL依靠證書來驗證服務器的身份,爲瀏覽器和服務器之間的通信加密。

傳輸的是明文(username=chy
password=123),隱私數據容易容易泄露——解決方案:對傳輸數據進行加密——Https:將數據加密後再傳輸(5$#^&*)

二.HTTPS簡介

HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer)是以安全爲目標的Http通道,可理解爲HTTP的加強版。實現原理是再HTTP下加入SSL層,SSL負責加密和解密數據(HTTPS = HTTP + SSL)
在這裏插入圖片描述
HTTPS的特點在於:

  1. 內容加密:採用混合加密技術,中間者無法直接查看明文內容。
    混合加密:結合對稱加密和非對稱加密技術。客戶端使用對稱密鑰對傳輸數據進行加密, 然後用非對稱密鑰對對稱密鑰進行加密。所以網絡上傳輸的數據是被對稱加密過的密文和用非對稱加密後的密鑰。因此即使被黑客截取。由於沒有私鑰,所以無法獲取加密明文的密鑰,也無法獲取明文數據。
  2. 驗證身份:確保瀏覽器訪問的網站是經過CA驗證的可信任網站。
  3. 數據完整性:防止傳輸的內容被中間人冒充或篡改。

三.HTTPS和HTTP區別

  1. 定義上:
    HTTP:超文本傳輸協議,是一個基於請求與響應,無狀態的,應用層的協議,常基於TCP/IP協議傳輸數據,互聯網上應用最爲廣泛的一種網絡協議,所有的WWW文件都必須遵守這個標準。設計HTTP的初衷是爲了提供一種發佈和接收HTML頁面的方法。
    HTTPS:HTTPS是身披SSL外殼的HTTP。HTTPS是一種通過計算機網絡進行安全通信的傳輸協議,經由HTTP進行通信,利用SSL/TLS建立全信道,加密數據包。HTTPS使用的主要目的是提供對網站服務器的身份認證,同時保護交換數據的隱私與完整性。
  2. HTTPS協議需要CA申請證書(交費)(權威機構頒發證書——安全|自己生成的證書——不安全)
  3. HTTP是超文本傳輸協議,信息是明文傳輸。HTTPS協議是SSL+HTTP協議構建的可進行加密傳輸、身份認證的安全的網絡協議
  4. HTTP和HTTPS使用不同連接方式,端口也不同(http:80,https:443)

四.SSL/TLS

4.1 簡介

SSL(Secure Sockets Layer 安全套接層),及其繼任者傳輸層安全(Transport Layer Security,TLS)是爲網絡通信提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡連接進行加密。
SSL協議位於TCP/IP協議與各種應用層協議之間,爲數據通訊提供安全支持。SSL協議可分爲兩層:

  1. SSL記錄協議(SSL Record Protocol):它建立在可靠的傳輸協議(如TCP)之上,爲高層協議提供數據封裝、壓縮、加密等基本功能的支持。 (通過對稱加密實現,用來保證數據傳輸過程中完整性和私密性)
  2. SSL握手協議(SSL Handshake Protocol):它建立在SSL記錄協議之上,用於在實際的數據傳輸開始前,通訊雙方進行身份認證、協商加密算法、交換加密密鑰等。(通過非對稱加密實現,負責握手過程的身份認證)

4.2 SSL加密算法

(1)對稱加密

原理:加密算法是公開的,靠的是密鑰來加密數據。使用一個密鑰加密,使用相同的密鑰才能解密
常用算法:DES,3DES,AES
優點:計算量小,加密和解密速度較快,適合加密較大數據
缺點:在傳輸加密數據之前需要傳遞密鑰,密鑰傳輸容易泄露;一個用戶需要對應一個密鑰,服務器管理密鑰比較麻煩

(2)非對稱加密

原理:加密算法是公開的,有一個公鑰,一個私鑰(公鑰和私鑰不是隨機的,由加密算法生成);公鑰加密只能私鑰解密,私鑰加密只能公鑰解密,加密解密需要不同密鑰
常用算法:RSA
優點:可以傳輸公鑰(服務器—>客戶端)和公鑰加密的數據(客戶端->服務器),數據傳輸安全
缺點:計算量大,加密和解密速度慢

4.3SSL證書種類

(1)域名型https證書(DVSSL),信任等級一般,只需網站的真實性便可辦法證書保護網站;
(2)企業型https證書(OVSSL),信任等級強,需驗證企業身份,審覈嚴格,安全性高;
(3)增強型https證書(EVSSL),信任等級最高,一般用於銀行證券等金融機構,審覈嚴格,安全性最高,可激活綠色網址欄
PS:自己生成的證書——訪問https網站:顯示證書無效,存在危險(不受信任的證書)

五.Https的工作原理

5.1原理

發送方將對稱加密的密鑰通過非對稱加密的公鑰進行加密,接收方使用私鑰進行解密得到對稱加密的密鑰,再通過對稱加密交換數據。Https協議通過對稱加密(傳輸快,傳輸交換數據)和非對稱加密(安全,傳輸密鑰)結合處理實現的。

5.2通信過程

  1. 瀏覽器發起往服務器的443端口發起請求"https://www.baidu.com"(及之後的對稱加密算法)。
  2. 服務器中有公鑰和私鑰,收到請求,會將公鑰服務器身份認證信息通過SSL數字證書返回給瀏覽器;

服務端,都有公鑰、私鑰和證書:
私鑰用來加密發出去的信息;
公鑰用來解密收到的信息;
證書用來證明身份,一般證書包含公鑰以及身份認證信息;這裏的證書可以是向某個權威機構申請的,也可以是自制的。區別在於自己辦法的證書需要客戶端驗證通過,纔可以繼續訪問;而使用受信任的公司申請的證書則不會彈出提示頁面。

  1. 瀏覽器進入數字證書認證環節,這一部分是瀏覽器內置的TLS完成的:
    (1)首先瀏覽器會從內置的證書列表中索引,找到服務器下發證書對應的機構,如果沒有找到,此時就會警告用戶該證書不是由權威機構頒發,是不可信任的(瀏覽器顯示https警告)。如果查到了對應的機構,則取出該機構頒發的公鑰。
    (2)用機構的證書公鑰解密得到證書的內容和證書籤名,內容包括網站的網址、網站的公鑰、證書的有效期等。瀏覽器會先驗證證書籤名的合法性。簽名通過後,瀏覽器驗證證書記錄的網址是否和當前網址是一致的,不一致會警告用戶。如果網址一致會檢查證書有效期,證書過期了也會警告用戶。這些都通過認證時,瀏覽器就可以安全使用證書中的網站公鑰了。
    (3)瀏覽器生成一個隨機數R,並使用網站公鑰對R進行加密。(R就是之後數據傳輸的對稱密鑰)

  2. 瀏覽器將加密的R傳送給服務器。

  3. 服務器用自己的私鑰解密得到R。

  4. 服務器以R爲密鑰使用了對稱加密算法加密網頁內容並傳輸給瀏覽器。

  5. 瀏覽器以R爲密鑰使用對應的對稱解密算法獲取網頁內容。

總結
前5步其實就是HTTPS的握手過程,這個過程主要是認證服務端證書(內置的公鑰)的合法性。
因爲非對稱加密計算量較大,整個通信過程只會用到一次非對稱加密算法(主要是用來保護傳輸客戶端生成的用於對稱加密的隨機數私鑰)。後續內容的加解密都是通過一開始約定好的對稱加密算法進行的。

握手過程採用了一次非對稱加密,對密鑰加密。使得瀏覽器與服務器雙方知道傳輸數據過程對稱加密算法的規則(對稱密鑰)。
數據傳輸過程採用多次對稱加密,對傳輸數據加密。瀏覽器和服務器用握手過程獲得的對稱加密規則獲取傳輸數據。
在這裏插入圖片描述

5.3優缺點

優點

  1. 使用HTTPS協議可認證用戶和服務器,確保數據發送到正確的客戶機和服務器
  2. HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,要比HTTP協議安全,防止數據在傳輸過程中被竊取,確保數據的完整性
  3. HTTPS是現行架構下最安全的解決方案,雖然不是絕對安全,但是它大幅度增加了中間人攻擊的成本

缺點

  1. 相同網絡環境下,HTTPS協議會使頁面的加載時間延長50%,增加10%~20%耗電,此外,HTTPS協議還會影響緩存,增加數據開銷和功耗(增加了加密解密過程)
  2. HTTPS協議的安全是有範圍的,在黑客攻擊、拒絕服務攻擊、服務器劫持方面幾乎起不到什麼作用
  3. 最關鍵的,SSL證書信用鏈體系並不安全,特別是再某些國家可以控制CA根證書的情況下,中間人攻擊一樣可行
  4. 成本方面:
    (1)SSL的專業證書需要購買,功能越強大,費用越高
    (2)SSL證書通常需要綁定固定IP,爲服務器增加固定IP會增加一定費用
    (3)HTTPS連接服務器端資源佔用高,相同負載下會增加帶寬和投入成本

六.HTTPS的現狀

2017年1月發佈的Chrome 56瀏覽器開始把收集密碼或信用卡數據的HTTP頁面標記爲“不安全”,若用戶使用2017年10月推出的Chrome 62,帶有輸入數據的HTTP頁面和所有以無痕模式瀏覽的HTTP頁面都會被標記爲“不安全”,此外,蘋果公司強制所有iOS App在2017年1月1日前使用HTTPS加密。可見HTTPS正在逐漸取代HTTP協議。

(五)網絡數據解析

(5.1)Json數據解析

JSON(JavaScript Object Notation, JS 對象簡譜) 是一種輕量級的數據交換格式。它基於ECMAScript (歐洲計算機協會制定的js規範)的一個子集,採用完全獨立於編程語言的文本格式來存儲和表示數據。簡潔和清晰的層次結構使得 JSON 成爲理想的數據交換語言。 易於人閱讀和編寫,同時也易於機器解析和生成,並有效地提升網絡傳輸效率。

(5.1.1)原生JSONObject

1、JSONObject&JSONArray表示形式
JSONObject的數據是用 { } 來表示的

{
    "id":"1",
    "courseID":"化學",
    "title":"滴定實驗",
    "content":"下週二實驗樓201必須完成"
}

JSONArray的數據是由JSONObject構成的數組,用 [ { } , { } , …… , { } ] 來表示的

[
    {
        "id":"1",
        "courseID":"數學",
        "title":"一加一等於幾"
    },
    {
        "id":"2",
        "courseID":"語文",
        "title":"請背誦全文"
    }
] 

2、JSONObject&JSONArray 與 JSON格式字符串 轉換
反序列化:JSONObject&JSONArray -> JSON格式字符串

String jsonStr = jsonObject.toString();
String jsonStr = jsonArray.toString();

序列化:JSONObject&JSONArray <- JSON格式字符串

/*json字符串最外層是大括號時:*/
JSONObject jsonObject = new JSONObject(jsonStr);

/*json字符串最外層是方括號時:*/
JSONArray jsonArray = new JSONArray(jsonStr);

3、JSONObject獲取數據
(1)從JSONArray中獲得JSONObject

/*JSONObject 獲取jsonArray :需要數組的字段名*/
JSONArray jsonArray = jsonObject.getJSONArray("children");


/*jsonArray獲取JSONObject : 需要遍歷數組*/
 for (int i = 0; i < jsonArray.length(); i++) {
    JSONObject jsonObject = jsonArray.getJSONObject(i);
 }

(2)從JSONObject中獲取具體數據

int mid= jsonObject.getInt("id");    
String mcourse=jsonObject.getString("courseID");   

(5.1.2)FastJson

阿里巴巴FastJson是一個Json處理工具包,包括“序列化”和“反序列化”兩部分,它可以解析JSON格式的字符串,支持將Java Bean序列化爲JSON字符串,也可以從JSON字符串反序列化到JavaBean。它具備如下特徵:
(1)速度最快,測試表明,fastjson具有極快的性能,超越任其他的Java Json parser。包括自稱最快的JackJson;
(2)功能強大,完全支持Java Bean、集合、Map、日期、Enum,支持範型,支持自省;無依賴,能夠直接運行在Java SE 5.0以上版本;支持Android;開源 (Apache 2.0)
1、引入依賴

import com.alibaba.fastjson.JSON;

2、構造實體類

package com.json.bean;

public class Person {
    private String id ;
    private String name;
    private int age ;

    public Person(){

    }
    public Person(String id,String name,int age){
        this.id=id;
        this.name=name;
        this.age=age;
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "解析json字符串後獲取的person對象:id的值是" + 
            id + ", name的值是" + name + ", age的值是" + age + "";
    }

}

3、JSON格式字符串與javaBean之間的轉換
javabean轉化成json字符串

		//javabean轉化爲json字符串
		Person person = new Person("1","fastjson",1);
		String jsonString = JSON.toJSONString(person);

		//javaarray轉化爲json字符串
		Person person1 = new Person("1","fastjson1",1);
        Person person2 = new Person("2","fastjson2",2);
        List<Person> persons = new ArrayList<Person>();
        persons.add(person1);
        persons.add(person2);
        String jsonString = JSON.toJSONString(persons);

json字符串轉換成javabean

 	//將json字符串轉化成javabean對象
	person =JSON.parseObject(jsonString,Person.class);

	//將json字符串轉換爲javaarray
	List<Person> persons2 = JSON.parseArray(jsonString,Person.class);
        //輸出解析後的person對象,也可以通過調試模式查看persons2的結構
        System.out.println("person1對象:"+persons2.get(0).toString());
        System.out.println("person2對象:"+persons2.get(1).toString());

(5.1.3)Gson

Gson工具類GsonUtil

public class GsonUtil {

    private static Gson gson = null;

    static {
        if (gson == null) {
            gson = new Gson();
        }
    }


    private GsonUtil() {
    }


    /**
     * 將object對象轉成json字符串
     *
     * @param object
     * @return
     */
    public static String GsonString(Object object) {
        String gsonString = null;
        if (gson != null) {
            gsonString = gson.toJson(object);
        }
        return gsonString;
    }


    /**
     * 將gsonString轉成泛型bean
     *
     * @param gsonString
     * @param cls
     * @return
     */
    public static <T> T GsonToBean(String gsonString, Class<T> cls) {
        T t = null;
        if (gson != null) {
            t = gson.fromJson(gsonString, cls);
        }
        return t;
    }


    /**
     * 轉成list
     * 泛型在編譯期類型被擦除導致報錯
     *
     * @param gsonString
     * @param cls
     * @return
     */
    public static <T> List<T> GsonToList(String gsonString, Class<T> cls) {
        List<T> list = null;
        if (gson != null) {
            list = gson.fromJson(gsonString, new TypeToken<List<T>>() {
            }.getType());
        }
        return list;
    }


    /**
     * 轉成list
     * 解決泛型問題
     *
     * @param json
     * @param cls
     * @param <T>
     * @return
     */
    public static <T> List<T> jsonToList(String json, Class<T> cls) {
        Gson gson = new Gson();
        List<T> list = new ArrayList<T>();
        JsonArray array = new JsonParser().parse(json).getAsJsonArray();
        for (final JsonElement elem : array) {
            list.add(gson.fromJson(elem, cls));
        }
        return list;
    }


    /**
     * 轉成list中有map的
     *
     * @param gsonString
     * @return
     */
    public static <T> List<Map<String, T>> GsonToListMaps(String gsonString) {
        List<Map<String, T>> list = null;
        if (gson != null) {
            list = gson.fromJson(gsonString,
                    new TypeToken<List<Map<String, T>>>() {
                    }.getType());
        }
        return list;
    }


    /**
     * 轉成map的
     *
     * @param gsonString
     * @return
     */
    public static <T> Map<String, T> GsonToMaps(String gsonString) {
        Map<String, T> map = null;
        if (gson != null) {
            map = gson.fromJson(gsonString, new TypeToken<Map<String, T>>() {
            }.getType());
        }
        return map;
    }

}

(5.1.4)JSONObject、FastJson與Gson三者對比

把Java對象JSON序列化,Jackson速度最快,在測試中比Gson快接近50%,FastJSON和Gson速度接近。
把JSON反序列化成Java對象,FastJSON、Jackson速度接近,Gson速度稍慢,不過差距很小。

(5.2)XML數據解析

(5.2.1)Dom

DOM(Document Object Model) 是一種用於XML文檔的對象模型,可用於直接訪問XML文檔的各個部分。它是一次性全部將內容加載在內存中,生成一個樹狀結構,它沒有涉及回調和複雜的狀態管理。 缺點是加載大文檔時效率低下。

public class DomPersonService {
    /**
     * @param inStream
     * @return
     * @throws Exception
     */
    public static List<Person> getPersons(InputStream inStream)
            throws Exception {

        List<Person> persons = new ArrayList<Person>();
        //文檔的解析
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse(inStream);
        //操作對象樹
        Element root = document.getDocumentElement();//返回文檔根元素
        NodeList personNodes = root.getElementsByTagName("person");
        for (int i = 0; i < personNodes.getLength(); i++) {
            Element personElement = (Element) personNodes.item(i);
            int id = new Integer(personElement.getAttribute("id"));
            Person person = new Person();
            person.setId(id);
            NodeList childNodes = personElement.getChildNodes();
            for (int y = 0; y < childNodes.getLength(); y++) {
                if (childNodes.item(y).getNodeType() == Node.ELEMENT_NODE) {
                    if ("name".equals(childNodes.item(y).getNodeName())) {
                        String name = childNodes.item(y).getFirstChild()
                                .getNodeValue();
                        person.setName(name);
                    }
                    else if ("age".equals(childNodes.item(y).getNodeName())) {
                        String age = childNodes.item(y).getFirstChild()
                                .getNodeValue();
                        person.setAge(new Short(age));
                    }
                }
            }
            persons.add(person);
        }
        inStream.close();
        return persons;
    }
}

使用DOM解析XML文件

public void testDOMGetPersons() throws Throwable {
    InputStream inStream = this.getClass().getClassLoader()
        .getResourceAsStream("person.xml");
    List<Person> persons = DomPersonService.getPersons(inStream);
    for (Person person : persons) {
        Log.i(TAG, person.toString());
     }
}

(5.2.2)Sax

SAX(Simple API for XML) 使用流式處理的方式,它並不記錄所讀內容的相關信息。它是一種以事件爲驅動的XML API,解析速度快,佔用內存少。使用回調函數來實現。 缺點是不能倒退。

public class SAXForHandler extends DefaultHandler {
    /**
     * -----------------SAX解析XML----------------------
     */
    private static final String TAG = "SAXForHandler";
    private List<Person> persons;
    private String perTag;// 通過此變量,記錄前一個標籤的名稱
    Person person;// 記錄當前Person
    public List<Person> getPersons() {
        return persons;
    }
    @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        String data = new String(ch, start, length).trim();
        if ( ! "".equals(data.trim())) {
            Log.i(TAG, "content: " + data.trim());
        }
        if ("name".equals(perTag)) {
            person.setName(data);
        }
        else if ("age".equals(perTag)) {
            person.setAge(new Short(data));
        }
        super.characters(ch, start, length);
    }
    @Override
    public void endDocument() throws SAXException {
        Log.i(TAG, "***endDocument()***");
        super.endDocument();
    }
    @Override
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        Log.i(TAG, qName + "***endElement()***");
        if ("person".equals(localName)) {
            persons.add(person);
            person = null;
        }
        perTag = null;
        super.endElement(uri, localName, qName);
    }
    @Override
    public void startDocument() throws SAXException {
        persons = new ArrayList<Person>();
        Log.i(TAG, "***startDocument()***");
        super.startDocument();
    }
    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {//localName標籤名,fullName帶命名空間的標籤名,attribute存放該標籤所有屬性
        if ("person".equals(localName)) {
            for (int i = 0; i < attributes.getLength(); i++) {
                Log.i(TAG, "attributeName:" + attributes.getLocalName(i)
                        + "_attribute_Value:" + attributes.getValue(i));
                person = new Person();
                person.setId(Integer.valueOf(attributes.getValue(i)));
                // person.setId(Integer.parseInt(attributes.getValue(i)));
            }
        }
        perTag = localName;
        Log.i(TAG, qName + "***startElement()***");
        super.startElement(uri, localName, qName, attributes);
    }

}

使用SAX解析XML文件

/**
 * *****************************使用SAX解析XML文件*******************
 * 輸入流是指向程序中讀入數據
 * @throws Throwable
 */
public void testSAXGetPersons() throws Throwable {
    InputStream inputStream = this.getClass().getClassLoader()
        .getResourceAsStream("person.xml");
    SAXForHandler saxForHandler = new SAXForHandler();
    //用工廠模式解析XML
    SAXParserFactory spf = SAXParserFactory.newInstance();
    SAXParser saxParser = spf.newSAXParser();
    saxParser.parse(inputStream, saxForHandler);
    // 第二種方式解析XML
    // XMLReader xmlReader = saxParser.getXMLReader();
    // xmlReader.setContentHandler(handler);        
    // xmlReader.parse(new InputSource(inputStream));
    List<Person> persons = saxForHandler.getPersons();
    inputStream.close();
    for (Person person : persons) {
        Log.i(TAG, person.toString());
    }
}

(5.2.3)Pull

Pull內置於Android系統中。也是官方解析佈局文件所使用的方式。Pull與SAX有點類似,都提供了類似的事件,如開始元素和結束元素。不同的是,SAX的事件驅動是回調相應方法,需要提供回調的方法,而後在SAX內部自動調用相應的方法。而Pull解析器並沒有強制要求提供觸發的方法。因爲他觸發的事件不是一個方法,而是一個數字。它使用方便,效率高。

public class PullPersonService {

    /**
     * ------------------------使用PULL解析XML-----------------------
     * @param inStream
     * @return
     * @throws Exception
     */
    public static List<Person> getPersons(InputStream inStream)
            throws Exception {
        Person person = null;
        List<Person> persons = null;
        XmlPullParser pullParser = Xml.newPullParser();
        pullParser.setInput(inStream, "UTF-8");
        int event = pullParser.getEventType();// 觸發第一個事件
        while (event != XmlPullParser.END_DOCUMENT) {
            switch (event) {
            case XmlPullParser.START_DOCUMENT:
                persons = new ArrayList<Person>();
                break;
            case XmlPullParser.START_TAG:
                if ("person".equals(pullParser.getName())) {
                    int id = new Integer(pullParser.getAttributeValue(0));
                    person = new Person();
                    person.setId(id);
                }
                if (person != null) {
                    if ("name".equals(pullParser.getName())) {
                        person.setName(pullParser.nextText());
                    }
                    if ("age".equals(pullParser.getName())) {
                        person.setAge(new Short(pullParser.nextText()));
                    }
                }
                break;
            case XmlPullParser.END_TAG:
                if ("person".equals(pullParser.getName())) {
                    persons.add(person);
                    person = null;
                }
                break;
            }
            event = pullParser.next();
        }
        return persons;
    }
}

PULL解析XML文件

public void testPullGetPersons() throws Throwable {
    InputStream inStream = this.getClass().getClassLoader()
            .getResourceAsStream("person.xml");
    List<Person> persons = PullPersonService.getPersons(inStream);
    for (Person person : persons) {
        Log.i(TAG, person.toString());
    }
}

(5.2.4)Dom、Sax與Pull三者對比

  • 內存佔用:SAX、Pull比DOM要好;
  • 編程方式:SAX採用事件驅動,在相應事件觸發的時候,會調用用戶編好的方法,也即每解析一類XML,就要編寫一個新的適合該類XML的處理類。DOM是W3C的規範,Pull簡潔。
  • 訪問與修改:SAX採用流式解析,DOM隨機訪問。
  • 訪問方式:SAX,Pull解析的方式是同步的,DOM逐字逐句。

(5.3)Json數據 & XML數據比較

(5.3.1)代碼比較

  • Json
{
    "name": "中國",
    "province": [{
        "name": "黑龍江",
        "cities": {
            "city": ["哈爾濱", "大慶"]
        }
    }, {
        "name": "廣東",
        "cities": {
            "city": ["廣州", "深圳", "珠海"]
        }
    }]
}
  • XML
<?xml version="1.0" encoding="utf-8"?>
<country>
    <name>中國</name>
    <province>
        <name>黑龍江</name>
        <cities>
            <city>哈爾濱</city>
            <city>大慶</city>
        </cities>
    </province>
     <province>
        <name>廣東</name>
        <cities>
            <city>廣州</city>
            <city>深圳</city>
            <city>珠海</city>
        </cities>
    </province>
</country>

(5.3.2)特點比較

JSON XML 結論
可讀性 簡易語法 規範的標籤形式 平分秋色,不同場景下各有優勢
可擴展性 可擴展 可擴展 因爲 JSON是JS標準的子集合,所以它在JS處理擴展時更優優勢,可存儲JS複合對象
編碼難度 可編碼 支持Dom4j、Dom、SAX等 XML編碼方式有很多,但是JSON也可以很快的編碼,並且在代碼量(對結構解析)上要較XML少
解碼難度 各不相同,解碼要看被解碼的數據格式和編碼規則,JSON和XML都遵循自己的解碼方式

(5.3.3)Json優勢

  • JSON數據清晰
  • JSON有很多工具類支持它的轉換
  • JSON在所有主流瀏覽器有很好的支持
  • JSON在傳輸時數據量更小
  • JSON在JS中有天然的語言優勢(因爲它是標準的子集合)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章