網絡層web
主要任務是把網絡協議數據單元或分組從源計算機通過適當的路徑發送到目的地計算機。從源計算機到目的計算機可能要通過若干個中間節點,這須要在通訊子網中進行路由選擇。編程
網絡層與數據鏈路層有很大的差異,數據鏈路層僅把數據幀從線纜或信道的一端傳送到另外一端(即在相鄰節點間進行數據傳送),網絡層的目的是實現兩個端系統之間的數據透明傳送,具體功能包括路由選擇、阻塞控制和網際互連等。設計模式
TCP 鏈接是一條虛鏈接而不是一條真正的物理鏈接。瀏覽器
TCP 可把太長的數據塊劃分短一些再傳送。TCP 也可等待積累有足夠多的字節後再構成報文段發送出去。緩存
(1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送(報文段4)。
(2)服務器B收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN同樣,一個FIN將佔用一個序號。
(3)服務器B關閉與客戶端A的鏈接,發送一個FIN給客戶端A(報文段6)。
(4)客戶端A發回ACK報文確認,並將確認序號設置爲收到序號加1(報文段7)。服務器
中斷鏈接端能夠是Client端,也能夠是Server端網絡
假設Client端發起中斷鏈接請求,也就是發送FIN報文。Server端接到FIN報文後,意思是說"我Client端沒有數據要發給你了",可是若是你還有數據沒有發送完成,則沒必要急着關閉Socket,能夠繼續發送數據。因此你先發送ACK,"告訴Client端,你的請求我收到了,可是我還沒準備好,請繼續你等個人消息"。這個時候Client端就進入FIN_WAIT狀態,繼續等待Server端的FIN報文。當Server端肯定數據已發送完成,則向Client端發送FIN報文,"告訴Client端,好了,我這邊數據發完了,準備好關閉鏈接了"。Client端收到FIN報文後,"就知道能夠關閉鏈接了,可是他仍是不相信網絡,怕Server端不知道要關閉,因此發送ACK後進入TIME_WAIT狀態,若是Server端沒有收到ACK則能夠重傳。「,Server端收到ACK後,"就知道能夠斷開鏈接了"。Client端等待了2MSL後依然沒有收到回覆,則證實Server端已正常關閉,那好,我Client端也能夠關閉鏈接了。Ok,TCP鏈接就這樣關閉了!多線程
1.爲何創建鏈接協議是三次握手,而關閉鏈接倒是四次握手呢?less
這是由於服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它能夠把ACK和SYN(ACK起應答做用,而SYN起同步做用)放在一個報文裏來發送。但關閉鏈接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你全部的數據都所有發送給對方了,因此你能夠未必會立刻會關閉SOCKET,也即你可能還須要發送一些數據給對方以後,再發送FIN報文給對方來表示你贊成如今能夠關閉鏈接了,因此它這裏的ACK報文和FIN報文多數狀況下都是分開發送的。tcp
2.爲何TIME_WAIT狀態還須要等2MSL後才能返回到CLOSED狀態?
這是由於雖然雙方都贊成關閉鏈接了,並且握手的4個報文也都協調和發送完畢,按理能夠直接回到CLOSED狀態(就比如從SYN_SEND狀態到ESTABLISH狀態那樣);可是由於咱們必需要假想網絡是不可靠的,你沒法保證你最後發送的ACK報文會必定被對方收到,所以對方處於LAST_ACK狀態下的SOCKET可能會由於超時未收到ACK報文,而重發FIN報文,因此這個TIME_WAIT狀態的做用就是用來重發可能丟失的ACK報文。
粘包問題能夠用下圖來表示:
1. 先接收到data1,而後接收到data2.
2. 一次性接收到了data1和data2的所有數據.
3. 先接收到了data1的所有數據和data2的部分數據,而後接收到了data2的餘下的數據.
4. 先接收到data1的部分數據,而後接收到data1餘下的部分以及data2的所有.
後三種就是粘包了
粘包出現緣由
在流傳輸中出現,UDP不會出現粘包,由於它有消息邊界(參考Windows 網絡編程)
1 發送端須要等緩衝區滿才發送出去,形成粘包
2 接收方不及時接收緩衝區的包,形成多個包接收
何時須要考慮粘包問題?
1 : 若是利用tcp每次發送數據,就與對方創建鏈接,而後雙方發送完一段數據後,就關閉鏈接,這樣就不會出現粘包問題
2:若是發送數據無結構,如文件傳輸,這樣發送方只管發送,接收方只管接收存儲就ok,也不用考慮粘包
3:若是雙方創建鏈接,須要在鏈接後一段時間內發送不一樣結構數據,如鏈接後,有好幾種結構:
1)"hello give me sth abour yourself"
2)"Don't give me sth abour yourself"
那這樣的話,若是發送方連續發送這個兩個包出去,接收方一次接收可能會是"hello give me sth abour yourselfDon't give me sth abour yourself" 這樣接收方就傻了,究竟是要幹嗎?不知道,由於協議沒有規定這麼詭異的字符串,因此要處理把它分包,怎麼分也須要雙方組織一個比較好的包結構,因此通常可能會在頭加一個數據長度之類的包,以確保接收。
TCP短鏈接和UDP不須要考慮粘包問題。
解決辦法
本質上是要在應用層維護消息與消息的邊界(下文的「包」能夠認爲是「消息」)
一、定長包
二、包尾加\r\n(ftp)缺點是若是消息自己含有\r\n字符,則也分不清消息的邊界
三、包頭加上包體長度
四、更復雜的應用層協議
HTTP:
HTTP是hypertext transfer protocol(超文本傳輸協議)的簡寫,它是TCP/IP協議的一個應用層協議,用於定義WEB瀏覽器與WEB服務器之間交換數據的過程。
HTTP 1.0 協議是無狀態的(stateless)。
HTTP 協議自己也是無鏈接的,雖然它使用了面向鏈接的 TCP 向上提供的服務。
在HTTP1.0協議中,客戶端與web服務器創建鏈接後,只能得到一個web資源。
在HTTP1.1協議,容許客戶端與web服務器創建鏈接後,在一個鏈接上獲取多個web資源。
一個完整的HTTP請求包括以下內容:一個請求行、若干消息頭、以及實體內容
accept:瀏覽器經過這個頭告訴服務器,它所支持的數據類型
Accept-Charset: 瀏覽器經過這個頭告訴服務器,它支持哪一種字符集
Accept-Encoding:瀏覽器經過這個頭告訴服務器,支持的壓縮格式
Accept-Language:瀏覽器經過這個頭告訴服務器,它的語言環境
Host:瀏覽器經過這個頭告訴服務器,想訪問哪臺主機
If-Modified-Since: 瀏覽器經過這個頭告訴服務器,緩存數據的時間
Referer:瀏覽器經過這個頭告訴服務器,客戶機是哪一個頁面來的 防盜鏈
Connection:瀏覽器經過這個頭告訴服務器,請求完後是斷開連接仍是何持連接
個HTTP響應表明服務器向客戶端回送的數據,它包括: 一個狀態行、若干消息頭、以及實體內容 。
HTTP響應中的經常使用響應頭(消息頭)
Location: 服務器經過這個頭,來告訴瀏覽器跳到哪裏
Server:服務器經過這個頭,告訴瀏覽器服務器的型號
Content-Encoding:服務器經過這個頭,告訴瀏覽器,數據的壓縮格式
Content-Length: 服務器經過這個頭,告訴瀏覽器回送數據的長度
Content-Language: 服務器經過這個頭,告訴瀏覽器語言環境
Content-Type:服務器經過這個頭,告訴瀏覽器回送數據的類型
Refresh:服務器經過這個頭,告訴瀏覽器定時刷新
Content-Disposition: 服務器經過這個頭,告訴瀏覽器如下載方式打數據
Transfer-Encoding:服務器經過這個頭,告訴瀏覽器數據是以分塊方式回送的
Expires: -1 控制瀏覽器不要緩存
Cache-Control: no-cache
Pragma: no-cache
NIO
阻塞I/O已有了必定了解,咱們知道阻塞I/O在調用InputStream.read()方法時是阻塞的,它會一直等到數據到來時(或超時)纔會返回;一樣,在調用ServerSocket.accept()方法時,也會一直阻塞到有客戶端鏈接纔會返回,每一個客戶端鏈接過來後,服務端都會啓動一個線程去處理該客戶端的請求。
阻塞I/O通訊模型,它的兩點缺點:
1. 當客戶端多時,會建立大量的處理線程。且每一個線程都要佔用棧空間和一些CPU時間
2. 阻塞可能帶來頻繁的上下文切換,且大部分上下文切換多是無心義的。
NIO的工做原理:
NIO經過Selector、Channel、和Buffer來實現非阻塞IO,NIO非阻塞的實現主要採用了Reactor(反應器)設計模式,這個設計模式與Observer設計模式相似,只不過Observer設計模式只能處理一個時間源,而Reactor設計模式能夠處理多個事件源。Channel是一個雙向的非阻塞通道,在通道的兩邊均可以進行數據的讀寫工做。Selector實現了用一個線程來管理多個通道(採用了複用與解複用的方式使得一個線程可以管理多個通道,便可以把多個流合併成爲一個流,或者把一個流分紅多個流的方式),它相似於一個觀察者。在實現時須要把Channel的IO事件(例如connect,read,write等)註冊給Selector。Selector內部實現原理爲:對全部註冊的Channel進行輪詢訪問,一旦輪詢到一個Channel有註冊的事件發生,例若有數據來了,它就經過傳回SelectionKey的方式來通知開發人員對Channel進行數據的讀或寫操做。這種經過輪詢的方式在處理多線程時不須要上下文的切換,而採用多線程的實現方式在線程之間切換須要上下文的切換,同時也須要進行壓棧與彈棧的操做,所以NIO具備較高的執行效率。
1. 由一個專門的線程來處理全部的 IO 事件,並負責分發。
2. 事件驅動機制:事件到的時候觸發,而不是同步的去監視事件。
Java NIO的服務端只需啓動一個專門的線程來處理全部的 IO 事件