自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

手動(dòng)發(fā)包只握手兩次,我發(fā)現(xiàn)了TCP的秘密

網(wǎng)絡(luò) 通信技術(shù)
如果因?yàn)楦鞣N原因,服務(wù)端并未收到客戶端發(fā)來的第三次握手包,那客戶端后續(xù)發(fā)送的數(shù)據(jù),服務(wù)端如何處理?

 [[399328]]

星球提問

TCP三次握手這個(gè)話題,沒有一萬,也有九千篇文章寫過了。

今天寫這篇文章,是因?yàn)橛星蛴言谖业闹R星球里提了這么一個(gè)問題:

總結(jié)一下三個(gè)小問題:

客戶端發(fā)送完第三次握手后,是不是不管服務(wù)器有沒有收到,直接就發(fā)送數(shù)據(jù)?

TCP的第三次握手能不能攜帶數(shù)據(jù)?

如果因?yàn)楦鞣N原因,服務(wù)端并未收到客戶端發(fā)來的第三次握手包,那客戶端后續(xù)發(fā)送的數(shù)據(jù),服務(wù)端如何處理?

我的回答

以下是我的回答:

首先來回答這位球友最開始的問題:客戶端發(fā)送完第三個(gè)握手后,是不是不管服務(wù)器有沒有收到,直接就發(fā)送數(shù)據(jù)?

你可以從理論上來猜測一下,如果上面這個(gè)問題的答案是否定的話,也就是說客戶端還得要確認(rèn)服務(wù)器收到自己的第三次握手包以后才能發(fā)送數(shù)據(jù)。那怎么確認(rèn)呢?是不是服務(wù)端還得回復(fù)自己一下:我收到了你的第三次握手包了,你可以發(fā)送數(shù)據(jù)了。

但如果這樣一來,那是不是就變成了四次握手,而不是三次握手了呢?

所以反過來想,這個(gè)問題的答案就是肯定的,即:客戶端發(fā)送完第三次握手包后,不再需要服務(wù)端的確認(rèn),立即可以發(fā)送數(shù)據(jù)。

下面是《TCP/IP協(xié)議詳解》(卷1)中的連接建立示意圖,你可以看到客戶端這一側(cè),發(fā)送完第三次握手包以后,狀態(tài)就別變成了ESTABLISH狀態(tài)了,并未等待服務(wù)器確認(rèn),就開始在傳輸數(shù)據(jù)了。

光理論不夠,我們再來抓包看一下,下面是我用抓包軟件抓了一個(gè)TCP連接建立的握手時(shí)序圖,同樣你可以看到,在第三次握手包發(fā)送后,左側(cè)的客戶端立即就發(fā)出了正式的數(shù)據(jù)傳輸:一個(gè)HTTP請求包。

所以這個(gè)問題的答案就清楚了。

接下來看第二個(gè)問題:客戶端在發(fā)送第三次握手包的時(shí)候是不是會攜帶數(shù)據(jù)一起傳輸過去?

其實(shí)從上面的2個(gè)圖中你可以看出,TCP三次握手并未攜帶有效的應(yīng)用層數(shù)據(jù),數(shù)據(jù)的傳輸是在握手完成以后才開始的。但是如果我們非得問一句:客戶端在發(fā)送第三次握手?jǐn)?shù)據(jù)包的時(shí)候,到底能不能順帶攜帶一些數(shù)據(jù)過去呢?

關(guān)于這一問題,最權(quán)威的答案還是得看RFC標(biāo)準(zhǔn)文檔,關(guān)于TCP標(biāo)準(zhǔn)協(xié)議的規(guī)范,是記錄在編號793的RFC793一文中,鏈接如下:

https://www.rfc-editor.org/rfc/rfc793.html

文檔有點(diǎn)長,而且是英文版,看起來可能有些吃力。

在事件處理這一節(jié)里面,會找到下面這段文字:

大意是說:如果我們的同步包SYN已經(jīng)得到了確認(rèn),就把連接狀態(tài)改為ESTABLISHED,然后發(fā)送的第三次握手包中可能會包含數(shù)據(jù)(如果已經(jīng)有數(shù)據(jù)在排隊(duì)等待傳輸?shù)脑?

這就說的很清楚了:TCP標(biāo)準(zhǔn)協(xié)議規(guī)范中,第三次握手包是允許傳輸數(shù)據(jù)的!

最后一個(gè)問題:如果因?yàn)楦鞣N原因,服務(wù)端并未收到客戶端發(fā)來的第三次握手包,那客戶端后續(xù)發(fā)送的數(shù)據(jù),服務(wù)端如何處理?

這里先賣個(gè)關(guān)子,接著往下看。

接下來才是這篇文章的精華部分:

實(shí)驗(yàn)論證

TCP建立連接的三次握手,是操作系統(tǒng)內(nèi)核協(xié)議棧自動(dòng)完成的,作為底層服務(wù),這個(gè)過程對應(yīng)用程序是透明的,我們開發(fā)應(yīng)用程序的時(shí)候,只需要使用應(yīng)用層編程接口就行了,比如套接字接口。

所以,大部分人對TCP三次握手的概念還是建立在書本上,博客里,公眾號文章里,今天,我們自己來發(fā)送TCP數(shù)據(jù)包來實(shí)現(xiàn)三次握手!

自己發(fā)包,來驗(yàn)證我們上面的結(jié)論!

使用的工具,是之前一篇文章中提到的神器:scapy。

為了方便查看數(shù)據(jù),我找了一個(gè)沒有HTTPS的網(wǎng)站,通過ping它的域名,拿到了IP地址,向其進(jìn)行握手并發(fā)送GET請求包。

  1. from scapy.all import * 
  2. def tcp_test(ip, port, data): 
  3.  
  4.     # 第一次握手,發(fā)送SYN包 
  5.     # 請求端口和初始序列號隨機(jī)生成 
  6.     # 使用sr1發(fā)送而不用send發(fā)送,因?yàn)閟r1會接收返回的內(nèi)容 
  7.     ans = sr1(IP(dst=ip) / TCP(dport=port, sport=RandShort(), seq=RandInt(), flags='S'), verbose=False
  8.  
  9.     # 假定此刻對方已經(jīng)發(fā)來了第二次握手包:ACK+SYN 
  10.  
  11.     # 對方回復(fù)的目標(biāo)端口,就是我方使用的請求端口(上面隨機(jī)生成的那個(gè)) 
  12.     sport = ans[TCP].dport 
  13.     s_seq = ans[TCP].ack 
  14.     d_seq = ans[TCP].seq + 1 
  15.  
  16.     # 第三次握手,發(fā)送ACK確認(rèn)包 
  17.     send(IP(dst=ip) / TCP(dport=port, sport=sport, ack=d_seq, seq=s_seq, flags='A'), verbose=False
  18.  
  19.     # 發(fā)起GET請求 
  20.     send(IP(dst=ip)/TCP(dport=port, sport=sport, seq=s_seq, ack=d_seq, flags=24)/data, verbose=False
  21.    
  22.    
  23. if __name__ == '__main__'
  24.     data = 'GET / HTTP/1.1\n' 
  25.     data += 'Host: www.chengtu.com\n' 
  26.     data += 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36\n' 
  27.     data += 'Accept: text/html' 
  28.     data += '\r\n\r\n' 
  29.  
  30.     tcp_test("150.138.151.65", 80, data) 

執(zhí)行上面這段代碼,來抓包看一下:

可以看到,成功的完成了三次握手動(dòng)作,服務(wù)器還返回了數(shù)據(jù),證明手動(dòng)編程來握手是可行的。

下面論證星球中,球友提出的問題:第三個(gè)握手包里面能不能攜帶數(shù)據(jù)呢?

我們來試一下就知道了:

  1. from scapy.all import * 
  2. def tcp_test_2(ip, port, data): 
  3.  
  4.     # 第一次握手,發(fā)送SYN包 
  5.     # 請求端口和初始序列號隨機(jī)生成 
  6.     # 使用sr1發(fā)送而不用send發(fā)送,因?yàn)閟r1會接收返回的內(nèi)容 
  7.     ans = sr1(IP(dst=ip) / TCP(dport=port, sport=RandShort(), seq=RandInt(), flags='S'), verbose=False
  8.  
  9.     # 假定此刻對方已經(jīng)發(fā)來了第二次握手包:ACK+SYN 
  10.  
  11.     # 對方回復(fù)的目標(biāo)端口,就是我方使用的請求端口(上面隨機(jī)生成的那個(gè)) 
  12.     sport = ans[TCP].dport 
  13.     s_seq = ans[TCP].ack 
  14.     d_seq = ans[TCP].seq + 1 
  15.  
  16.     # 第三次握手,發(fā)送ACK確認(rèn)包,順帶把數(shù)據(jù)一起帶上 
  17.     send(IP(dst=ip) / TCP(dport=port, sport=sport, ack=d_seq, seq=s_seq, flags='A')/data, verbose=False
  18.  
  19.  
  20. if __name__ == '__main__'
  21.  
  22.     data = 'GET / HTTP/1.1\n' 
  23.     data += 'Host: www.chengtu.com\n' 
  24.     data += 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36\n' 
  25.     data += 'Accept: text/html' 
  26.     data += '\r\n\r\n' 
  27.  
  28.     tcp_test_2("150.138.151.65", 80, data) 

看到了吧,在第三次握手中,我的GET請求就帶過去了,TCP協(xié)議仍然能夠正常工作!

這是Linux的情況,我又找了我們大學(xué)的網(wǎng)站試了一下,因?yàn)閷W(xué)校網(wǎng)站沒用HTTPS(就很離譜),而且是ASP.NET技術(shù)棧做的(別問我怎么知道的),服務(wù)器是Windows,依然可以正常工作,說明Windows的協(xié)議棧也支持這種操作。

接下來驗(yàn)證另一個(gè)問題:如果第三次握手包服務(wù)器沒有收到,就直接發(fā)送數(shù)據(jù),會發(fā)生什么?

怎么驗(yàn)證,很簡單,直接把發(fā)送第三次握手的那一行注釋掉,不發(fā)送第三次握手,直接發(fā)送GET請求就行了:

  1. from scapy.all import * 
  2. def tcp_test(ip, port, data): 
  3.  
  4.     # 第一次握手,發(fā)送SYN包 
  5.     # 請求端口和初始序列號隨機(jī)生成 
  6.     # 使用sr1發(fā)送而不用send發(fā)送,因?yàn)閟r1會接收返回的內(nèi)容 
  7.     ans = sr1(IP(dst=ip) / TCP(dport=port, sport=RandShort(), seq=RandInt(), flags='S'), verbose=False
  8.  
  9.     # 假定此刻對方已經(jīng)發(fā)來了第二次握手包:ACK+SYN 
  10.  
  11.     # 對方回復(fù)的目標(biāo)端口,就是我方使用的請求端口(上面隨機(jī)生成的那個(gè)) 
  12.     sport = ans[TCP].dport 
  13.     s_seq = ans[TCP].ack 
  14.     d_seq = ans[TCP].seq + 1 
  15.  
  16.     # 第三次握手,發(fā)送ACK確認(rèn)包 
  17.     # send(IP(dst=ip) / TCP(dport=port, sport=sport, ack=d_seq, seq=s_seq, flags='A'), verbose=False
  18.  
  19.     # 發(fā)起GET請求 
  20.     send(IP(dst=ip)/TCP(dport=port, sport=sport, seq=s_seq, ack=d_seq, flags='A')/data, verbose=False
  21.    
  22.    
  23. if __name__ == '__main__'
  24.     data = 'GET / HTTP/1.1\n' 
  25.     data += 'Host: www.chengtu.com\n' 
  26.     data += 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36\n' 
  27.     data += 'Accept: text/html' 
  28.     data += '\r\n\r\n' 
  29.  
  30.     tcp_test("150.138.151.65", 80, data) 

結(jié)果發(fā)現(xiàn)依然能正常工作!分析了一下,發(fā)現(xiàn)這種方式其實(shí)和上面那種情況是等價(jià)的:直接在第三次握手包中帶了數(shù)據(jù)。

這里雖然把第三次握手那一行注釋了,但直接發(fā)送的那個(gè)GET請求包中,ACK標(biāo)記是置位了的,所以服務(wù)端就把這個(gè)GET包當(dāng)成了第三次握手了。

所以結(jié)論就是:如果第三次握手包服務(wù)器沒有收到,就直接發(fā)送數(shù)據(jù),服務(wù)器將這個(gè)攜帶應(yīng)用數(shù)據(jù)的包當(dāng)做第三次握手(前提是這一個(gè)包中攜帶有ACK標(biāo)記)。

除了我上面的回答外,這位球友又評論補(bǔ)充了一個(gè)問題:

其實(shí)看到這里,這個(gè)問題的答案想必已經(jīng)心中有數(shù)了,但咱們還是來實(shí)驗(yàn)?zāi)M一下:先發(fā)送帶數(shù)據(jù)的請求包,然后再發(fā)送第三次握手包,看看會發(fā)生什么?

從圖中可以看到,直接發(fā)送的那個(gè)帶數(shù)據(jù)的請求包,被當(dāng)做了第三次握手包,而后面再發(fā)送的那個(gè)名義上的第三次握手包,也就是圖中黑色的那一行,被當(dāng)作了重復(fù)發(fā)送的無效包,被忽略掉了,對通信沒有造成影響。

以上就是我對這位球友問題的全部解答。

本文轉(zhuǎn)載自微信公眾號「編程技術(shù)宇宙」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系編程技術(shù)宇宙公眾號。

 

責(zé)任編輯:武曉燕 來源: 編程技術(shù)宇宙
相關(guān)推薦

2021-04-30 13:32:17

TCP三次握手網(wǎng)絡(luò)協(xié)議

2024-10-16 08:38:37

TCP編號信息

2019-05-14 10:09:06

TCP連接握手

2024-05-20 08:25:55

2022-07-05 22:18:08

TCP網(wǎng)絡(luò)

2021-04-26 05:41:32

百度網(wǎng)盤秒傳

2021-07-26 08:08:56

TCPIP網(wǎng)絡(luò)協(xié)議

2020-08-04 08:48:34

數(shù)據(jù)彈屏技術(shù)

2020-04-01 08:40:44

Vue.jsweb開發(fā)

2024-06-03 11:43:55

2022-10-10 07:34:36

TCP三次握手區(qū)塊鏈

2020-03-10 10:43:21

機(jī)器學(xué)習(xí)人工智能計(jì)算機(jī)

2019-05-28 10:45:07

TCP3次握手數(shù)據(jù)傳輸

2019-09-25 09:00:56

iPhone 11拆解蘋果

2020-05-18 08:42:23

CSS背景圖像前端開發(fā)

2021-10-29 11:45:26

Python代碼Python 3.

2021-12-02 09:20:33

TCPLinux三次握手

2021-07-10 07:40:27

Excel數(shù)據(jù)分析大數(shù)據(jù)

2022-04-26 06:43:12

文檔TCPLinux

2022-11-30 09:18:51

JavaMyBatisMQ
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號