目錄
基本網絡協議
TCP(傳輸控制協議)
可靠傳輸:序列號+確認應答+重傳機制
序列號(seq)
確認應答(ACK)
超時重傳
三次握手與四次揮手
三次握手(建立連接)
四次揮手(斷開連接)
Java Socket實現
服務器端
客戶端
相關API
UDP(用戶數據報協議)
傳輸特點
無連接(Connetionless)
不可靠傳輸(Unreliable)
高效輕量(Efficient & Lightweight)
工作流程
發送方
接收方
Java Socket實現
服務器端
客戶端
相關API
HTTP(超文本傳輸協議)
核心特征
應用層協議
請求-響應模式
無狀態
可擴展
媒體無關
通信流程(以目前主流的HTTP 1.1為例)
HTTP報文結構
請求報文(客戶端—>服務器)
請求方法分類
響應報文(服務器—>客戶端)
狀態碼分類
網絡協議分層模型
OSI七層模型(理論模型)
物理層
數據鏈路層
網絡層
傳輸層
會話層
表示層
應用層
TCP / IP 四層模型(實際應用)
網絡接口層
網絡層
傳輸層
應用層
關鍵概念——封裝與解封裝
封裝
解封裝
基本網絡協議
TCP(傳輸控制協議)
TCP屬于傳輸層的協議,通過序列號,確認應答,重傳機制保證數據的可靠傳輸,在連接前通過三次揮手建立連接,在結束時通過四次揮手斷開,我們先從序列號,確認應答,重傳機制講起
可靠傳輸:序列號+確認應答+重傳機制
序列號(seq)
序列號給每個字節的數據編號(如第一個字節編號1,第二個2,以此類推),接收方通過序列號判斷數據順序,解決亂序問題
確認應答(ACK)
ack表示確認號,內容為期望接收對方下一個字節的編號,ACK=1表示ack字段有效
接收方收到數據后,會回復一個ACK報文,附帶 “期望收到的下一個序列號ack”(如收到 1-100 字節,回復ack=101),表示 “1-100 已收到,請發 101 及以后的”
超時重傳
發送方發送數據后啟動計時器,若超時未收到ACK,則認為數據丟失,重新發送該數據,解決丟包問題
三次握手與四次揮手
三次握手(建立連接)
-
第一次握手(SYN):客戶端→服務器,發送 SYN(同步)報文,附帶一個初始序列號(如seq=100),表示 “我想和你建立連接,后續數據從 100 開始編號”。(這里SYN是TCP用于建立連接的同步位,SYN表示這是一個連接請求/應答報文)
-
第二次握手(SYN):服務器→客戶端,發送SYN+ACK(同步 + 確認)報文,附帶自己的初始序列號(如seq=200)和對客戶端的確認號(ack=101,表示 “我收到了你的 100,下次請發 101 及以后的數據”)。
-
第三次握手:客戶端→服務器,發送ACK(確認)報文,確認號為ack=201(表示 “我收到了你的 200,下次請發 201 及以后的數據”)。
四次揮手(斷開連接)
-
第一次揮手:客戶端→服務器,發送 FIN (結束)報文,表示 “我數據發完了,想斷開”。
-
第二次揮手:服務器→客戶端,發送 ACK 報文,表示 “收到你的斷開請求,我還在處理剩余數據”。
-
第三次揮手:服務器→客戶端,發送 FIN 報文,表示 “我數據也發完了,可以斷開了”。
-
第四次揮手:客戶端→服務器,發送 ACK 報文,表示 “收到,確認斷開”。
Java Socket實現
Java Socket是對 TCP 和 UDP 協議的封裝,位于java.net包中,而TCP編程主要用到ServerSocket Socket InputStream OutputStream四個類
一般Java Socket TCP通信流程為:
服務器端
創建服務器端Socket(ServerSocket)并綁定相關端口 ->等待客戶連接(使用accept阻塞方法,連接成功就返回一個Socket對象代表與客戶端連接的專用通道)->獲取IO流讀取數據
客戶端
創建客戶端Socket,并指定host和port確定IP地址和端口號 ->連接成功后,可以通過Socket對象獲取對象的連接信息 -> 獲取IO流讀取數據
相關API
//ServerSocket構造器
ServerSocket(); //未綁定的服務器套接字,后續需要bind(SocketAddress)方法綁定
ServerSocket(int port); //指定端口號
ServerSocket(int port,int backlog); //指定端口和請求的最大對列長度
ServerSocket(int port,int backlog,InetAddress bindAddr);//指定端口號隊列長度和IP地址//ServerSocket方法
Socket accept() throws IOException; //監聽客戶端連接(阻塞方法,直到有客戶端連接)
int getLocalPort(); //返回ServerSocket綁定的端口號
void close() throws IOException; //關閉ServerSocket
void setSoTimeout(int timeout) throws SocketException; //設置ServerSocket的超時時間(毫秒)
//Socket構造器
//創建連接到指定主機和端口的Socket
Socket(String host, int port) throws UnknownHostException, IOException;
// 高級用法:創建連接到指定主機和端口,并綁定本地IP和端口的Socket
Socket(InetAddress address,int port,IntAddress localAddr,int localPort) throws IOException;
//服務器端無需手動創建,直接使用返回值
Socket accept() throws IOException; // 監聽客戶端連接(阻塞方法,直到有客戶端連接)//Socket方法
InputStream getInputStream() throws IOException; //獲取輸入流(接收數據)
OutputStream getOutputStream() throws IOException; //獲取輸出流(發送數據)
InetAddrsss getInetAddrss(); //返回遠程服務器的IP地址
int getPort(); //返回本地端口號
InetAddress getLocalAddress(); //返回本地綁定的IP地址
int getLocalPort(); //返回本地綁定的端口號
void close() throws IOException; //關閉Socket連接
void setSoTimeout(int timeout) throw SocketException; //設置Socket超時時間(ms)
下面我們來看Java Socket編程中實現 TCP 編程的完整示例:
// 服務器端代碼
try (ServerSocket serverSocket = new ServerSocket(8888)) {System.out.println("服務器啟動,監聽端口8888");try (Socket clientSocket = serverSocket.accept();BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)) {String clientMessage = in.readLine();System.out.println("客戶端消息:" + clientMessage);out.println("服務器響應:" + clientMessage);} catch (IOException e) {System.err.println("處理客戶端連接時出錯:" + e.getMessage());}
} catch (IOException e) {System.err.println("啟動服務器失敗:" + e.getMessage());
}
// 客戶端代碼
try (Socket socket = new Socket("localhost", 8888); //如果要PrintWriter out = new PrintWriter(socket.getOutputStream(), true);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {out.println("Hello, Server!");String response = in.readLine();System.out.println("服務器響應:" + response);} catch (UnknownHostException e) {System.err.println("主機不可達:" + e.getMessage());
} catch (IOException e) {System.err.println("通信錯誤:" + e.getMessage());
}
UDP(用戶數據報協議)
和TCP相同,UDP也是傳輸層的協議,與TCP共同構成互聯網數據傳輸的核心基礎,比較與TCP,UDP是一種無連接,不可靠,但高效的協議,常常用于滿足對實時性要求高、可容忍少量數據丟失的場景
傳輸特點
無連接(Connetionless)
通信前無需建立連接,也無需斷開連接,且發送方直接封裝數據并發送,接收方收到后直接處理,雙方無需維護連接狀態
不可靠傳輸(Unreliable)
不保證數據的到達順序,不保證數據一定送達,無流量控制和擁堵控制,可能導致接收方緩沖區溢出
高效輕量(Efficient & Lightweight)
頭部開銷小:UDP頭部僅8字節,包含源端口,目的端口,數據長度,校驗四個字段
工作流程
發送方
應用程序將數據傳遞給UDP層,UDP層加8字節頭部,形成“用戶數據報”,數據報被傳遞給 IP ,封裝成 IP 數據包后通過網絡發送
接收方
IP層接收數據包,解封裝后將UDP數據交給UDP層,UDP層校驗數據報的完整性,若校驗失敗就直接丟棄,校驗通過后,根據目的端口將數據交給對應的應用程序
Java Socket實現
服務器端
創建DatagramSocket并綁定端口,創建接收數據包DatagramPacket,然后接收客戶端數據并處理,最好基于客戶端的IP和端口創建新的DatagramPacket并發送
客戶端
創建DatagramSocket并由系統自動分配臨時端口,指定服務器IP,端口及待發送數據創建發送數據包DatagramPacket,然后用DatagramSocket對象調用send方法,創建數據接收包DatagramPacket,調用DatagramSocket對象的receive方法,最后解析packet中服務器返回的內容
相關API
// DatagramSocket是發送和接受UDP數據包的套接字,是UDP通信的基礎
// DatagramSocket構造器
DatagramSocket() throws SocketException; //創建隨機端口的UDP套接字
DatagramSocket(int port) throws SocketException; //創建綁定指定端口的UDP套接字
DatagramSocket(int port,InetAddress laddr); //指定本地地址和端口// DatagramSocket方法
void send(DatagramPacket p) throws IOException; //發送數據包
void receive(DatagramPacket p) throws IOException; //接收數據包(阻塞方法)
void close(); //關閉套接字
InetAddress getInetAddress(); //獲取連接的遠程地址(未連接時返回null)
int getPort(); //獲取綁定的本地端口
// DatagramSocket是用于封裝UDP數據包的類,包含數據內容,長度,目標地址和端口
// DatagramSocket構造器
//指定數據,長度,目標地址和端口(發送用)
DatagramPacket(byte[] buf,int length,InetAddrss address,int port);
//創建接收緩沖區(接受用)
DatagramPacket(byte[] buf,int length);//核心方法
byte[] getData(); //獲取數據
int getLength(); //獲取數據長度
void setLength(int length); //設置數據長度(用于接收時截斷)
InetAddress getAddress(); //獲取發送方/接收方的IP地址
int getPort(); //獲取綁定的本地端口
//表示IP地址,用于指定通信的目標主機
static InetAddress getByName(String host); //通過域名或IP字符串獲取地址
static InetAddress getLocalHost() throws UnKnownHostException;//獲取本地主機地址
String getHostAddress(); //獲取IP地址字符串
String getHostNAme(); //獲取主機名
下面我們來看Java Socket編程中實現 TCP 編程的完整示例:
// 服務器端代碼
try (DatagramSocket socket = new DatagramSocket(9876)) { // 綁定端口9876System.out.println("UDP Server started on port 9876");while (true) {// 1. 接收客戶端數據byte[] receiveBuffer = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);socket.receive(receivePacket); // 阻塞等待數據// 2. 解析客戶端數據String clientMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());InetAddress clientAddress = receivePacket.getAddress();int clientPort = receivePacket.getPort();System.out.println("Received from " + clientAddress + ":" + clientPort + ": " + clientMessage);// 3. 發送響應給客戶端String response = "Server received: " + clientMessage;byte[] sendBuffer = response.getBytes();DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, clientAddress, clientPort);socket.send(sendPacket);}}catch (Exception e) {e.printStackTrace();}
//客戶端代碼
try (DatagramSocket socket = new DatagramSocket()) { // 系統自動分配端口// 1. 準備發送數據String message = "Hello, UDP Server!";byte[] sendBuffer = message.getBytes();InetAddress serverAddress = InetAddress.getByName("localhost");int serverPort = 9876;// 2. 發送數據到服務器DatagramPacket sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, serverAddress, serverPort);socket.send(sendPacket);// 3. 接收服務器響應byte[] receiveBuffer = new byte[1024];DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);socket.receive(receivePacket); // 阻塞等待響應// 4. 解析響應String serverResponse = new String(receivePacket.getData(), 0, receivePacket.getLength());System.out.println("Server response: " + serverResponse);} catch (Exception e) {e.printStackTrace();}
HTTP(超文本傳輸協議)
HTTP是互聯網中應用層的核心協議,用于規范用戶端與服務端之間超文本(HTML,圖片,視頻等)傳輸,是萬維網(WWW)的基礎,定義了數據如何被請求,傳輸和響應的規則
核心特征
應用層協議
HTTP是基于 TCP/IP 協議族,工作在IOS模型的應用層的傳輸協議
請求-響應模式
通信由客戶端主動發起請求,服務器端接收后返回響應,是典型的“客戶端-服務器”架構
無狀態
服務器不會記住用戶的信息,每次請求間無關聯
可擴展
可以自定義頭部字段,方法,狀態碼等支持功能擴展
媒體無關
可以傳輸任意類型的數據
通信流程(以目前主流的HTTP 1.1為例)
-
DNS域名解析(把網址翻譯成IP地址)
-
建立TCP連接(打通客戶端和服務端的通道,HTTP基于TCP傳輸數據,因此發送請求前先建立TCP連接(三次連接)
-
發送HTTP請求(向服務器發送HTTP請求報文,包含要什么資源,客戶端信息等)
-
服務器處理請求(根據請求準備數據,提取請求行和頭部等信息,將處理結果封裝成HTTP響應報文)
-
返回HTTP響應(返回給客戶端響應報文)
-
瀏覽器渲染頁面(解析HTML,加載相關資源,把數據渲染成頁面)
-
關閉或復用TCP連接(通道的后續處理)
HTTP報文結構
HTTP的報文分為兩種:客戶端向服務端發送的請求報文,服務端向客戶端發送的響應報文
報文均由“起始行+頭部+空行+體”四部分構成
請求報文(客戶端—>服務器)
#請求行
#方法 + 路徑 + 協議版本
GET /products?category=books&sort=price HTTP/1.1#頭部字段
#必選頭部指定目標服務器域名(HTTP/1.1強制要求)
Host: www.example.com
#客戶端標識(瀏覽器型號,設備信息)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/114.0.0.0 Safari/537.36
#客戶端可接受的響應數據類型
Accept: application/json
#客戶端支持的壓縮算法,服務器可根據此壓縮響應主體減少傳輸量
Accept-Encoding:gzip
#請求主體的數據格式
Content-Type: application/x-www-form-urlencoded
#請求主體的字節數
Content-Length: 30
#客戶端存儲的鍵值對,用于身份驗證
Cookie: sessionid=abc123
#控制TCP連接狀態,keep-alive表示復用連接,close表示響應后關閉
Connection: keep-alive#空行
#強制分隔請求頭部和主體#請求主體
#根據Content-Type定義的格式傳輸
username=test&password=123456
請求方法分類
GET:獲取資源(如網頁、圖片),無請求主體,參數附在 URL 后(?key=value)
POST:提交數據(如表單、創建資源),參數放在請求主體中,更安全(無長度限制)
PUT:全量更新資源(如替換用戶信息),需提交完整資源數據
DELETE:刪除指定資源
HEAD:僅獲取響應頭部(用于檢查資源是否存在、更新時間等,無主體)
PATCH:部分更新資源(如僅修改用戶昵稱)
響應報文(服務器—>客戶端)
#狀態行
#HTTP版本 + 狀態碼 + 原因短語
HTTP/1.1 200 OK#頭部字段
#服務器軟件標識
Server: Nginx/1.21.0
#響應主體的數據格式及編碼
Content-Type: application/json; charset=utf-8
#響應主體
Content-Length: 68
#服務器指定的資源緩存規則
Cache-Control: max-age=3600
#服務器向客戶端設置 Cookie
Set-Cookie: user=test; Path=/; Max-Age=86400; HttpOnly
#資源的最后修改時間
Last-Modified: Tue, 29 Jul 2025 10:00:00 GMT#空行
#分隔響應頭部和主體#響應主體
#根據Content-Type定義的格式傳輸返回實際的資源數據(如HTML頁面,JSON結果,圖片二進制)
{"code": 200, "message": "success", "data": {"userId": 123, "username": "test"}}
狀態碼分類
3 位數字,分類表示請求處理結果,核心分類: 1xx(信息性):請求已接收,繼續處理(如100 Continue:服務器允許客戶端繼續發送主體) 2xx(成功):請求正常處理(如200 OK:成功返回數據;204 No Content:成功但無主體) 3xx(重定向):需客戶端進一步操作(如301 Moved Permanently:資源永久遷移;304 Not Modified:資源未更新,使用緩存) 4xx(客戶端錯誤):請求存在問題(如400 Bad Request:請求格式錯誤;404 Not Found:資源不存在) 5xx(服務器錯誤):服務器處理失敗(如500 Internal Server Error:服務器內部錯誤;503 Service Unavailable:服務器暫不可用)
網絡協議分層模型
了解了這么多網絡的基本協議,我們可能會產生一個疑問:這么多的網絡協議是怎么共同運轉工作的呢?
分層模型其實分為兩種:OSI七層模型 和 TCP/IP四層模型
OSI七層模型(理論模型)
OSI是國際標準化組織(ISO)提出的理論模型,將網絡通信分為 7 層,每一層都有明確的功能邊界
物理層
定義物理介質的電氣 / 機械特性,負責 “比特流”(0/1 電信號 / 光信號)的物理傳輸,一般通過網線、光纖、無線信號(WiFi)、集線器(Hub)傳輸
數據鏈路層
實現 “同一局域網內” 兩個節點的可靠通信,處理幀同步、差錯校驗、MAC 地址識別,協議有以太網協議(Ethernet)、PPP 協議,傳輸時用交換機根據 MAC 地址轉發
網絡層
實現 “跨局域網” 通信,通過 IP 地址選擇傳輸路徑(路由),解決 “數據從 A 網到 B 網怎么走”,協議有IP 協議(IPv4/IPv6)、ICMP 協議(ping 命令),由路由器根據 IP 地址選路
傳輸層
提供 “端到端” 的可靠 / 高效傳輸,區分同一設備上的不同應用(通過端口號),協議有TCP 協議(可靠)、UDP 協議(高效)
會話層
建立、管理、終止 “應用程序之間的會話”(如數據庫連接的開啟和關閉),協議有RPC 協議(遠程過程調用)
表示層
處理數據格式轉換(加密、壓縮、編碼),確保雙方應用能理解數據,如JPEG(圖片編碼)、SSL/TLS(加密)、JSON/XML(數據格式)
應用層
直接為用戶應用提供服務,定義應用間通信的具體規則,協議有HTTP(網頁)、FTP(文件傳輸)、DNS(域名解析)、SMTP(郵件)
TCP / IP 四層模型(實際應用)
OSI模型過于理想化,實際上互聯網使用的是TCP/IP模型,將7層簡化為了四層,更貼合實際開發
網絡接口層
實際上對應OSI層的物理層+數據鏈路層,用于處理硬件細節,我們在學習網絡編程時幾乎不會直接接觸
網絡層
對應的就是OSI模型的網絡層,核心功能是操作IP與路由的相關信息,如Java中就用InetAddress類來操作IP
傳輸層
對應的就是OSI模型的傳輸層,用TCP 協議(可靠)或UDP 協議(高效)提供 “端到端” 的可靠 / 高效傳輸,區分同一設備上的不同應用(通過端口號),是Java Socket編程的核心
應用層
實際上對應OSI層的會話層 + 表示層 + 應用層,用HTTP、DNS 等協議統一處理實際應用的問題,由Java Web開發總結處理
關鍵概念——封裝與解封裝
數據在網絡中傳輸時,會經歷 “封裝” 和 “解封裝” 過程,這是分層模型的核心機制:
封裝
數據從應用層向下傳遞到物理層時,每一層會在數據前添加 “頭部”(Header,含該層的控制信息,如 TCP 頭部的端口號、IP 頭部的 IP 地址),部分層還會加 “尾部”(Trailer)
解封裝
數據從物理層向上傳遞到應用層時,每一層會剝離自己添加的頭部,最終還原出原始數據
一個HTTP請求的封裝過程
應用層:HTTP報文(如GET/index.html...)↓ (向下傳輸,添加TCP頭部:源端口、目的端口80、序號等)傳輸層:TCP段(TCP頭部 + HTTP報文)↓ (向下傳輸,添加IP頭部:源IP、目的IP、TTL等)網絡層:IP數據報(IP頭部+TCP段)↓ (向下傳輸,添加以太網頭部:源MAC、目標MAC等)數據鏈路層:以太網幀(以太網頭部 + IP數據報 + 尾部校驗)↓ (轉換為電信號/光信號)物理層:比特流(01流)