一、準(zhǔn)備工作
1.1 Wireshark
下載安裝(MAC版)
Wireshark(前稱(chēng)Ethereal)是一個(gè)網(wǎng)絡(luò)封包分析軟件瘫里。網(wǎng)絡(luò)封包分析軟件的功能是截取網(wǎng)絡(luò)封包,并盡可能顯示出最為詳細(xì)的網(wǎng)絡(luò)封包資料荡碾。Wireshark使用WinPCAP作為接口谨读,直接與網(wǎng)卡進(jìn)行數(shù)據(jù)報(bào)文交換。 -- 摘自百度百科
官網(wǎng)地址:https://www.wireshark.org/
mac版本下載地址:https://2.na.dl.wireshark.org/osx/Wireshark%203.4.9%20Intel%2064.dmg
配置啟動(dòng)
第一次下載完之后坛吁,打開(kāi)可能會(huì)碰到如下提示:
you don't have permission to capture on that device
解決方式:
whoami
cd /dev
sudo chown ${whoami}:admin bp*
執(zhí)行完上述命令重新啟動(dòng)工具即可劳殖。
1.2 netstat
Netstat是控制臺(tái)命令,是一個(gè)監(jiān)控TCP/IP網(wǎng)絡(luò)的非常有用的工具,它可以顯示路由表拨脉、實(shí)際的網(wǎng)絡(luò)連接以及每一個(gè)網(wǎng)絡(luò)接口設(shè)備的狀態(tài)信息哆姻。Netstat用于顯示與IP、TCP玫膀、UDP和ICMP協(xié)議相關(guān)的統(tǒng)計(jì)數(shù)據(jù)矛缨,一般用于檢驗(yàn)本機(jī)各端口的網(wǎng)絡(luò)連接情況。 -- 摘自百度百科
這個(gè)簡(jiǎn)單掌握一些命令參數(shù)就好
1.3 Java&IDE
這個(gè)玩意相信不用多說(shuō)帖旨,這里只需要copy一些后面會(huì)用到的代碼即可
這個(gè)程序大概就是監(jiān)聽(tīng)指定端口箕昭,當(dāng)有新的連接到來(lái)分配一個(gè)線程處理該socket的其他事件
public class BIOServer {
private final static ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool();
private static void handleSocket(Socket socket) throws IOException {
byte[] socketContent = new byte[1024];
int len;
while ((len = socket.getInputStream().read(socketContent)) != -1) {
System.out.println(
"Thread:" + Thread.currentThread().getName() + " receive message:" + new String(socketContent, 0, len));
}
}
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
Socket socket = serverSocket.accept(); // 此處將阻塞直到連接建立
EXECUTOR_SERVICE.submit(() -> {
System.out.println("New connection!,Submit it to thread:" + Thread.currentThread().getName());
try {
handleSocket(socket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
二、前景知識(shí)準(zhǔn)備
在查看TCP連接報(bào)文時(shí)解阅,主要看的就是TCP頭部信息落竹,所以這里先貼一下TCP頭部結(jié)構(gòu),有個(gè)感覺(jué):
2.2 TCP連接狀態(tài)一覽
整個(gè)TCP的連接和斷開(kāi)其實(shí)也是TCP狀態(tài)的變化货抄,所以提前了解下TCP狀態(tài)都有哪些筋量,更加有助于后面的分析。
狀態(tài) | 描述 |
---|---|
LISTEN | 表示服務(wù)器端的某個(gè)SOCKET處于監(jiān)聽(tīng)狀態(tài)碉熄,等待客戶(hù)端的連接 |
SYN-SENT | 客戶(hù)端發(fā)送syn請(qǐng)求連接服務(wù)器端時(shí)的狀態(tài) |
SYN-RECEIVED | 服務(wù)器端收到客戶(hù)端的請(qǐng)求連接syn報(bào)文時(shí)處于的狀態(tài) |
ESTABLISHED | TCP連接建立成功時(shí)的狀態(tài) |
FIN-WAIT-1 | 當(dāng)客戶(hù)端向服務(wù)端發(fā)起斷開(kāi)連接FIN報(bào)文后,進(jìn)入此狀態(tài)肋拔,等待服務(wù)端ACK確認(rèn) |
FIN-WAIT-2 | 當(dāng)客戶(hù)端收到服務(wù)端對(duì)FIN報(bào)文的ACK后锈津,進(jìn)入此狀態(tài)。此時(shí)該連接為半連接凉蜂,如果被動(dòng)關(guān)閉方一直不進(jìn)行關(guān)閉琼梆,那么該客戶(hù)端socket將一直保持該狀態(tài) |
CLOSE-WAIT | 當(dāng)服務(wù)端確認(rèn)了客戶(hù)端的FIN關(guān)閉報(bào)文之后進(jìn)入此狀態(tài);此時(shí)服務(wù)端 可能有一些數(shù)據(jù)需要處理 |
LAST-ACK | 當(dāng)被動(dòng)關(guān)閉方發(fā)送FIN關(guān)閉報(bào)文之后窿吩,就進(jìn)入該狀態(tài)等待最終客戶(hù)端的ACK確認(rèn) |
TIME-WAIT | 當(dāng)主動(dòng)關(guān)閉方收到被動(dòng)關(guān)閉方的FIN關(guān)閉報(bào)文并進(jìn)行ACK確認(rèn)之后茎杂,主動(dòng)方會(huì)進(jìn)入此狀態(tài),并且等待2MSL |
CLOSED | 關(guān)閉狀態(tài) |
三纫雁、實(shí)戰(zhàn)TCP連接的三次握手
客戶(hù)端--> telnet
服務(wù)端--> java程序
首先直接來(lái)看三次握手的整個(gè)流程:
接下來(lái)自上往下分析整個(gè)連接建立的過(guò)程煌往。
3.1 服務(wù)端創(chuàng)建并監(jiān)聽(tīng)socket
首先啟動(dòng)1.3節(jié)的java程序,通過(guò)netstat查看服務(wù)端socket狀態(tài):
netstat -a -n | grep 8888
可以看到服務(wù)端程序啟動(dòng)之后,這里socket狀態(tài)為LISTEN刽脖,正在等待新連接與其建立連接羞海。
3.2 客戶(hù)端連接服務(wù)端socket
執(zhí)行命令
telnet 127.0.0.1 8888
然后觀察Wireshark得到如下報(bào)文:
上面圈出來(lái)的三條報(bào)文就是TCP三次握手的過(guò)程了,接下來(lái)分別看一下這三條報(bào)文曲管,首先第一條
通過(guò)這里src port和dst port可以得知却邓,這是客戶(hù)端發(fā)向服務(wù)端的一條報(bào)文,這條報(bào)文帶有SYN(即SYN=1)標(biāo)志位院水,表示請(qǐng)求建立連接腊徙,那么此時(shí)客戶(hù)端的狀態(tài)應(yīng)該變?yōu)榱?strong>SYN_SEND;不過(guò)由于服務(wù)端馬上進(jìn)行ACK檬某,這個(gè)狀態(tài)捕捉不到撬腾,然后查看第二條報(bào)文:
相同,通過(guò)這里src port和dst port可以得知橙喘,這是服務(wù)度發(fā)向客戶(hù)端的一條報(bào)文时鸵,這條報(bào)文中帶有SYN和ACK標(biāo)志位,代表了服務(wù)端對(duì)客戶(hù)端請(qǐng)求的確認(rèn)厅瞎,此時(shí)并且還回復(fù)了ack=1,這里ack的值為客戶(hù)端發(fā)向服務(wù)端的報(bào)文中攜帶的Seq值+1饰潜,用于確認(rèn)服務(wù)端收到了此條報(bào)文,seq機(jī)制也是TCP實(shí)現(xiàn)報(bào)文丟失重傳從而可靠的重點(diǎn)和簸。再來(lái)看第三條報(bào)文:
同理得知這條報(bào)文是客戶(hù)端發(fā)給服務(wù)端的報(bào)文彭雾,攜帶標(biāo)志位ACK,并且回復(fù)ack=y+1,這里的y就是第二條報(bào)文中服務(wù)端發(fā)向客戶(hù)端報(bào)文攜帶的seq序列锁保。到這里TCP三次握手完畢了薯酝,接下來(lái)通過(guò)netstat查看socket狀態(tài):
netstat -a -n | grep 8888
可以看到此時(shí)客戶(hù)端的socket和服務(wù)端socket狀態(tài)都變?yōu)榱薊STABLISHED,此時(shí)我們通過(guò)客戶(hù)端和服務(wù)端進(jìn)行通信
四爽柒、實(shí)戰(zhàn)TCP連接的四次揮手
客戶(hù)端--> telnet
服務(wù)端--> java程序
4.1 關(guān)閉客戶(hù)端
當(dāng)關(guān)閉客戶(hù)端時(shí)吴菠,則客戶(hù)端會(huì)向服務(wù)端發(fā)起斷開(kāi)請(qǐng)求,服務(wù)端收到斷開(kāi)請(qǐng)求浩村,回應(yīng)客戶(hù)端:
此時(shí)查看客戶(hù)端和服務(wù)端socket狀態(tài):
可以看到客戶(hù)端socket此時(shí)因?yàn)橐呀?jīng)收到了服務(wù)端的ack做葵,所以此時(shí)狀態(tài)為FIN_WAIT_2,并且服務(wù)端socket狀態(tài)變?yōu)镃LOSE_WAIT
4.2 關(guān)閉服務(wù)端
關(guān)閉了客戶(hù)端之后心墅,再來(lái)關(guān)閉服務(wù)端進(jìn)行觀察
目前只剩下客戶(hù)端一個(gè)socket了酿矢,說(shuō)明服務(wù)端socket已經(jīng)完成了關(guān)閉,此時(shí)客戶(hù)端處于TIME_WAIT階段怎燥,需要等待2MSL之后才可以完成關(guān)閉瘫筐。
到這里差不多結(jié)束了,可能你會(huì)問(wèn)铐姚,在第二節(jié)中列了那么多狀態(tài)策肝,怎么沒(méi)看到其他狀態(tài)呢?這是因?yàn)橛行顟B(tài)存在時(shí)間過(guò)短導(dǎo)致無(wú)法捕捉到,比如為什么沒(méi)看到SYN_SEND驳糯,那是因?yàn)楫?dāng)客戶(hù)端發(fā)送SYN報(bào)文后篇梭,服務(wù)端馬上就進(jìn)行了確認(rèn),并且客戶(hù)端也馬上對(duì)服務(wù)端的確認(rèn)進(jìn)行了確認(rèn)酝枢,所以這些中間狀態(tài)很難捕捉恬偷。
五、TCP連接過(guò)程中的一些注意點(diǎn)
5.1 三次握手時(shí)
5.1.1 注意點(diǎn)一
為什么連接時(shí)只需要三次就夠了帘睦,兩次可以嗎袍患?四次可以嗎?
- 假如握手兩次
這種情況下竣付,就相當(dāng)于客戶(hù)端發(fā)送syn連接請(qǐng)求诡延,服務(wù)端進(jìn)行確認(rèn),然后連接建立成功古胆,總共兩次肆良。這樣有沒(méi)有問(wèn)題呢?我們假如在客戶(hù)端第一次發(fā)送syn連接請(qǐng)求時(shí)逸绎,由于網(wǎng)絡(luò)擁塞等原因惹恃,服務(wù)端遲遲沒(méi)有做出ACK應(yīng)答,那么此時(shí)客戶(hù)端就認(rèn)為第一次發(fā)送的syn請(qǐng)求失效棺牧,然后重發(fā)一個(gè)新的syn連接請(qǐng)求巫糙,然后建立成功,緊接著啪啪啪通訊完畢颊乘,然后啪啪啪連接斷開(kāi)参淹,看似沒(méi)有什么問(wèn)題了,但是假如最早發(fā)送的syn請(qǐng)求并非失效乏悄,只是網(wǎng)絡(luò)等原因?qū)е略撜?qǐng)求滯留浙值,正好此時(shí)該請(qǐng)求報(bào)文到達(dá)服務(wù)端,服務(wù)端收到連接請(qǐng)求建立連接成功(因?yàn)榫蛢纱伍菪。源藭r(shí)連接已經(jīng)建立了)开呐,然后等待客戶(hù)端發(fā)送數(shù)據(jù),但是其實(shí)客戶(hù)端早啪啪啪結(jié)束回家了识啦,此時(shí)服務(wù)端就只能干等著白白消耗資源!
- 假如握手四次
四次無(wú)非就是和四次揮手一樣神妹,那么多的一步多到哪里了颓哮?從三次揮手過(guò)程可以看到,在客戶(hù)端發(fā)送syn請(qǐng)求之后鸵荠,客戶(hù)端同時(shí)發(fā)給客戶(hù)端syn連接請(qǐng)求冕茅,以及對(duì)客戶(hù)端syn的ack確認(rèn),這兩步能分開(kāi)不?分開(kāi)倒也是可以姨伤,但是能一步解決為啥要分兩步哨坪?吃飽了撐的嗎不是!
5.2 四次揮手時(shí)
5.2.1 注意點(diǎn)一
為什么當(dāng)服務(wù)端發(fā)送FIN確認(rèn)斷開(kāi)后乍楚,客戶(hù)端不立馬close当编,而是進(jìn)入TIME_WAIT狀態(tài),等待2MSL之后才斷開(kāi)徒溪?
MSL(Max Segment Lifetime忿偷,最大分段生存期,指一個(gè)TCP報(bào)文在Internet上的最長(zhǎng)生存時(shí)間)
回顧一下TCP連接斷開(kāi)的過(guò)程:
1)客戶(hù)端發(fā)送FIN
2)服務(wù)端收到FIN發(fā)送ACK
3)服務(wù)端發(fā)送FIN
4)客戶(hù)端收到FIN發(fā)送ACK
假設(shè)有這種場(chǎng)景:假如第4步客戶(hù)端返回的ACK號(hào)丟失了臊泌,結(jié)果會(huì)如何呢鲤桥?這時(shí),服務(wù)器沒(méi)有接收到ACK號(hào)渠概,可能會(huì)重發(fā)一次FIN茶凳。如果這時(shí)客戶(hù)端的套接字已經(jīng)關(guān)閉刪除了,會(huì)發(fā)生什么事呢播揪?套接字被刪除贮喧,那么套接字中保存的控制信息也就跟著消失了,套接字對(duì)應(yīng)的端口號(hào)就會(huì)被釋放出來(lái)剪芍。這時(shí)塞淹,如果別的應(yīng)用程序要?jiǎng)?chuàng)建套接字,新套接字碰巧又被分配了同一個(gè)端口號(hào)罪裹,而服務(wù)器重發(fā)的FIN正好到達(dá)饱普,會(huì)怎么樣呢?本來(lái)這個(gè)FIN是要發(fā)給剛剛刪除的那個(gè)套接字的状共,但新套接字具有相同的端口號(hào)套耕,于是這個(gè)FIN就會(huì)錯(cuò)誤地跑到新套接字里面,新套接字就開(kāi)始執(zhí)行斷開(kāi)操作了峡继,這不亂了套了冯袍?
因?yàn)橹灰蛻?hù)端socket不刪除,就不會(huì)有新的socket綁定此端口碾牌,那應(yīng)該等待多久呢康愤?
這里就是上面所說(shuō)的2MSL了,根據(jù)MSL定義:一個(gè)TCP報(bào)文在Internet上的最長(zhǎng)生存時(shí)間舶吗,上面說(shuō)的情況頂多就是客戶(hù)端socket斷開(kāi)刪除之后征冷,第4步發(fā)送的ACK報(bào)文依然有效情況才會(huì)導(dǎo)致,那我們就一直等到這個(gè)報(bào)文失效了不就行了誓琼?那這樣看到貌似只要等待1MSL就夠了检激,那為啥還是2MSL呢肴捉?試想如果正好就在MSL臨界值,服務(wù)端以為收不到了重發(fā)了FIN報(bào)文叔收,那這種情況不是依然存在問(wèn)題嗎齿穗?所以保守起見(jiàn)再多等一下等到有可能重發(fā)的FIN報(bào)文也失效,這樣就不會(huì)有問(wèn)題啦饺律!
5.3 關(guān)于Wireshark
5.3.1 注意點(diǎn)一
tcp請(qǐng)求多了去了窃页,怎么才能準(zhǔn)確找到我想要的報(bào)文?
這里設(shè)置過(guò)濾就好了蓝晒,簡(jiǎn)單的方式就是根據(jù)端口過(guò)濾
5.3.2 注意點(diǎn)二
為什么過(guò)濾了之后腮出,什么也沒(méi)抓到?
這個(gè)要設(shè)置好抓包的網(wǎng)卡芝薇,因?yàn)槲覀兌际潜镜貑?dòng)的程序建立TCP胚嘲,所以這里要設(shè)置抓包lo0網(wǎng)卡