目录
基本网络协议
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流)