必備網(wǎng)絡基礎(chǔ):從TCP/IP到HTTP請求排查)
JavaEE初學者最容易踩的坑往往不在語法而在網(wǎng)絡。我見過不少同學代碼寫得挺溜一部署就懵本地跑得好好的換個機器就訪問不了瀏覽器里敲localhost:8080能通換成局域網(wǎng) IP 就超時。說到底就是缺了網(wǎng)絡基礎(chǔ)知識。這篇文章不聊虛的專門把 JavaEE 開發(fā)需要的那部分網(wǎng)絡底子給你補上從 IP 和端口開始到協(xié)議分層、TCP/UDP再到一次 HTTP 請求的完整旅程最后附上實戰(zhàn)中常見的排查命令和報錯解法。適合剛學完 JavaSE、準備進入 JavaEE 階段的學習者也適合那些被網(wǎng)絡問題卡過很久、想系統(tǒng)補補課的在職新人。1. JavaEE開發(fā)繞不開網(wǎng)絡——先建立全局視角1.1 三層架構(gòu)里的網(wǎng)絡鏈路JavaEE 項目說白了就是一個 Web 應用你的代碼跑在服務器上用戶通過瀏覽器訪問。一個典型的初學者項目鏈路是這樣的瀏覽器 → Tomcat → MySQL。這三者之間每一條線都是網(wǎng)絡通信。即使全部部署在同一臺電腦上這些通信也要走完整的網(wǎng)絡協(xié)議棧。道理很簡單瀏覽器和 Tomcat 之間靠 HTTP 協(xié)議通信Tomcat 和 MySQL 之間靠 MySQL 協(xié)議通信底層也是 TCP這些協(xié)議的數(shù)據(jù)都要經(jīng)過網(wǎng)卡、IP 協(xié)議棧、TCP/UDP 傳輸才能從一端到達另一端。理解這個鏈路你就明白了為什么學習 Servlet 時要接觸HttpServletRequest——它就是 Tomcat 把網(wǎng)絡傳過來的 HTTP 報文解析之后封裝出來的對象。不懂報文的格式就很難理解請求頭里的Content-Type是干嘛的也很難理解 Session 和 Cookie 為什么存在。很多初學者容易犯的一個錯誤是把網(wǎng)絡知識當成網(wǎng)管才需要學的或者操作系統(tǒng)課才需要學的覺得寫 Java 業(yè)務代碼用不上。但實際等你學到 Nginx 反向代理、微服務調(diào)用、消息隊列的時候網(wǎng)絡基礎(chǔ)不扎實的人幾乎是寸步難行。同一個請求懂網(wǎng)絡的人知道哪些耗時在網(wǎng)絡傳輸、哪些耗時在應用處理不懂的人只會猜測服務器是不是太慢了。1.2 IP地址與端口定位資源的兩個坐標網(wǎng)絡通信第一步是找到對方。IP 地址解決找哪臺機器端口解決找那臺機器上的哪個程序。這兩個東西是網(wǎng)絡通信最基本的坐標。IP 地址理解成門牌號就對了。IPv4 是四個 0~255 的數(shù)字一共 32 位類似192.168.1.10IPv6 是 128 位的十六進制表示類似fe80::1。在 JavaEE 開發(fā)里最常見的幾個 IP 段是127.0.0.1回環(huán)地址代表本機學習階段 90% 的場景都在和它打交道。192.168.x.x私有地址段局域網(wǎng)內(nèi)部使用聯(lián)調(diào)時經(jīng)常出現(xiàn)。10.x.x.x、172.16.x.x~172.31.x.x同樣是私有地址段企業(yè)內(nèi)網(wǎng)常見。端口是 0~65535 的數(shù)字一個進程可以綁定多個端口但一個端口同一時刻只能被一個進程占用。常用端口要爛熟于心HTTP 是 80HTTPS 是 443MySQL 是 3306Tomcat 默認是 8080。1~1023 的端口通常需要管理員權(quán)限才能綁定所以本地開發(fā)很少用這一段的端口。這里有個小細節(jié)值得提醒localhost和127.0.0.1不完全等價。localhost是一個主機名操作系統(tǒng)解析它時會先嘗試 IPv6 的::1如果系統(tǒng)支持再退回 IPv4 的127.0.0.1。個別情況下你在hosts文件里把localhost指向了別的 IP或者 Java 程序配置了-Djava.net.preferIPv6Addressestrue就會導致訪問 localhost 能通、訪問 127.0.0.1 不通的詭異問題。真遇到這種問題不必慌先想想這個區(qū)別。2. 網(wǎng)絡分層協(xié)議太多必須分層理解2.1 從OSI七層到TCP/IP四層網(wǎng)絡通信的細節(jié)非常多物理層有電信號、數(shù)據(jù)鏈路層有 MAC 地址、網(wǎng)絡層有 IP 路由、傳輸層有端口和流量控制、應用層有 HTTP 報文。如果不分層任何一個協(xié)議設(shè)計者都會被復雜度壓垮。所以網(wǎng)絡界最經(jīng)典的思路就是分層。教科書上必然會講 OSI 七層模型物理層、數(shù)據(jù)鏈路層、網(wǎng)絡層、傳輸層、會話層、表示層、應用層。這個概念好就好在邏輯清晰但它更像一個理論參考模型實際工業(yè)界用的是 TCP/IP 四層模型應用層HTTP、FTP、DNS、SMTP 等傳輸層TCP、UDP網(wǎng)絡層IP、ICMP、ARP網(wǎng)絡接口層以太網(wǎng)、Wi-Fi 等TCP/IP 四層模型把 OSI 的上三層應用層、表示層、會話層合并成了應用層把物理層和數(shù)據(jù)鏈路層合并成了網(wǎng)絡接口層。對 JavaEE 開發(fā)來說你需要精通的其實是三層應用層的 HTTP、傳輸層的 TCP/UDP、網(wǎng)絡層的 IP。最底下兩層平時寫代碼接觸不到但理解數(shù)據(jù)包走向時能用到。有些同學喜歡死記七層模型其實不必。我更建議你記住 TCP/IP 四層模型因為你在 Java 里寫的Socket代碼、看的 TCP 報文、調(diào)的HttpClient全都在這個模型里有明確位置。模型的意義在于每一層就像一個獨立的部門上層不用關(guān)心下層怎么實現(xiàn)下層也不用理解上層的數(shù)據(jù)含義。2.2 數(shù)據(jù)封裝一層套一層的俄羅斯套娃分層最大的好處是每一層只管自己的事。數(shù)據(jù)發(fā)送時從應用層開始逐層向下每一層都添加自己的頭部信息接收的時候反過來逐層剝離。這個過程叫封裝和解封裝。拿最經(jīng)典的場景舉例你在瀏覽器里訪問http://192.168.1.10:8080/login數(shù)據(jù)是這樣一層層裝進去的應用層構(gòu)造 HTTP 報文包括請求行、請求頭、空行、請求體。傳輸層加上 TCP 頭其中最重要的字段是源端口和目標端口目標端口 8080。網(wǎng)絡層加上 IP 頭包含源 IP 和目標 IP192.168.1.10。網(wǎng)絡接口層把整個包封裝成幀通過網(wǎng)線或 Wi-Fi 變成物理信號發(fā)出去。對方收到數(shù)據(jù)后從下往上逐層解包每層剝掉自己的頭部最終把 HTTP 報文呈現(xiàn)給應用層。這個過程很像寄快遞你把產(chǎn)品放進包裝盒快遞員在外面貼上快遞單運輸途中還有運輸編號。收件人拿到包裹后先撕掉快遞單打開包裝盒最后拿出產(chǎn)品。每一層只關(guān)心自己該處理的那一層包裝不關(guān)心里面到底是什么。這正是層的理想狀態(tài)職責單一、互相隔離。理解封裝之后你會更容易看懂抓包工具里的內(nèi)容。用 Wireshark 抓一個 HTTP 請求你看到的不是一條干凈的請求而是一堆幀 IP 頭 TCP 頭 HTTP 頭的嵌套結(jié)構(gòu)。以前覺得神秘的報文其實就是這種一層套一層的結(jié)構(gòu)。2.3 JavaEE開發(fā)者最容易忽略的層傳輸層初學者往往花很多時間看 HTTP卻對傳輸層一知半解。實際上 TCP 和 UDP 的差異直接決定了你的網(wǎng)絡應用是否穩(wěn)定。HTTP 本身基于 TCP所以你寫 Socket 編程、配置 Tomcat 線程池、理解 Keep-Alive都和 TCP 的機制密不可分。舉個例子Tomcat 默認的工作方式是為每個請求分配一個線程。這個模型背后依賴 TCP 連接的建立和釋放你把 Tomcat 的maxConnections調(diào)大并不是無腦調(diào)大因為每個 TCP 連接都會占用操作系統(tǒng)的文件描述符和內(nèi)存。理解了傳輸層你才知道這些配置背后的代價是什么。3. TCP與UDP兩種可靠性的取舍3.1 TCP為什么可靠——三次握手到四次揮手TCP 是面向連接的、可靠的、基于字節(jié)流的傳輸協(xié)議。它的可靠性不是憑空來的而是靠一系列機制保證確認應答、超時重傳、流量控制、擁塞控制。最經(jīng)典的三次握手解決的是雙方確認彼此收發(fā)能力的問題。三次握手的過程客戶端發(fā)送 SYN 報文seqx意思是我準備發(fā)送數(shù)據(jù)了你能收到嗎服務端回復 SYNACK 報文seqyackx1意思是我收到你的請求了我也準備好了你能收到我的回復嗎客戶端再發(fā)送 ACK 報文seqx1acky1意思是我收到你的回復了雙方確認完畢開始傳數(shù)據(jù)吧。為什么是三次而不是兩次因為兩次握手只能確認客戶端發(fā)送、服務端接收沒問題但服務端無法確認客戶端是否能收到自己的回復。換句話說兩次握手后服務端不知道客戶端是否已經(jīng)準備好可能存在服務端以為連接建立了客戶端卻不知道的半開狀態(tài)。三次握手讓雙方都確認了我能發(fā)、我能收、你也能收、你也能發(fā)才算是把一條雙向通道徹底打通。四次揮手則是斷開連接的過程。TCP 是全雙工協(xié)議兩個方向的數(shù)據(jù)傳輸是獨立的所以每一方向都需要單獨確認關(guān)閉客戶端發(fā)送 FIN 報文表示我沒有數(shù)據(jù)要發(fā)了請求斷開服務端回復 ACK表示收到你的斷開請求服務端發(fā)送 FIN 報文表示我也沒有數(shù)據(jù)要發(fā)了準備斷開客戶端回復 ACK表示收到連接斷開第二和第三步不能合并因為服務端在收到客戶端的 FIN 之后可能還有數(shù)據(jù)沒發(fā)完。必須先 ACK 表示收到斷開請求等數(shù)據(jù)發(fā)完再發(fā) FIN。這正是可靠的體現(xiàn)——寧可多花一次交互也不愿意丟數(shù)據(jù)。3.2 UDP的適用場景與Java中的取舍UDP 和無連接、不可靠、基于數(shù)據(jù)報的傳輸協(xié)議。沒有握手、沒有確認、沒有重傳發(fā)出去就不管了。聽起來很拉胯但它的優(yōu)點恰恰是低延遲、開銷小、無連接狀態(tài)。選擇場景其實很清晰網(wǎng)頁瀏覽、文件下載、郵件傳輸必須用 TCP數(shù)據(jù)不能丟。視頻通話、網(wǎng)絡游戲UDP 居多能容忍偶爾的丟包但不能接受卡頓和等待。DNS 查詢用 UDP因為請求和響應都非常短小不需要復雜的可靠傳輸。為什么視頻通話明明也會丟包還是用 UDP因為 TCP 的重傳機制會導致延遲劇烈波動視頻里一個關(guān)鍵幀丟了重傳反而會讓畫面卡住還不如 UDP 直接丟棄舊包渲染新幀。這就是可靠性和實時性之間的權(quán)衡。學習網(wǎng)絡協(xié)議最重要的是理解協(xié)議設(shè)計背后的取舍而不是背誦協(xié)議的名字。Java 里實現(xiàn)這兩種協(xié)議的方式也完全不同TCP用Socket客戶端和ServerSocket服務端。UDP用DatagramSocketDatagramPacket。說句實在話UDP 在 JavaEE 業(yè)務開發(fā)里用得不多面試時卻經(jīng)常被問到。你只要能答清楚TCP 可靠但慢UDP 不可靠但快選型取決于業(yè)務是否容忍數(shù)據(jù)丟失就已經(jīng)超過大半的候選人了。3.3 手寫一個最簡TCP通信Demo知識要落到代碼上才有感覺。這里用 Java 寫一個最簡的 TCP 通信讓你感受一下 Socket 編程長什么樣。服務端ServerSocket serverSocket new ServerSocket(9999); System.out.println(服務端啟動等待連接...); Socket socket serverSocket.accept(); // 阻塞等待客戶端連接 BufferedReader in new BufferedReader( new InputStreamReader(socket.getInputStream())); String line in.readLine(); System.out.println(收到客戶端消息: line); socket.close(); serverSocket.close();客戶端Socket socket new Socket(127.0.0.1, 9999); OutputStream out socket.getOutputStream(); out.write(hello server\n.getBytes()); out.flush(); socket.close();注意這個 Demo 有兩個粗糙的地方。第一服務端只接收一個客戶端就關(guān)閉了真實場景必須循環(huán)accept()第二readLine()要求客戶端發(fā)送換行符否則會一直阻塞。但這不是重點重點是理解兩個核心動作accept()是阻塞式的它會讓程序停下來等待連接流的讀寫就是普通的輸入輸出流操作。理解了這一點你會發(fā)現(xiàn)原本聽上去高深莫測的網(wǎng)絡通信本質(zhì)上就是打開一個 Socket然后像讀寫文件一樣讀寫字節(jié)流。有一個小習慣我強烈建議養(yǎng)成的網(wǎng)絡流的flush()一定要調(diào)用。很多人寫完數(shù)據(jù)沒 flush數(shù)據(jù)一直停留在緩沖區(qū)里導致對端收不到響應。這個坑在寫 NIO 和 Netty 的時候尤其容易踩。4. 從瀏覽器到服務器一次HTTP請求的完整旅程4.1 DNS解析把域名翻譯成IP地址當用戶在瀏覽器輸入www.example.com瀏覽器并不知道這個域名的服務器物理地址在哪里。它必須先做 DNS 解析把域名翻譯成 IP 地址。解析過程是這樣的瀏覽器先查本地 hosts 文件再查操作系統(tǒng) DNS 緩存然后發(fā)請求給配置的 DNS 服務器通常是路由器自動分配的或者手動指定的運營商 DNS。DNS 服務器如果在自己的緩存里找不到就會繼續(xù)向上層的根域名服務器、權(quán)威域名服務器逐級查詢直到拿到最終的 IP 地址。學習階段沒有域名怎么辦有一個非常實用的技巧手動編輯 hosts 文件比如寫一行192.168.1.10 myapp.local訪問myapp.local就等價于訪問192.168.1.10。這個技巧在聯(lián)調(diào)測試環(huán)境時非常常用因為你不需要讓每個人都記住一串冰冷的 IP。我見過不少初學同學在配置數(shù)據(jù)庫連接時把localhost改成localhost:3306還是連不上最后發(fā)現(xiàn) hosts 文件里把localhost映射到了某個不存在的 IP。排查思路其實很簡單先 ping 一下域名看解析結(jié)果是否符合預期。4.2 HTTP報文結(jié)構(gòu)請求和響應里的暗語HTTP 協(xié)議說白了是一個文本協(xié)議它的報文格式是規(guī)定好的。理解了這個格式你就能讀懂瀏覽器開發(fā)者工具里那些看似雜亂無章的 Header。一個典型的 HTTP 請求報文長這樣POST /login HTTP/1.1 Host: 192.168.1.10:8080 Content-Type: application/x-www-form-urlencoded Content-Length: 14 usernameadmin第一行是請求行包含方法、路徑和協(xié)議版本接下來是請求頭每行一個鍵值對空行之后是請求體。注意報文體和報文頭之間必須有一個空行這是 HTTP 協(xié)議規(guī)定的分隔符。對應的響應報文長這樣HTTP/1.1 200 OK Content-Type: text/html;charsetUTF-8 Content-Length: 320 html.../html狀態(tài)行HTTP/1.1 200 OK里有協(xié)議版本、狀態(tài)碼和原因短語。狀態(tài)碼的分類必須記牢2xx成功200 最常見3xx重定向301、302、3044xx客戶端錯誤404、403、4055xx服務端錯誤500、502、503很多初學者看到 404 就以為是服務器掛了其實 404 是資源不存在說明服務器是通的看到 500 才說明服務器內(nèi)部代碼報錯了。把狀態(tài)碼搞明白排錯效率會高很多。4.3 用瀏覽器開發(fā)者工具實測理論說再多不如動手看一眼真實的請求。Chrome 按 F12切到 Network 面板刷新一次頁面所有請求都會列出來Name請求的資源名Status狀態(tài)碼Type文檔類型文檔、腳本、樣式、圖片Time總耗時Waterfall時間線能看每個階段的耗時占比點擊任意一條請求切到 Headers 標簽能看到 General 里的 Request URL、Request Method、Status Code能看到 Request Headers 里的 User-Agent、Cookie、Content-Type。切換回 Response 標簽還能直接看到服務器返回的原始內(nèi)容。這個動作我建議初學者至少做十次以上。你每寫一個 Servlet 或者 SpringMVC 接口都用開發(fā)者工具看看請求和響應的完整結(jié)構(gòu)??吹讲煌埱蠓椒℅ET、POST的差異看到提交表單時 Content-Type 從application/x-www-form-urlencoded變成application/json這些畫面比翻十頁文檔都管用。如果想看更底層的 TCP 包走向裝一個 Wireshark在 loopback 網(wǎng)卡上抓包。你會看到數(shù)據(jù)先經(jīng)過三次握手然后才是 HTTP 請求出發(fā)最后四次揮手斷開。親眼看到一次完整的連接建立和斷開比背十遍三次握手四次揮手都有說服力。5. Java網(wǎng)絡編程基礎(chǔ)與真實開發(fā)中的坑5.1 Socket編程的三個認知誤區(qū)第一次接觸 Socket 編程的人普遍有三個認知誤區(qū)這里提前幫你們排掉。第一個誤區(qū)以為 Socket 是協(xié)議。其實 Socket 是操作系統(tǒng)提供的網(wǎng)絡編程接口它封裝了 TCP 或 UDP 的底層細節(jié)。你用Socket寫代碼本身不涉及 TCP 協(xié)議的實現(xiàn)那些都在操作系統(tǒng)內(nèi)核里。Java 里的Socket類只是對伯克利套接字接口的封裝。第二個誤區(qū)以為accept()之后的 Socket 可以立刻讀寫。實際上InputStream的read()方法是阻塞的沒有數(shù)據(jù)時線程會一直掛著。如果服務端開啟了連接卻沒有發(fā)送數(shù)據(jù)客戶端線程就會被卡住。這也是為什么真實項目里要用線程池處理連接否則一個阻塞的read()就能耗盡所有線程。第三個誤區(qū)以為close()只是關(guān)閉流。對 Socket 來說close()會直接關(guān)閉底層 TCP 連接。如果客戶端調(diào)用了close()服務端再向這個連接寫數(shù)據(jù)就會收到連接重置異常。正確做法是如果要通知對方我發(fā)完了但還希望接收數(shù)據(jù)需要用socket.shutdownOutput()來關(guān)閉輸出方向而不是直接close()5.2 長連接、短連接與連接池網(wǎng)絡通信里最容易被忽視的性能問題就是頻繁建立連接的開銷。HTTP 1.0 每請求一次就斷開一次 TCP 連接意味著每次請求都要經(jīng)歷三次握手和四次揮手開銷非常可觀。HTTP 1.1 引入了 Keep-Alive可以在同一個 TCP 連接上發(fā)送多個請求這就是長連接。JavaEE 開發(fā)中對長連接的應用遍地都是Tomcat 默認會維護一個線程池來處理并發(fā)連接每個連接對應一個線程這就是連接復用的思想。數(shù)據(jù)庫連接池Druid、HikariCP本質(zhì)上維護著一批到 MySQL 的 TCP 長連接避免每次執(zhí)行 SQL 都重新握手。Redis 客戶端的連接池、HTTP 客戶端的連接池機制完全相同。為什么連接池這么重要打個比方?jīng)]有連接池就像每買一次菜去一趟菜市場光是路上的時間就占了半天有連接池就是在冰箱里屯了一批菜隨用隨取。對高并發(fā)系統(tǒng)來說連接建立和釋放的開銷往往比業(yè)務邏輯本身的耗時還大所以連接池是 JavaEE 性能優(yōu)化的第一課。踩過一個具體的坑曾經(jīng)把一個 SpringBoot 項目的數(shù)據(jù)庫最大連接數(shù)配得很大結(jié)果 MySQL 服務器本身連接數(shù)上限不夠直接導致系統(tǒng)拒絕新的連接。連接池不是越大越好要根據(jù)并發(fā)量和數(shù)據(jù)庫承載能力綜合評估。5.3 網(wǎng)絡編程高頻面試題學完基礎(chǔ)之后有幾個高頻考點值得提前準備它們幾乎是 Java 后端面試的標配TCP 粘包和拆包問題TCP 是字節(jié)流沒有消息邊界。發(fā)送方發(fā)了兩條消息接收方可能一次讀完也可能分兩次讀完。解決方案一般是固定長度、特殊分隔符或者在消息頭里帶上長度字段。Netty 里的LengthFieldBasedFrameDecoder就是解決這個問題的。TIME_WAIT 是什么主動關(guān)閉連接的一方在斷開后會停留 2MSL 時間。這個狀態(tài)是為了防止舊連接的延遲數(shù)據(jù)包干擾新連接。高并發(fā)短連接場景下大量 TIME_WAIT 會占用端口資源所以要盡量復用連接。端口被占用如何定位Windows 下用netstat -ano | findstr 8080Linux 下用ss -tlnp | grep 8080然后根據(jù) PID 殺死對應進程。面試官考察這些問題的目的不是讓你背答案而是希望你體現(xiàn)出遇到過問題、思考過原理的能力。把上面的最小 Demo 自己跑一遍用抓包工具看一眼粘包現(xiàn)象你的理解深度會完全不同。6. 剛學JavaEE時遇到網(wǎng)絡問題怎么排查6.1 四個命令覆蓋90%場景學 JavaEE 的過程中每天都要和網(wǎng)絡打交道網(wǎng)絡出問題了怎么辦我的建議是不要瞎猜按順序用四個命令排查。第一個是ping。先看目標主機通不通。ping 127.0.0.1通說明本機協(xié)議棧正常ping 局域網(wǎng)IP通說明局域網(wǎng)內(nèi)網(wǎng)絡正常ping www.baidu.com通說明外網(wǎng)正常。第二個是telnet??炊丝谕ú煌?。telnet 127.0.0.1 8080如果顯示連接成功說明目標服務正在監(jiān)聽這個端口如果拒絕連接說明服務沒起來或者端口不對。Windows 7 以上系統(tǒng)默認沒裝 telnet 客戶端需要去啟用或關(guān)閉 Windows 功能里勾選。第三個是netstat/ss。看本機端口監(jiān)聽狀態(tài)。netstat -ano | findstr 8080在 Windows 下能列出占用 8080 端口的進程 PIDLinux 下推薦用ss -tlnp查看監(jiān)聽的端口和對應進程。第四個是curl。直接發(fā)起 HTTP 請求驗證應用層。curl -v http://127.0.0.1:8080/會打印出完整的連接過程和響應頭比瀏覽器更直觀。這四招配合起來基本能在五分鐘內(nèi)把問題定位到具體環(huán)節(jié)是服務沒啟動、端口被占用、網(wǎng)絡不通還是防火墻攔截。6.2 常見報錯的含義與解法把學習階段最常見的網(wǎng)絡報錯整理成了一張速查表遇到問題直接對照現(xiàn)象可能原因處理方向ConnectException: Connection refused目標端口沒有服務監(jiān)聽檢查服務是否啟動、端口號是否正確SocketTimeoutException連接超時或讀超時對方響應慢、防火墻丟包、網(wǎng)絡擁塞UnknownHostExceptionDNS 解析失敗檢查 hosts 配置、主機名拼寫B(tài)indException: Address already in use端口已占用用 netstat 找 PID 再殺進程瀏覽器提示無法訪問此網(wǎng)站服務未啟動或防火墻攔截先確認本機端口再看防火墻入站規(guī)則這里要特別說一個我早期踩過的坑在 Windows 上開著兩個 IDEA 窗口跑同一個 SpringBoot 項目一個沒關(guān)干凈另一個再啟動就報BindException。很多新手第一反應是防火墻攔截折騰半天發(fā)現(xiàn)是舊進程沒殺掉。所以遇到端口占用第一步永遠是查進程列表而不是改端口或關(guān)防火墻。6.3 學習階段的最小閉環(huán)建議最后給初學者的建議。不要一上來就學 Nginx、Kafka 這些分布式網(wǎng)絡架構(gòu)先把這個最小閉環(huán)跑通下載并啟動 Tomcat確保瀏覽器能訪問http://127.0.0.1:8080/看到默認頁面。寫一個最簡單的 Servlet部署上去觀察 Tomcat 日志中的訪問記錄。用curl和瀏覽器各訪問一次接口對比兩者發(fā)起的請求頭差異。用開發(fā)者工具看一次完整請求的請求行、請求頭、空行、請求體。在 Java 代碼里手動創(chuàng)建一個 Socket 連接本機端口感受底層網(wǎng)絡傳輸?shù)倪^程。這個閉環(huán)不需要理解太深的理論只需要親手把每一條鏈路跑通。跑通之后你對網(wǎng)絡初識這個階段的掌握就已經(jīng)超過大多數(shù)人了后面再去學 Servlet、SpringMVC、分布式通信都會順很多。說到這想起我自己的學習經(jīng)歷。當時我糾結(jié)了很久的 TCP 三次握手看遍博客也沒完全理解直到用 Wireshark 在本地抓了一次包看到 SYN、SYNACK、ACK 三個包的完整來回才一把打通。所以這篇內(nèi)容里我一直強調(diào)動手和觀測網(wǎng)絡這東西靠想象很難學好靠工具看得多了自然就通了。后續(xù)你要是把基礎(chǔ)鏈路跑通了推薦再去啃《TCP/IP詳解 卷一》那時候你會覺得每一頁都在講你親手抓過的包效率完全不一樣。