TCP報文格式
![](http://static.javashuo.com/static/loading.gif)
source
:發送TCP數據的源端口
dest
:接受TCP數據的目的端口
seq
:標識該TCP所包含的數據字節的開始序列號
ack_seq
:確認序列號,表示接受方下一次接受的數據序列號。
doff
:數據首部長度。和IP協議同樣,以4字節爲單位。通常的時候爲5
urg
:若是設置緊急數據指針,則該位爲1
ack
:若是確認號正確,那麼爲1
psh
:若是設置爲1,那麼接收方收到數據後,當即交給上一層程序
rst
:爲1的時候,表示請求從新鏈接
syn
:爲1的時候,表示請求創建鏈接
fin
:爲1的時候,表示請求關閉鏈接window窗口,告訴接收者能夠接收的大小check對TCP數據進行較核
urg_ptr
:若是urg=1,那麼指出緊急數據對於歷史數據開始的序列號的偏移值
TCP鏈接創建三次握手
![](http://static.javashuo.com/static/loading.gif)
- 第一次握手:創建鏈接時,客戶端發送syn包(seq=x)到服務器,並進入SYN_SEND狀態,等待服務器確認;
- 第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(seq=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
- 第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
- 完成三次握手,客戶端與服務器開始傳送數據,
TCP優缺點
優勢程序員
- TCP提供以承認的方式顯式地建立和終止鏈接。
- TCP保證可靠的、順序的(數據包以發送的順序接收)以及不會重複的數據傳輸。
- TCP經過保持連續並將數據塊分紅更小的分片來處理大數據塊。無需程序員知道
缺點編程
- TCP在傳遞數據時必須建立(並保持)一個鏈接。這個鏈接給通訊進程增長了開銷,讓它比UDP速度要慢。TCP 鏈接的資源消耗,其中包括:數據包信息、條件狀態、序列號等。
- TCP鏈接存在安全隱患:經過故意不完成創建鏈接所須要的三次握手過程,形成鏈接一方的資源耗盡
- 序列號的可預測性,目標主機應答鏈接請求時返回的SYN/ACK 的序列號是可預測的 TCP 會話劫持和SYN FLOOD(同步洪流)就是根據TCP的這個弱點出現的一種網絡攻擊方式
TCP 數據結構
在TCP鏈接創建的過程當中,會用到一些數據結構安全
- 半鏈接隊列:在三次握手協議中,服務器維護一個未鏈接隊列,該隊列爲每一個客戶端的SYN包(syn=j)開設一個條目,該條目代表服務器已收到SYN 包,並向客戶發出確認,正在等待客戶的確認包。這些條目所標識的鏈接在服務器處於Syn_RECV狀態,當服務器收到客戶的確認包時,刪除該條目,服務器進入ESTABLISHED狀態。
- Backlog參數:表示未鏈接隊列的最大容納數目。
- SYN-ACK 重傳次數:服務器發送完SYN-ACK包,若是未收到客戶確認包,服務器進行首次重傳,等待一段時間仍未收到客戶確認包,進行第二次重傳,若是重傳次數超過系統規定的最大重傳次數,系統將該鏈接信息從半鏈接隊列中刪除。注意,每次重傳等待的時間不必定相同。
- 半鏈接存活時間:是指半鏈接隊列的條目存活的最長時間,也即服務從收到SYN包到確認這個報文無效的最長時間,該時間值是全部重傳請求包的最長等待時間總和。有時咱們也稱半鏈接存活時間爲Timeout時間、SYN_RECV存活時間
TCP 標誌位
應該就是code bit那個字段的位的意義服務器
- SYN(同步標誌):該標誌置上的時候,同步序列編號(Synchronize Sequence Numbers)欄有效。該標誌僅在三次握手創建TCP鏈接時有效。它提示TCP鏈接的服務端檢查序列編號,該序列編號爲TCP鏈接初始端(通常是客戶端)的初始序列編號。在這裏,能夠把TCP序列編號看做是一個範圍從0到4,294,967,295的32位計數器。經過TCP鏈接交換的數據中每個字節都通過序列編號。在TCP報頭中的序列編號欄包括了TCP分段中第一個字節的序列編號。
- ACK(確認標誌):該標誌置上的時候,確認編號(Acknowledgement Number)欄有效。大多數狀況下該標誌位是置位的。TCP報頭內的確認編號欄內包含的確認編號(w+1,Figure-1)爲下一個預期的序列編號,同時提示遠端系統已經成功接收全部數據。
- RST(復位標誌):該標誌置上的時候,用於復位相應的TCP鏈接。
- URG(緊急標誌):該標誌置上的時候,表示緊急(The urgent pointer) 標誌有效。
- PSH(推標誌):該標誌置位時,接收端不將該數據進行隊列處理,而是儘量快將數據轉由應用處理。在處理 telnet 或 rlogin 等交互模式的鏈接時,該標誌老是置位的。
- FIN(結束標誌):帶有該標誌置位的數據包用來結束一個TCP回話,但對應端口仍處於開放狀態,準備接收後續數據。
TCP鏈接過程的中間狀態
- SYN_SENT:客戶端發送syn數據包向服務器端申請創建TCP鏈接,此時客戶端的狀態爲SYN_SENT
- SYN_RCVD:接收方收到請求,給發起方發送一個設置了SYN與ACK標誌位的TCP數據包作爲應答,另外設置一個比客戶機發送來的ISN大1個單位的ISN值,這常被稱爲SYN_ACK數據包或SYN_ACK報文這時鏈接的狀態稱作SYN_RCVD
- ESTABLISHED:發起方而後發送一個帶有ACK應答和增1後的ISN標誌來確認SYN_ACK至此,完成了三次握手,此時的鏈接狀態爲連結成功: ESTABLISHED
TCP鏈接終止協議(四次揮手)
![](http://static.javashuo.com/static/loading.gif)
因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的鏈接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP鏈接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另外一方執行被動關閉。
TCP客戶端發送一個FIN,用來關閉客戶到服務器的數據傳送(報文段4)。
服務器收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1(報文段5)。和SYN同樣,一個FIN將佔用一個序號。
服務器關閉客戶端的鏈接,發送一個FIN給客戶端(報文段6)。
客戶段發回ACK報文確認,並將確認序號設置爲收到序號加1(報文段7)。網絡
關閉鏈接等待兩倍最大生存時間
主動關閉鏈接方發送ack響應後,爲何須要等待兩倍最大生存時間後再關閉鏈接?數據結構
由於主動方發送的ACK消息可能丟失,並致使被動方再次發送FIN報文,TIME_WAIT爲鏈接中」離羣的段」提供從網絡中消失的時間。考慮一下,若是延遲或者重傳段在鏈接關閉後到達時會發生什麼呢?一般狀況下,由於TCP僅僅丟棄該數據並響應RST消息,因此這不會形成任何問題。當RST消息到達發出延時段的主機時,由於該主機也沒有記錄鏈接的任何信息,因此它也丟棄該段。然而,若是兩個相同主機之間又創建了一個具備相同端口號的新鏈接,那麼離羣的段就可能被當作是新鏈接的,若是離羣的段中數據的任何序列號偏偏在新鏈接的當前接收窗口中,數據就會被從新接收,其結果就是破壞新鏈接併發
- 簡單點說:就是爲了將以前的TCP鏈接的端口號給佔住,不讓新的TCP鏈接使用,避免以前TCP鏈接上發出的包(由於某些緣由比較晚纔到達目標主機)被新的TCP鏈接給收到和處理。
TCP關閉鏈接時狀態
- CLOSED: 初始狀態。
- LISTEN: 服務器端的某個SOCKET處於監聽狀態,能夠接受鏈接了
- FIN_WAIT_1:FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是:FIN_WAIT_1狀態其實是當SOCKET在ESTABLISHED狀態時,它想主動關閉鏈接,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,固然在實際的正常狀況下,不管對方何種狀況下,都應該立刻迴應ACK報文,因此FIN_WAIT_1狀態通常是比較難見到的,而FIN_WAIT_2狀態還有時經常能夠用netstat看到。
- FIN_WAIT_2:FIN_WAIT_2狀態下的SOCKET,表示半鏈接,也即有一方要求close鏈接,但另外還告訴對方,我暫時還有點數據須要傳送給你,稍後再關閉鏈接。
- TIME_WAIT: 表示收到了對方的FIN報文,併發送出了ACK報文,就等2MSL(2倍最大生存時間)後便可回到CLOSED可用狀態了。若是FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,能夠直接進入到TIME_WAIT狀態,而無須通過FIN_WAIT_2狀態。
- CLOSING: 正常狀況下,當你發送FIN報文後,按理來講是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。可是CLOSING狀態表示你發送FIN報文後,並無收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼狀況下會出現此種狀況呢?其實細想一下,也不可貴出結論:那就是若是雙方几乎在同時close一個SOCKET的話,那麼就出現了雙方同時發送FIN報文的狀況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET鏈接。
- CLOSE_WAIT: 在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN報文給本身,你係統毫無疑問地會迴應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正須要考慮的事情是察看你是否還有數據發送給對方,若是沒有的話,那麼你也就能夠close這個SOCKET,發送FIN報文給對方,也即關閉鏈接。因此你在CLOSE_WAIT狀態下,須要完成的事情是等待你去關閉鏈接。
- LAST_ACK: 它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也便可以進入到CLOSED可用狀態了。
三次和四次
爲何創建鏈接協議是三次握手,而關閉鏈接倒是四次握手呢?socket
這是由於服務端的LISTEN狀態下的SOCKET當收到SYN報文的建連請求後,它能夠把ACK和SYN(ACK起應答做用,而SYN起同步做用)放在一個報文裏來發送。但關閉鏈接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你全部的數據都所有發送給對方了,因此你能夠未必會立刻會關閉SOCKET,也即你可能還須要發送一些數據給對方以後,再發送FIN報文給對方來表示你贊成如今能夠關閉鏈接了,因此它這裏的ACK報文和FIN報文多數狀況下都是分開發送的。函數
TCP Socket 編程流程圖
![](http://static.javashuo.com/static/loading.gif)
UDP socket 編程流程圖
![](http://static.javashuo.com/static/loading.gif)
UDP和TCP區別
- UDP沒有三次握手過程。
- UDP處理的細節比TCP少。UDP不能保證消息被傳送到(它也報告消息沒有傳送到)目的地。
- UDP也不保證數據包的傳送順序。UDP把數據發出去後只能但願它可以抵達目的地。
UDP優缺點:
- UDP不要求保持一個鏈接
- UDP沒有因接收方承認收到數據包(或者當數據包沒有正確抵達而自動重傳)而帶來的開銷。
- 設計UDP的目的是用於短應用和控制消息
- 在一個數據包鏈接一個數據包的基礎上,UDP要求的網絡帶寬比TDP更小。
Socket概念
- 網絡的Socket數據傳輸是一種特殊的I/O,Socket也是一種文件描述符
- Socket也具備一個相似於打開文件的函數調用Socket(),該函數返回一個整型的Socket描述符,隨後的鏈接創建、數據傳輸等操做都是經過該Socket實現的。
Socket類型
- 流式Socket(SOCK_STREAM):流式是一種面向鏈接的Socket,針對於面向鏈接的TCP服務應用
- 數據報式Socket(SOCK_DGRAM):數據報式Socket是一種無鏈接的Socket,對應於無鏈接的UDP服務應用
socket調用庫函數主要有:
下面這幾個接口都會建立一個socket大數據
Socket(af,type,protocol)
:建立套接字
bind(sockid, local addr, addrlen)
:創建地址和套接字的聯繫
listen( Sockid ,quenlen)
:服務器端偵聽客戶端的請求
創建服務器/客戶端的鏈接 (面向鏈接TCP)
Connect(sockid, destaddr, addrlen)
:客戶端請求鏈接
newsockid=accept(Sockid,Clientaddr, paddrlen)
:服務器端等待從編號爲Sockid的Socket上接收客戶鏈接請求
發送/接收數據
send(sockid, buff, bufflen)
:面向鏈接的發送數據
recv( )
:面向鏈接的接收數據
sendto(sockid,buff,…,addrlen)
面向無鏈接發送數據
recvfrom( )
面向無鏈接的接收數據
釋放套接字