前言
大家好,我是阿秀
后端,可以說是僅次于算法崗之外競爭最為激烈的崗位,而其中的服務端開發也是很多人會選擇在秋招中投遞的一個崗位,我想對于很多人來說,走上服務端開發之路的起點就是一個回聲服務器了。
今天帶大家實戰一把,真實體驗服務端底層數據交換的點點滴滴,在這過程中可以讓你看見?TCP 三次握手四次揮手的具體過程,全程干貨,不開玩笑。
環境工具
客戶端:Ubuntu 16.04 ,IP:192.168.78.128 ,簡稱為客戶端A
服務端:Ubuntu 16.04 ,IP:192.168.78.130,簡稱為服務端B
抓包利器-大白鯊??Wireshark ?以及 Linux 下的抓包命令 tcpdump
GCC:5.4.0
因為 Wireshark 的圖標就很像一條大鯊魚的魚鰭,所以又叫大白鯊,不信你看看
大白鯊
三次握手與四次揮手
這里簡單介紹下 TCP 最經典的三次握手與四次揮手
三次握手四次揮手
三次握手
第一次握手:建立連接。客戶端發送連接請求報文段,將 SYN 位置為1,seq(Sequence Number) 為 x;然后,客戶端進入 SYN_SEND 狀態,等待服務端的確認;
第二次握手:服務端收到 SYN 報文段。服務端收到客戶端的 SYN 報文段,需要對這個SYN 報文段進行確認,設置 ack(Acknowledgment Number) 為 x+1 (也就是 seq+1);同時,自己還要發送 SYN 請求信息,將 SYN 位置為1,seq 為 y;服務端將上述所有信息放到一個報文段(即 SYN+ACK 報文段)中,一并發送給客戶端,此時服務端進入 SYN_RECV 的狀態;
第三次握手:客戶端收到服務端的 SYN+ACK 報文段。然后將ack設置為y+1,向服務端發送ACK報文段,這個報文段發送完畢以后,客戶端和服務端端都進入 ESTABLISHED 狀態,完成TCP三次握手。
數據交互
成功建立連接后,客戶端與服務端就開始進行數據交互。客戶端發送數據,服務端回復收到該數據,然后交替進行下去。
四次揮手
當客戶端和服務端通過三次握手建立 TCP 連接進行可靠數據傳輸后,當數據傳送完畢,肯定是要斷開TCP連接,這里就有了神秘的“四次揮手”。
第一次揮手:客戶端設置 seq和ack,向服務端發送一個FIN報文段;此時,客戶端進入 FIN_WAIT_1 狀態;這表示客戶端沒有數據要發送給服務端了;
第二次揮手:服務端收到了客戶端發送的FIN報文段,向客戶端回一個 ACK 報文段,ack 為 seq+1;客戶端進入 FIN_WAIT_2 狀態;服務端告訴客戶端,我“同意”你的關閉請求;
第三次揮手:服務端向客戶端發送 FIN 報文段,請求關閉連接,同時服務端進入 LAST_ACK 狀態;
第四次揮手:客戶端收到服務端發送的 FIN 報文段,向服務端發送 ACK 報文段,然后客戶端進入 TIME_WAIT狀態;服務端收到客戶端的 ACK 報文段以后,就關閉連接;此時,客戶端等待 2MSL 后依然沒有收到回復,則證明服務端已正常關閉,那好,客戶端也可以關閉連接了。
思路整理
這里主要使用的是尹圣雨[韓]的著作《TCP/IP網絡編程》第 4 章中的簡易版回聲服務器來進行實驗。
所謂回聲服務端,就像小時候在回聲山谷中玩的游戲一樣,你朝山谷中大吼一聲“啊”,然后山谷也會給你一個“啊”。回聲服務端就是你向服務端發送一個“hello world”,回聲服務端也向你回復一個“hello world”。
1、將客戶端程序 echo_client.c 放在客戶端 A 中,將服務端程序 echo_server.c 放在服務端 B 中
2、在客戶端 A 中開啟一個命令行窗口,使用 tcpdump 命令監控 A、B 之間的網絡通信,并將消息保存為 pacp 文件,方便后續進行抓包分析
3、在服務端 B 中編譯程序 echo_server.c,開啟服務端程序 echo_server,監聽指定端口 2333
這里的端口號可以自己指定,在 1025-65535 之間都可以,主要是因為0-1024已經被系統占用了,比如http的80端口,ssh的22端口。而 Linux 下默認端口數在65535個,所以自己可以指定的端口號就在1025-65535之間。
4、在客戶端 A 中編譯程序 echo_client.c,并且開啟客戶端程序 echo_client,指定通話 IP 以及端口號,我在這里就是服務端 B 的IP:192.168.78.130以及 2333 端口號了
5、在客戶端 A 中發送消息“hello”,然后按 Q 退出即可
6、通信完畢,將 2 中保存的文件轉存到 Windows 環境下,使用大白鯊 Wireshark 進行網絡數據包分析。
7、分析抓到的數據包文件
開干
說了那么多,終于可以開始開干了!
1、將echo_client.c 、echo_server.c分別放在客戶端A:192.168.78.128 以及服務端B:192.168.78.130 中。
客戶端A
服務端B
2、 ?在客戶端A中新開一個命令行窗口,輸入命令:sudo tcpdump -i any tcp and host 192.168.78.130 and port 2333 -w message.pcap
1、由于tcpdump命令需要管理員權限,所以需要加上sudo命令進而獲取管理員權限。
2、這段命令的大概含義就是監控客戶端 A:192.168.78.128,和服務端?B:192.168.78.130 之間在端口號2333 上的基于TCP的數據交換,并且保存為 message.pcap 文件
tcpd數據包保存命令
可以看到,我們在輸入該條命令后,需要首先輸入 Linux 下的密碼獲取管理員權限,然后就開始監聽客戶端 A:192.168.78.128,與服務端 B:192.168.78.130之間在端口號2333上的TCP通信了。
3、接下來,我們進入含有 echo_server.c的文件夾,將服務端B的程序進行編譯,編譯命令為 gcc echo_server.c -o echo_server,可以看到當前文件夾下出現了 echo_server 程序
編譯服務器程序
接下來,開始監聽我們預先設置好的端口號 2333,命令為:./echo_server 2333,服務端開始正式監聽。
運行服務器程序
4、服務端 B 設置完畢,我們開始轉戰客戶端 A ,在 2 中使用 tcpdump 命令監聽的那個端口號不要關閉,千萬不要關閉,我們在客戶端 A 中另外新開一個命令行。
跟服務端 B 中類似,首先將客戶端 A 中的程序echo_client.c進行編譯,編譯命令:gcc echo_client.c -o echo_client
編譯客戶端程序
跟服務端 B 中類似,我們在客戶端 A 中開啟客戶端程序echo_client,指定通話IP :192.168.78.130及端口號 2333
命令為./echo_client 192.168.78.130 2333
運行客戶端程序
可以看到出現 “Connected…..”字樣,說明我們已經走完長征兩萬五千公里,成功會師啦!
客戶端 A 與服務端 B 終于成功連接了,這個時候我們再轉去看一下服務端 B 的狀態。
服務器程序狀態
在服務端 B 的監聽窗口也出現了“Connect client 1”字樣,換句話說,在服務端看來,有一個客戶端與它成功建立連接了。
5、下一步就可以開始通信了,我們在客戶端 A 中發送“hello”字樣。
在客戶端A發送消息
可以看到,我們在客戶端 A 中發送了一條消息“hello”, 服務端 B 也給了我們一個消息“hello”,這也就是我們上文中提到的回聲服務端了。
接下來,我們按照提示,輸入“Q”結束本次通話。
退出客戶端A
至此,本次通話結束。
6、最后我們在 2 中開啟 tcpdump 命令監控的界面中,按下 ctrl+ c ,結束監聽。
保存抓包文件
可以看到,提示我們一共成功捕獲了10 個packets,沒有數據包丟失。接下來,我們將捕獲文件 message.pacp傳輸到 Windows 下開始進行抓包分析。
抓包分析
可以看出一共 10 個數據包,也對應了上文中我們在 Linux 下通過tcpdump命令抓到的數據包個數。其中序號 1-3 為三次握手的數據包,序號 4-7 為兩次數據交換的數據包,8-10 為三次揮手的數據包。
抓到的10個數據包問題1:4-7 為什么是兩次數據交換呢?
回答1:我們的回聲服務端就是你發送什么數據過去,服務端發送什么數據回來,所以第一次數據交換:客戶端A發送數據”hello“到服務端B,B回復 確認收到。這也對應著4、5數據包;第二次數據交換:服務端B發送數據”hello“到客戶端A,A回復 確認收到。這也對應著6、7數據包。
問題2:說好的四次揮手呢?這里怎么只有三次了?
回答2:?因為服務端收到客戶端的 FIN 后,服務端也可以同時關閉連接,這樣就可以把 ACK 和 FIN 兩個包合并到一起發送,這樣可以節省一個網絡包,“四次揮手”變成了“三次揮手”。這樣可以節省網絡資源,省時又省力。而通常情況下,服務端收到客戶端的 FIN 后,很可能還沒發送完數據,所以就會先回復客戶端一個ACK 包,完成所有數據包的發送后,才會發送 FIN 包,也就是“四次揮手“了。
三次握手過程
三次揮手過程
第一次握手,序號為1,客戶端A:192.168.78.128 向服務端B:192.168.78.130 發送SYN請求包,seq為1796975076。
第二次握手,序號為2,服務端 B:192.168.78.130 向客戶端A:192.168.78.128 發送 SYN、ACK 請求回復包,seq為1222412335,ack為1796975077,也就是第一次握手中的 seq+1。
第三次握手,序號為3,客戶端A:192.168.78.128 向服務端B:192.168.78.130 發送 ACK 確認包,seq為1796975077,ack為為1222412336,也就是第二次握手中的 seq+1。
兩次數據交互過程
第一次數據交互:
第一次數據交互
序號為4,客戶端A:192.168.78.128 向服務端B:192.168.78.130 發送 push 消息包,可以看到下方的數據解析為“hello”,并且數據長度 len = 6。
可能有些小伙伴問“hello不是一共5個字符嗎?長度應該為5啊。”len = 6是因為“hello“長度為5,再加上結尾的‘\0’,加起來一共就是6了
序號為5,服務端B:192.168.78.130 向客戶端A:192.168.78.128 發送 ACK 確認包,表示已經收到該消息。
第二次數據交互:
第二次數據交互
序號為6,服務端B:192.168.78.130 客戶端A:192.168.78.128發送push消息包,可以看到下方的數據解析為“hello”,并且 len = 6。
序號為7,客戶端A:192.168.78.128 向服務端B:192.168.78.130 ?發送ACK確認包,表示已經收到該消息。
三次揮手過程
正式的四次揮手如下圖所示:
標準的四次揮手
我們所抓到的三次揮手如下所示:
我們抓包抓到的三次揮手
第一次揮手,序號為8:客戶端A:192.168.78.128 向服務端B:192.168.78.130 ?發送 FIN 請求斷開連接包,表示主動請求斷開鏈接。
第二三次揮手,序號為9:服務端B:192.168.78.130 向 ?客戶端A:192.168.78.128發送 FIN、ACK 確認并請求斷開消息包,表示收到上次斷開連接的請求,并請求斷開服務端到客戶端的鏈接。
可以看出,我們所抓的包中,將第二次揮手和第三次揮手合并為一個數據包了,也就是192.168.78.130->192.168.78.128的包中既有FIN也有ACK,所以這也是三次揮手而不是四次揮手的原因。
第四次揮手,序號為 10:客戶端A:192.168.78.128 向服務端B:192.168.78.130 ?發送 ACK 確認包,表示收到服務端發送過來的請求斷開連接消息,并給予回復。
結語
學會將自己所學的知識串聯起來是你邁向大佬的必經之路
。
巨人的肩膀
《TCP/IP網絡編程》- 尹圣雨[韓]
《Wireshark網絡分析就這么簡單》- 林沛滿
《Wireshark網絡分析的藝術》- 林沛滿
《計算機網絡-自頂向下方法》- ames F. Kurose、Keith W. Ross
《TCP/IP詳解 卷1:協議》- kevin R.Fall W.Richard Stevens
—END—
“你只管努力,剩下的交給時間就好,我就是活生生的例子~”