濮陽免費網(wǎng)站制作2024年新冠疫情最新消息今天
tcp頭部
tcp端口號
TCP的連接是需要四個要素確定唯一一個連接:
(源IP,源端口號)+ (目地IP,目的端口號)
所以TCP首部預(yù)留了兩個16位作為端口號的存儲,而IP地址由上一層IP協(xié)議負責傳遞
源端口號和目地端口各占16位兩個字節(jié),也就是端口的范圍是2^16=65535
另外1024以下是系統(tǒng)保留的端口,從1024-65535是用戶使用的端口范圍
tcp序號和確認號
32位序號 seq:Sequence number 縮寫seq ,TCP通信過程中某一個傳輸方向上的字節(jié)流的每個字節(jié)的序號,通過這個來確認發(fā)送的數(shù)據(jù)有序,比如現(xiàn)在序列號為1000,發(fā)送了1000,下一個序列號就是2000。
32位確認號 ack:Acknowledge number 縮寫ack,TCP對上一次seq序號做出的確認號,用來響應(yīng)TCP報文段,給收到的TCP報文段的序號seq加1,即表示期待下一次發(fā)送的序號。
tcp標志位
每個TCP段都有一個目的,這是借助于TCP標志位選項來確定的,允許發(fā)送方或接收方指定哪些標志應(yīng)該被使用,以便段被另一端正確處理。
用的最廣泛的標志是?SYN,ACK?和?FIN,用于建立連接,確認成功的段傳輸,最后終止連接。
- SYN:簡寫為
S
,同步標志位,用于建立會話連接,同步序列號;- ACK: 簡寫為
.
,確認標志位,對已接收的數(shù)據(jù)包進行確認,1表示確認號有效,0表示報文中不包含確認信息;- FIN: 簡寫為
F
,完成標志位,表示我已經(jīng)沒有數(shù)據(jù)要發(fā)送了,即將關(guān)閉連接;- PSH:簡寫為
P
,推送標志位,表示該數(shù)據(jù)包被對方接收后應(yīng)立即交給上層應(yīng)用,而不在緩沖區(qū)排隊;- RST:簡寫為
R
,重置標志位,用于連接復(fù)位、拒絕錯誤和非法的數(shù)據(jù)包;- URG:簡寫為
U
,緊急標志位,表示數(shù)據(jù)包的緊急指針域有效,用來保證連接不被阻斷,并督促中間設(shè)備盡快處理;
tcp的三次握手
第一次握手:
客戶端將TCP報文標志位SYN置為1,隨機產(chǎn)生一個序號值seq=x,將該數(shù)據(jù)包發(fā)送給服務(wù)器端,發(fā)送完畢后,客戶端進入SYN_SENT
狀態(tài),等待服務(wù)器端確認。
第二次握手:
服務(wù)器端收到數(shù)據(jù)包后由標志位SYN=1知道客戶端請求建立連接,服務(wù)器端將TCP報文標志位SYN和ACK都置為1,ack=x+1,隨機產(chǎn)生一個序號值seq=y,并將該數(shù)據(jù)包發(fā)送給客戶端以確認連接請求,服務(wù)器端進入SYN_RCVD
狀態(tài)。
第三次握手:
客戶端收到確認后,檢查ack是否為x+1,ACK是否為1,如果正確則將標志位ACK置為1,ack=y+1,并將該數(shù)據(jù)包發(fā)送給服務(wù)器端,服務(wù)器端檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,客戶端和服務(wù)器端進入ESTABLISHED
狀態(tài),完成三次握手,隨后客戶端與服務(wù)器端之間可以開始傳輸數(shù)據(jù)了。
為什么連接的建立需要三次握手?
假設(shè)client發(fā)出的第一個連接請求報文段并沒有丟失,而是在某個網(wǎng)絡(luò)結(jié)點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認為是client再次發(fā)出的一個新的連接請求。于是就向client發(fā)出確認報文段,同意建立連接。
假設(shè)采用的是“兩次握手”,那么只要server發(fā)出確認,新的連接就建立了。由于現(xiàn)在client并沒有發(fā)出建立連接的請求,因此不會理睬server的確認,也不會向server發(fā)送數(shù)據(jù)。但server卻以為新的運輸連接已經(jīng)建立,并一直等待client發(fā)來數(shù)據(jù)。這樣,server的很多資源就白白浪費掉了。
tcp的四次揮手
第一次揮手: Client端發(fā)起揮手請求,向Server端發(fā)送標志位是FIN報文段,設(shè)置序列號seq,此時,Client端進入FIN_WAIT_1
狀態(tài),這表示Client端沒有數(shù)據(jù)要發(fā)送給Server端了。
第二次分手:Server端收到了Client端發(fā)送的FIN報文段,向Client端返回一個標志位是ACK的報文段,ack設(shè)為seq加1,Client端進入FIN_WAIT_2
狀態(tài),Server端告訴Client端,我確認并同意你的關(guān)閉請求。
第三次分手: Server端向Client端發(fā)送標志位是FIN的報文段,請求關(guān)閉連接,同時Client端進入LAST_ACK
狀態(tài)。
第四次分手?: Client端收到Server端發(fā)送的FIN報文段,向Server端發(fā)送標志位是ACK的報文段,然后Client端進入TIME_WAIT
狀態(tài)。Server端收到Client端的ACK報文段以后,就關(guān)閉連接。此時,Client端等待2MSL的時間后依然沒有收到回復(fù),則證明Server端已正常關(guān)閉,那好,Client端也可以關(guān)閉連接了。
為什么連接的關(guān)閉需要四次揮手?
由于TCP協(xié)議是一種面向連接的、可靠的、基于字節(jié)流的運輸層通信協(xié)議,TCP是全雙工模式。這就意味著,關(guān)閉連接時,當Client端發(fā)出FIN報文段時,只是表示Client端告訴Server端數(shù)據(jù)已經(jīng)發(fā)送完畢了。當Server端收到FIN報文并返回ACK報文段,表示它已經(jīng)知道Client端沒有數(shù)據(jù)發(fā)送了,但是Server端還是可以發(fā)送數(shù)據(jù)到Client端的,所以Server很可能并不會立即關(guān)閉SOCKET,直到Server端把數(shù)據(jù)也發(fā)送完畢。當Server端也發(fā)送了FIN報文段時,這個時候就表示Server端也沒有數(shù)據(jù)要發(fā)送了,就會告訴Client端,我也沒有數(shù)據(jù)要發(fā)送了,之后彼此就會愉快的中斷這次TCP連接。
為什么要等待2MSL?
第一點:保證TCP協(xié)議的全雙工連接能夠可靠關(guān)閉:
由于IP協(xié)議的不可靠性或其它網(wǎng)絡(luò)原因,導(dǎo)致了Server端沒有收到Client端的ACK報文,發(fā)生超時。那么Server重發(fā)FIN,如果此時Client端的連接關(guān)閉了,則重發(fā)的FIN就找不到對應(yīng)的連接,導(dǎo)致連接錯亂。
第二點:保證這次連接的重復(fù)數(shù)據(jù)段從網(wǎng)絡(luò)中消失
如果Client端發(fā)送最后的ACK后直接進入CLOSED
狀態(tài),然后又再向Server端發(fā)起一個新連接,這時如果新老連接的端口號如果相同。
那么就可能出現(xiàn)問題:如果老連接某些數(shù)據(jù)滯留在網(wǎng)絡(luò)中,這些延遲數(shù)據(jù)在建立新連接后到達Client端,由于新老連接的端口號和IP都一樣,TCP協(xié)議就認為延遲數(shù)據(jù)是屬于新連接的,新連接就會接收到臟數(shù)據(jù),這樣就會導(dǎo)致數(shù)據(jù)包混亂。
接口認識
創(chuàng)建套接字(客戶端&服務(wù)器)
?創(chuàng)建套接字首先得選擇使用什么協(xié)議簇得套接字,比如使用ipv4協(xié)議(AF_INET)還是使用ipv6協(xié)議(AF_INET6)。其次選擇該套接字的類型,是使用面向字節(jié)流(SOCK_STREAM)還是面向數(shù)據(jù)報(SOCK_DGRAM)的套接字。第三個參數(shù)一般是置為0的。
該結(jié)果的返回值:成功return一個新的socket的文件描述符(fd),失敗則返回-1。
綁定套接字(服務(wù)器)
綁定套接字需要選擇綁定哪一個套接字(未建立連接),其次選擇該套接字使用什么地址簇,綁定哪個端口,綁定哪個ip。第三個參數(shù)是第二個結(jié)構(gòu)參數(shù)的大小。?
關(guān)于第二個參數(shù),這里有必要說明一下。各種網(wǎng)絡(luò)協(xié)議的地址格式并不相同。IPv4地址用sockaddr_in結(jié)構(gòu)體表示。大多時候,我們也是基于ipv4編程。下邊是關(guān)于sockaddr_in結(jié)構(gòu)體的介紹。
地址簇的選擇一般是選擇AF_INET,即ipv4。
端口號的綁定,還需要介紹一下,這里我們的端口號是基于主機序列的,而我們需要將主機序列的端口號轉(zhuǎn)換為網(wǎng)絡(luò)序列進行綁定。需要使用htons()函數(shù)。htons()作用是將端口號由主機字節(jié)序轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序的整數(shù)值,(host to net)。
htonl()作用和htons()一樣,不過它針對的是32位的(long),而htons()針對的是16位的(short)。
與htonl()和htons()作用相反的兩個函數(shù)是:ntohl()和ntohs()。
ip地址的綁定,這里還有一個in_addr的結(jié)構(gòu)體。
我們傳過去的ip地址一般都是點分十進制的,是便于我們識別的一種形式,需要將其轉(zhuǎn)換成網(wǎng)絡(luò)序列。inet_addr()作用是將一個IP字符串轉(zhuǎn)化為一個網(wǎng)絡(luò)字節(jié)序的整數(shù)值。一般需要轉(zhuǎn)換成為網(wǎng)絡(luò)序列的是客戶端。作為服務(wù)端,需要接受所有發(fā)向該主機的該端口的數(shù)據(jù)。(一臺主機可能配置多個ip)。因此:INADDR_ANY的宏值綁定在ip地址上就能解決這種問題。
監(jiān)聽套接字(服務(wù)器)
?listen的第二個參數(shù)是監(jiān)聽隊列大小。
Linux內(nèi)核協(xié)議棧為一個tcp連接管理使用兩個隊列:
1. 半鏈接隊列(用來保存處于SYN_SENT和SYN_RECV狀態(tài)的請求)
2. 全連接隊列(accpetd隊列)(用來保存處于established狀態(tài),但是應(yīng)用層沒有調(diào)用accept取走的請求)
而全連接隊列的長度會受到 listen 第二個參數(shù)的影響。
接收套接字(服務(wù)器)
首先選擇從哪個歡迎套接字接收新的套接字,其次該套接字使用什么類型的網(wǎng)絡(luò)協(xié)議結(jié)構(gòu),?第三個參數(shù)是第二個結(jié)構(gòu)體的大小(注意其類型)。
連接套接字(客戶端)
首先選擇連接該套接字的fd,其次選擇該套接字使用什么格式的網(wǎng)絡(luò)地址簇,端口是什么。第三個參數(shù)是第二個參數(shù)結(jié)構(gòu)體的大小。?