Android網(wǎng)絡(luò)編程知識是Android開發(fā)過程中必不可少的內(nèi)容,在網(wǎng)絡(luò)開發(fā)的過程中,我們通常會用到像Volley、OkHttp、Retrofit這些高度封裝好的框架,這使得我們的開發(fā)很便利但也屏蔽了相關(guān)的技術(shù)細(xì)節(jié)。而作為想要進(jìn)一步的開發(fā)者來說,我們不但要會用,有時(shí)候更要理解其實(shí)現(xiàn)的原理,理解了后更能促進(jìn)我們更好的使用這些框架。Ok,這篇文章我們講一講網(wǎng)絡(luò)的基本知識。
OSI 七層網(wǎng)絡(luò)模型
為了使不同的廠家生產(chǎn)的計(jì)算機(jī)能夠通信,以便能在更大的范圍內(nèi)建立計(jì)算機(jī)網(wǎng)絡(luò),國際標(biāo)準(zhǔn)化組織(ISO)在 1978 年提出了“開放系統(tǒng)互聯(lián)參考模型”,即 OSI/RM 模型(Open System Interconnection/Reference Model)。他將計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)的通信協(xié)議劃分為了七層,自下而上依次分別是:物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會話層、表示層、應(yīng)用層。其中低四層完成數(shù)據(jù)傳輸,高三層面向用戶。
物理層
物理層(Physical Layer)在局部局域網(wǎng)上傳送數(shù)據(jù)幀(data frame),它負(fù)責(zé)管理計(jì)算機(jī)通信設(shè)備和網(wǎng)絡(luò)媒體之間的互通。包括了針腳、電壓、線纜規(guī)范、集線器、中繼器、網(wǎng)卡、主機(jī)適配器等。
數(shù)據(jù)鏈路層
數(shù)據(jù)鏈路層(Data Link Layer)負(fù)責(zé)網(wǎng)絡(luò)尋址、錯(cuò)誤偵測和改錯(cuò)。當(dāng)表頭和表尾被加至數(shù)據(jù)包時(shí),會形成幀。數(shù)據(jù)鏈表頭(DLH)是包含了物理地址和錯(cuò)誤偵測及改錯(cuò)的方法。數(shù)據(jù)鏈表尾(DLT)是一串指示數(shù)據(jù)包末端的字符串。例如以太網(wǎng)、無線局域網(wǎng)(Wi-Fi)和通用分組無線服務(wù)(GPRS)等。
分為兩個(gè)子層:邏輯鏈路控制(logic link control,LLC)子層和介質(zhì)訪問控制(media access control,MAC)子層。
網(wǎng)絡(luò)層
網(wǎng)絡(luò)層(Network Layer) 決定數(shù)據(jù)的路徑選擇和轉(zhuǎn)寄,將網(wǎng)絡(luò)表頭(NH)加至數(shù)據(jù)包,以形成分組。網(wǎng)絡(luò)表頭包含了網(wǎng)絡(luò)數(shù)據(jù)。例如:互聯(lián)網(wǎng)協(xié)議(IP)等。
傳輸層
傳輸層(Transport Layer)把傳輸表頭(TH)加至數(shù)據(jù)以形成數(shù)據(jù)包。傳輸表頭包含了所使用的協(xié)議等發(fā)送信息。例如:傳輸控制協(xié)議(TCP)等。
會話層
會話層(Session Layer)負(fù)責(zé)在數(shù)據(jù)傳輸中設(shè)置和維護(hù)計(jì)算機(jī)網(wǎng)絡(luò)中兩臺計(jì)算機(jī)之間的通信連接。
表達(dá)層
表達(dá)層(Presentation Layer)把數(shù)據(jù)轉(zhuǎn)換為能與接收者的系統(tǒng)格式兼容并適合傳輸?shù)母袷健?br> 應(yīng)用層
應(yīng)用層(Application Layer)提供為應(yīng)用軟件而設(shè)的接口,以設(shè)置與另一應(yīng)用軟件之間的通信。例如: HTTP,HTTPS,F(xiàn)TP,TELNET,SSH,SMTP,POP3等。
TCP/IP 四層模型
由于 OSI/RM 模型過于復(fù)雜難以實(shí)現(xiàn),現(xiàn)實(shí)中廣泛使用的是 TCP/IP 模型。TCP/IP 是一個(gè)協(xié)議集,是由 ARPA ( Advanced Research Projects Agency Network 高等研究計(jì)劃署網(wǎng)絡(luò) ) 于 1977 到 1979 年推出的一種網(wǎng)絡(luò)體系結(jié)構(gòu)和協(xié)議規(guī)范。隨著 Internet 的發(fā)展,TCP/IP 得到進(jìn)一步的研究和推廣,成為 Internet 上的 “通用模型”。
??TCP/IP 模型在 OSI 模型的基礎(chǔ)上進(jìn)行了簡化,去掉了OSI參考模型中的會話層和表示層(這兩層的功能被合并到應(yīng)用層實(shí)現(xiàn)),同時(shí)將OSI參考模型中的數(shù)據(jù)鏈路層和物理層合并為主機(jī)到網(wǎng)絡(luò)層。變成了四層,從下到上分別為:網(wǎng)絡(luò)接口層、網(wǎng)絡(luò)層、傳輸層、應(yīng)用層。與 OSI 體系結(jié)構(gòu)對比如下:
IP 協(xié)議
網(wǎng)際協(xié)議((英語:Internet Protocol,縮寫為IP),又譯互聯(lián)網(wǎng)協(xié)議,是用于分組交換數(shù)據(jù)網(wǎng)絡(luò)的一種協(xié)議。
??IP是在TCP/IP協(xié)議族中網(wǎng)絡(luò)層的主要協(xié)議,任務(wù)僅僅是根據(jù)源主機(jī)和目的主機(jī)的地址來傳送數(shù)據(jù)。為此目的,IP定義了尋址方法和數(shù)據(jù)報(bào)的封裝結(jié)構(gòu)。第一個(gè)架構(gòu)的主要版本,現(xiàn)在稱為IPv4,仍然是最主要的互聯(lián)網(wǎng)協(xié)議,盡管世界各地正在積極部署IPv6。IP協(xié)議的作用在于把各種數(shù)據(jù)包準(zhǔn)確無誤的傳遞給對方,其中兩個(gè)重要的條件是IP地址和MAC地址(Media Access Control Address)。由于IP地址是稀有資源,不可能每個(gè)人都擁有一個(gè)IP地址,所以我們通常的IP地址是路由器給我們生成的IP地址,路由器里面會記錄我們的MAC地址。而MAC地址是全球唯一的,除去人為因素外不可能重復(fù)。舉一個(gè)現(xiàn)實(shí)生活中的例子,IP地址就如同是我們居住小區(qū)的地址,而MAC地址就是我們住的那棟樓那個(gè)房間那個(gè)人。
TCP 協(xié)議
傳輸控制協(xié)議(英語:Transmission Control Protocol,縮寫為TCP)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。
TCP 協(xié)議被認(rèn)為是穩(wěn)定的協(xié)議,因?yàn)樗幸韵绿攸c(diǎn):
- 面向連接,“三次握手”
- 雙向通信
- 保證數(shù)據(jù)按序發(fā)送,按序到達(dá)
- 超時(shí)重傳
?要使用 TCP 傳輸數(shù)據(jù),必須先建立連接,傳輸完成后釋放連接。分別對應(yīng)常說的“三次握手”、“四次揮手”。
TCP 的三次握手與四次揮手
三次握手與四次分手的流程如下所示:
三次握手
- 第一次握手:建立連接。客戶端發(fā)送連接請求報(bào)文段,將SYN位置為1,Sequence Number為x;然后,客戶端進(jìn)入SYN_SEND狀態(tài),等待服務(wù)器的確認(rèn);
- 第二次握手:服務(wù)器收到SYN報(bào)文段。服務(wù)器收到客戶端的SYN報(bào)文段,需要對這個(gè)SYN報(bào)文段進(jìn)行確認(rèn),設(shè)置Acknowledgment Number為x+1(Sequence Number+1);同時(shí),自己自己還要發(fā)送SYN請求信息,將SYN位置為1,Sequence Number為y;服務(wù)器端將上述所有信息放到一個(gè)報(bào)文段(即SYN+ACK報(bào)文段)中,一并發(fā)送給客戶端,此時(shí)服務(wù)器進(jìn)入SYN_RCVD狀態(tài);
- 第三次握手:客戶端收到服務(wù)器的SYN+ACK報(bào)文段。然后將ACK設(shè)置為y+1,向服務(wù)器發(fā)送ACK報(bào)文段,這個(gè)報(bào)文段發(fā)送完畢以后,客戶端和服務(wù)器端都進(jìn)入ESTABLISHED狀態(tài),完成TCP三次握手。
??當(dāng)客戶端和服務(wù)器通過三次握手建立了TCP連接后,當(dāng)數(shù)據(jù)傳輸完畢,斷開連接時(shí)就需要進(jìn)行TCP的四次揮手。
四次揮手
TCP 協(xié)議中,在通信結(jié)束后,需要斷開連接,這需要通過四次揮手,客戶端或服務(wù)器均可主動(dòng)發(fā)起,主動(dòng)的一方先斷開,這里以客戶端先斷開為例:
- 第一次分手:客戶端設(shè)置Seq和ACK,向服務(wù)器端發(fā)送一個(gè)FIN報(bào)文段;此時(shí),客戶端進(jìn)入FIN_WAIT_1狀態(tài);客戶端表示沒有數(shù)據(jù)要發(fā)送給服務(wù)器端了;
- 第二次分手:服務(wù)器端收到了客戶端發(fā)送的FIN報(bào)文段,向主機(jī)1回一個(gè)ACK報(bào)文段,ACK為Sequence Number加1;客戶端進(jìn)入FIN_WAIT_2狀態(tài);服務(wù)器告訴客戶端,我“同意”你的關(guān)閉請求;
- 第三次分手:服務(wù)器向客戶端發(fā)送FIN報(bào)文段,請求關(guān)閉連接,同時(shí)服務(wù)器進(jìn)入LAST_ACK狀態(tài);服務(wù)器告訴客戶端,我也沒啥數(shù)據(jù)要發(fā)給你了,請求斷開。
- 第四次分手:客戶端收到服務(wù)器發(fā)送的FIN報(bào)文段,向服務(wù)器發(fā)送ACK報(bào)文段,然后客戶端進(jìn)入TIME_WAIT狀態(tài);服務(wù)器收到客戶端的ACK報(bào)文段以后,就關(guān)閉連接;此時(shí),客戶端等待2MSL后依然沒有收到回復(fù),則證明服務(wù)端已正常關(guān)閉,那好,客戶端也可以關(guān)閉連接了。
為什么要進(jìn)行三次握手
三次握手的目的是為了防止已失效的連接請求報(bào)文段突然又傳送到了服務(wù)端,因而產(chǎn)生錯(cuò)誤。
“已失效的連接請求報(bào)文段”的產(chǎn)生在這樣一種情況下:client發(fā)出的第一個(gè)連接請求報(bào)文段并沒有丟失,而是在某個(gè)網(wǎng)絡(luò)結(jié)點(diǎn)長時(shí)間的滯留了,以致延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)server。本來這是一個(gè)早已失效的報(bào)文段。但server收到此失效的連接請求報(bào)文段后,就誤認(rèn)為是client再次發(fā)出的一個(gè)新的連接請求。于是就向client發(fā)出確認(rèn)報(bào)文段,同意建立連接。假設(shè)不采用“三次握手”,那么只要server發(fā)出確認(rèn),新的連接就建立了。由于現(xiàn)在client并沒有發(fā)出建立連接的請求,因此不會理睬server的確認(rèn),也不會向server發(fā)送數(shù)據(jù)。但server卻以為新的運(yùn)輸連接已經(jīng)建立,并一直等待client發(fā)來數(shù)據(jù)。這樣,server的很多資源就白白浪費(fèi)掉了。采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生。例如剛才那種情況,client不會向server的確認(rèn)發(fā)出確認(rèn)。server由于收不到確認(rèn),就知道client并沒有要求建立連接。
為什么要進(jìn)行四次揮手
TCP 連接是全雙工的,每一端都可以同時(shí)發(fā)送和接受數(shù)據(jù),關(guān)閉的時(shí)候兩端都要關(guān)閉各自兩個(gè)方向的通道,總共相當(dāng)于要關(guān)閉四個(gè)。
以客戶端發(fā)起關(guān)閉為例
1.服務(wù)器讀通道關(guān)閉
?2.客戶端寫通道關(guān)閉
?3.客戶端讀通道關(guān)閉
?4.服務(wù)器寫通道關(guān)閉
客戶端為什么要等待 2MSL?
MSL(Maximum Segment Life),是 TCP 對 TCP Segment 生存時(shí)間的限制
客戶端在發(fā)出確認(rèn)服務(wù)端關(guān)閉的 ACK 后,它沒有辦法知道對方是否收到這個(gè)消息,于是需要等待一段時(shí)間,如果服務(wù)端沒有收到關(guān)閉的消息后會重新發(fā)出 FIN 報(bào)文,這樣客戶端就知道自己上條消息丟了,需要再發(fā)一次;如果等待的這段時(shí)間沒有在收到 FIN 的重發(fā)報(bào)文,說明它的確已經(jīng)收到斷開的消息并且已經(jīng)斷開了。
??這個(gè)等待時(shí)間至少是:客戶端的 timeout + FIN 的傳輸時(shí)間,為了保證可靠,采用更加保守的等待時(shí)間 2MSL。
UDP協(xié)議
用戶數(shù)據(jù)報(bào)協(xié)議(英語:User Datagram Protocol,縮寫為UDP),又稱用戶數(shù)據(jù)報(bào)文協(xié)議,是一個(gè)簡單的面向數(shù)據(jù)報(bào)的傳輸層協(xié)議。
UDP是一個(gè)不可靠的或者說無連接的協(xié)議,他有以下的特點(diǎn):
UDP 缺乏可靠性。UDP 本身不提供確認(rèn),序列號,超時(shí)重傳等機(jī)制。UDP 數(shù)據(jù)報(bào)可能在網(wǎng)絡(luò)中被復(fù)制,被重新排序。即 UDP 不保證數(shù)據(jù)報(bào)會到達(dá)其最終目的地,也不保證各個(gè)數(shù)據(jù)報(bào)的先后順序,也不保證每個(gè)數(shù)據(jù)報(bào)只到達(dá)一次
UDP 數(shù)據(jù)報(bào)是有長度的。每個(gè) UDP 數(shù)據(jù)報(bào)都有長度,如果一個(gè)數(shù)據(jù)報(bào)正確地到達(dá)目的地,那么該數(shù)據(jù)報(bào)的長度將隨數(shù)據(jù)一起傳遞給接收方。而 TCP 是一個(gè)字節(jié)流協(xié)議,沒有任何(協(xié)議上的)記錄邊界。
UDP 是無連接的。UDP 客戶和服務(wù)器之前不必存在長期的關(guān)系。UDP 發(fā)送數(shù)據(jù)報(bào)之前也不需要經(jīng)過握手創(chuàng)建連接的過程。
UDP 支持多播和廣播。
??UDP 協(xié)議沒有 TCP 協(xié)議穩(wěn)定,因?yàn)樗唤⑦B接,也不按順序發(fā)送,可能會出現(xiàn)丟包現(xiàn)象,使傳輸?shù)臄?shù)據(jù)出錯(cuò)。但是有得就有失,UDP 的效率更高,因?yàn)?UDP 頭包含很少的字節(jié),比 TCP 負(fù)載消耗少,同時(shí)也可以實(shí)現(xiàn)雙向通信,不管消息送達(dá)的準(zhǔn)確率,只負(fù)責(zé)無腦發(fā)送。UDP 服務(wù)于很多知名應(yīng)用層協(xié)議,比如 NFS(網(wǎng)絡(luò)文件系統(tǒng))、SNMP(簡單網(wǎng)絡(luò)管理協(xié)議)。UDP 一般多用于 IP 電話、網(wǎng)絡(luò)視頻等容錯(cuò)率強(qiáng)的場景。
Socket (套接字)
[圖片上傳失敗...(image-efc77a-1558278902009)]
??Socket 作為應(yīng)用層和傳輸層之間的橋梁,與之關(guān)系最大的兩個(gè)協(xié)議就是傳輸層中的 TCP 和 UDP協(xié)議。
??Socket 是對 TCP/IP 協(xié)議族的一種封裝,是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層。TCP或者UDP的報(bào)文中,除了數(shù)據(jù)本身還包含了包的信息,比如目的地址和端口,包的源地址和端口,以及其他附加校驗(yàn)信息。從設(shè)計(jì)模式的角度看來,Socket其實(shí)就是一個(gè)門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議。
??Socket 還可以認(rèn)為是一種網(wǎng)絡(luò)間不同計(jì)算機(jī)上的進(jìn)程通信的一種方法,利用三元組(ip地址,協(xié)議,端口)就可以唯一標(biāo)識網(wǎng)絡(luò)中的進(jìn)程,網(wǎng)絡(luò)中的進(jìn)程通信可以利用這個(gè)標(biāo)志與其它進(jìn)程進(jìn)行交互。
??Socket 起源于 Unix ,Unix/Linux 基本哲學(xué)之一就是“一切皆文件”,都可以用“打開(open) –> 讀寫(write/read) –> 關(guān)閉(close)”模式來進(jìn)行操作。因此 Socket 也被處理為一種特殊的文件。
如圖所示,Socket 被稱為“套接字”,它把復(fù)雜的 TCP/IP 協(xié)議簇隱藏在背后,為用戶提供簡單的客戶端到服務(wù)端接口,讓我們感覺這邊輸入數(shù)據(jù),那邊就直接收到了數(shù)據(jù),像一個(gè)“管道”一樣。
Socket 的使用
Socket 的基本操作有以下幾部分:
- 連接遠(yuǎn)程機(jī)器
- 發(fā)送數(shù)據(jù)
- 接收數(shù)據(jù)
- 關(guān)閉連接
- 綁定端口
- 監(jiān)聽到達(dá)數(shù)據(jù)
- 在綁定的端口上接受來自遠(yuǎn)程機(jī)器的連接
要實(shí)現(xiàn)客戶端與服務(wù)端的通信,雙方都需要實(shí)例化一個(gè) Socket。
在 Java 中,客戶端可以實(shí)現(xiàn)上面的 1、2、3、4、,服務(wù)端實(shí)現(xiàn) 5、6、7.
Java.net 中為我們提供了使用 TCP、UDP 通信的兩種 Socket:
ServerSocket:流套接字,TCP
DatagramSocket:數(shù)據(jù)報(bào)套接字,UDP
使用 TCP 通信的 Socket 流程
服務(wù)端
- 調(diào)用 ServerSocket(int port) 創(chuàng)建一個(gè) ServerSocket,綁定到指定端口
- 調(diào)用 accept() 監(jiān)聽連接請求,如果客戶端請求連接則接受,返回通信套接字
- 調(diào)用 Socket 類的 getOutputStream() 和 getInputStream() 獲取輸出和輸入流,進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)
- 關(guān)閉套接字
客戶端:
- 調(diào)用 Socket() 創(chuàng)建一個(gè)流套接字,連接到服務(wù)端
- 調(diào)用 Socket 類的 getOutputStream() 和 getInputStream() 獲取輸出和輸入流,進(jìn)行網(wǎng)絡(luò)數(shù)據(jù)的收發(fā)
- 關(guān)閉套接字
使用 UDP 通信的 Socket 流程
服務(wù)端:
- 調(diào)用 DatagramSocket(int port) 創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字,綁定到指定端口
- 調(diào)用 DatagramPacket(byte[] buf, int length) 建立一個(gè)字節(jié)數(shù)組,以接受 UDP 包
- 調(diào)用 DatagramSocket 的 receive() 接收 UDP 包
- 調(diào)用 DatagramSocket.send() 發(fā)送 UDP 包
- 關(guān)閉數(shù)據(jù)報(bào)套接字
客戶端:
- 調(diào)用 DatagramSocket() 創(chuàng)建一個(gè)數(shù)據(jù)報(bào)套接字
- 調(diào)用 DatagramPacket(byte buf[], int offset, int length,InetAddress address, int port) 建立要發(fā)送的 UDP 包
- 調(diào)用 DatagramSocket 的 receive() 接收 UDP 包
- 調(diào)用 DatagramSocket.send() 發(fā)送 UDP 包
- 關(guān)閉數(shù)據(jù)報(bào)套接字