网络编程是实现不同设备间数据传输的核心技术,Java 通过java.net
包提供了丰富的 API,支持多种网络通信场景。本文将从网络编程基础概念入手,详解核心要素、架构差异,再结合 TCP 与 UDP 协议的实际案例,完整梳理 Java 网络编程的实现流程。
一、网络编程基础认知
1. 什么是网络编程?
网络编程指在网络协议规范下,实现不同计算机(或设备)上运行的程序之间的数据传输,常见应用场景包括即时通信、在线游戏、金融交易等。Java 的java.net
包封装了底层网络细节,开发者无需关注复杂的协议实现,即可快速构建网络应用。
2. 两种核心架构对比
实际开发中,网络应用主要基于B/S或C/S架构,二者各有优劣:
架构类型 | 全称 | 核心特点 | 优点 | 缺点 |
---|---|---|---|---|
B/S | 浏览器端 / 服务器端 | 客户端为浏览器,无需单独开发 | 无需开发客户端;用户免下载,打开浏览器即可使用 | 应用过大时,浏览器渲染压力大,用户体验下降 |
C/S | 应用端 / 服务器端 | 需单独开发客户端(如 APP、桌面软件) | 界面精美,交互流畅,用户体验好 | 需同时开发客户端与服务器端;用户需下载安装,更新繁琐 |
二、网络编程三要素
要实现设备间通信,必须明确IP 地址、端口号和通信协议三个核心要素,缺一不可。
1. IP 地址:设备的唯一标识
IP 地址是网络中设备的唯一编号,用于定位设备,分为IPv4
和IPv6
两个版本:
- IPv4:32 位二进制数,分 4 组(每组 8 位),格式如
192.168.1.1
,地址资源有限,已接近枯竭。 - IPv6:128 位二进制数,分 8 组(每组 16 位),格式如
2001:0db8:85a3:0000:0000:8a2e:0370:7334
,用于解决 IPv4 地址不足问题。
特殊 IP 地址:
- 私有地址:
192.168.0.0 - 192.168.255.255
,仅用于局域网(如家庭 WiFi、公司内网),不对外暴露。 - 本地回环地址:
127.0.0.1
(或localhost
),用于测试本机程序通信,数据不会发送到外部网络。
常用 CMD 命令:
ipconfig
:查看本机 IP 地址、子网掩码等网络配置。ping [IP/域名]
:测试与目标设备的网络连通性(如ping 127.0.0.1
测试本机网络)。
Java 中操作 IP:InetAddress
类
InetAddress
类封装了 IP 地址信息,可通过静态方法获取实例:
// 通过IP地址获取实例
InetAddress addr1 = InetAddress.getByName("127.0.0.1");
// 通过主机名获取实例(需DNS解析)
InetAddress addr2 = InetAddress.getByName("localhost");
// 获取主机名
System.out.println(addr1.getHostName()); // 输出:localhost
// 获取IP地址字符串
System.out.println(addr1.getHostAddress()); // 输出:127.0.0.1
2. 端口号:应用程序的唯一标识
端口号是设备内应用程序的唯一编号,用于区分同一设备上的不同服务(如浏览器用 80 端口,QQ 用特定端口)。
- 取值范围:0-65535(2 个字节表示)。
- 注意事项:
- 0-1023 为 “知名端口”,用于系统服务或常用协议(如 HTTP 用 80,HTTPS 用 443),开发者应使用 1024 以上端口。
- 一个端口号只能被一个应用程序占用,否则会抛出 “端口被占用” 异常。
3. 通信协议:数据传输的规则
协议是设备间数据传输的约定,定义了数据的格式、传输速率、错误处理等。Java 网络编程中最常用的是UDP和TCP协议:
协议类型 | 核心特点 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
UDP | 无连接、不可靠、面向数据报 | 传输速度快;无连接 overhead;支持广播 / 组播 | 不保证数据送达;数据大小限制(单次最多 64KB);可能乱序 | 即时通信(如 QQ 消息)、视频直播、DNS 查询 |
TCP | 面向连接、可靠、面向字节流 | 保证数据可靠送达(重传机制);无数据大小限制;有序 | 连接建立耗时;传输效率低于 UDP | 文件传输(如下载)、登录验证、HTTP/HTTPS 通信 |
三、UDP 协议实践:无连接通信
UDP 通信无需建立连接,发送方直接将 “数据报”(DatagramPacket
)发送到目标 IP 和端口,接收方被动接收。核心类为DatagramSocket
(用于发送 / 接收数据报)和DatagramPacket
(封装数据、目标地址和端口)。
1. UDP 发送端(UDPSendMessage)
步骤:
- 创建
DatagramSocket
对象(绑定端口,可选)。 - 封装数据为
DatagramPacket
(指定目标 IP 和端口)。 - 发送数据报。
- 释放资源。
代码实现:
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
public class UDPSendMessage {
public static void main(String[] args) throws Exception {
// 1. 创建发送端Socket(无参:随机端口;带参:指定端口)
DatagramSocket sendSocket = new DatagramSocket();
// 2. 准备数据并打包
String data = "Hello UDP!";
byte[] dataBytes = data.getBytes(); // 字符串转字节数组
InetAddress targetIp = InetAddress.getByName("127.0.0.1"); // 目标IP(本机)
int targetPort = 10086; // 目标端口
// 封装数据报:数据字节数组、长度、目标IP、目标端口
DatagramPacket packet = new DatagramPacket(
dataBytes, dataBytes.length, targetIp, targetPort
);
// 3. 发送数据
sendSocket.send(packet);
// 4. 释放资源
sendSocket.close();
}
}
2. UDP 接收端(UDPReceiveMessage)
步骤:
- 创建
DatagramSocket
对象(必须绑定与发送端一致的端口)。 - 创建空数据报(用于接收数据)。
- 阻塞接收数据(
receive()
方法会等待数据到来)。 - 解析数据报(获取数据、发送方 IP 和端口)。
- 释放资源。
代码实现:
import java.net.DatagramSocket;
import java.net.DatagramPacket;
public class UDPReceiveMessage {
public static void main(String[] args) throws Exception {
// 1. 创建接收端Socket(绑定端口,必须与发送端一致)
DatagramSocket receiveSocket = new DatagramSocket(10086);
// 2. 创建空数据报(缓冲区:1024字节,足够存储大部分UDP数据)
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// 3. 阻塞接收数据(程序会停在这里,直到收到数据)
System.out.println("UDP接收端已启动,等待数据...");
receiveSocket.receive(packet);
// 4. 解析数据
// 获取有效数据(避免缓冲区空字符)
byte[] receivedData = packet.getData();
int dataLength = packet.getLength();
String data = new String(receivedData, 0, dataLength);
// 获取发送方信息
String senderIp = packet.getAddress().getHostAddress();
int senderPort = packet.getPort();
// 打印结果
System.out.println("接收到数据:" + data);
System.out.println("发送方:" + senderIp + ":" + senderPort);
// 5. 释放资源
receiveSocket.close();
}
}
四、TCP 协议实践:可靠连接通信
TCP 是 “面向连接” 的协议,通信前必须先建立三次握手连接,通信后通过四次挥手关闭连接,保证数据可靠传输。核心类为Socket
(客户端)和ServerSocket
(服务器端),通过 IO 流实现数据读写。
1. TCP 客户端(TCPSendMessage)
客户端主动发起连接,向服务器发送数据。步骤:
- 创建
Socket
对象(指定服务器 IP 和端口,创建时即发起连接)。 - 获取
OutputStream
(从 Socket 中获取输出流,用于发送数据)。 - 发送数据(通过输出流写入字节)。
- 释放资源(先关流,再关 Socket)。
代码实现:
import java.net.Socket;
import java.io.OutputStream;
public class TCPSendMessage {
public static void main(String[] args) throws Exception {
// 1. 创建客户端Socket(指定服务器IP和端口,发起连接)
// 细节:若服务器未启动,会抛出“连接拒绝”异常
Socket clientSocket = new Socket("127.0.0.1", 10000);
// 2. 获取输出流(发送数据)
OutputStream os = clientSocket.getOutputStream();
// 3. 发送数据(字符串转字节数组)
String data = "Hello TCP! 你好,服务器";
os.write(data.getBytes());
// 4. 释放资源
os.close();
clientSocket.close();
}
}
2. TCP 服务器端(TCPReceiveMessage)
服务器端被动监听连接,接收客户端数据。步骤:
- 创建
ServerSocket
对象(绑定端口,用于监听客户端连接)。 - 阻塞等待客户端连接(
accept()
方法,返回与客户端通信的Socket
)。 - 获取
InputStream
(从 Socket 中获取输入流,用于接收数据)。 - 读取数据(通过输入流读取字节,转成字符串)。
- 释放资源(先关流,再关客户端 Socket,最后关 ServerSocket)。
代码实现:
import java.net.ServerSocket;
import java.net.Socket;
import java.io.InputStream;
import java.io.InputStreamReader;
public class TCPReceiveMessage {
public static void main(String[] args) throws Exception {
// 1. 创建服务器Socket(绑定端口,监听客户端连接)
ServerSocket serverSocket = new ServerSocket(10000);
System.out.println("TCP服务器已启动,等待客户端连接...");
// 2. 阻塞等待客户端连接(返回与客户端通信的Socket)
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接:" + clientSocket.getInetAddress().getHostAddress());
// 3. 获取输入流(接收数据)
InputStream is = clientSocket.getInputStream();
// 包装为字符流(避免中文乱码,需与客户端编码一致)
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
// 4. 读取数据(逐字符读取,直到流结束)
int readChar;
StringBuilder sb = new StringBuilder();
while ((readChar = isr.read()) != -1) {
sb.append((char) readChar);
}
System.out.println("接收到客户端数据:" + sb.toString());
// 5. 释放资源
isr.close();
is.close();
clientSocket.close();
serverSocket.close();
}
}
五、TCP 与 UDP 核心差异总结
对比维度 | UDP | TCP |
---|---|---|
连接方式 | 无连接 | 面向连接(三次握手建立,四次挥手关闭) |
可靠性 | 不可靠(数据可能丢失、乱序) | 可靠(重传机制,保证数据有序送达) |
数据限制 | 单次最多 64KB | 无限制(基于字节流,可分段传输) |
通信效率 | 高(无连接 overhead) | 低(连接建立 / 关闭耗时,确认机制开销) |
核心类 | DatagramSocket 、DatagramPacket | Socket 、ServerSocket |
适用场景 | 即时通信、直播、DNS | 文件传输、登录、HTTP/HTTPS |
六、总结
Java 网络编程的核心是基于java.net
包封装的 TCP/UDP 协议,开发者需先理解 “IP - 端口 - 协议” 三要素,再根据业务场景选择合适的协议:
- 追求效率、可接受少量数据丢失:选 UDP(如直播、消息推送)。
- 要求数据可靠、无丢失:选 TCP(如文件下载、登录验证)。
通过本文的基础概念梳理和代码实践,可掌握 Java 网络编程的核心流程,后续可进一步学习 Socket 长连接、NIO 非阻塞通信等高级内容,应对高并发网络场景。