Java网络编程核心技术与实践
网络编程基础概念
网络是由多个节点通过通信链路连接而成的系统,用于实现信息交换和资源共享。在计算机领域中,网络编程指的是让不同设备之间建立连接并进行数据交互的技术手段。
计算机网络分类
- 局域网(LAN):覆盖范围小,如办公室或家庭内部网络
- 城域网(MAN):城市级别的网络覆盖
- 广域网(WAN):跨区域甚至全球范围的网络,典型代表是互联网(Internet)
其中:
- 万维网(WWW)提供网页浏览服务
- 物联网(IoT)实现物与物之间的智能互联
OSI七层参考模型
| 层级 | 名称 | 功能说明 |
|---|---|---|
| 7 | 应用层 | 提供HTTP、FTP、SMTP等用户直接使用的协议 |
| 6 | 表示层 | 处理数据格式转换、加密解密等工作 |
| 5 | 会话层 | 管理会话建立与恢复,支持断点续传 |
| 4 | 传输层 | 控制数据传输可靠性,如TCP/UDP协议 |
| 3 | 网络层 | 负责IP地址分配与路由选择 |
| 2 | 数据链路层 | 使用MAC地址确保帧正确传输 |
| 1 | 物理层 | 定义物理介质和信号传输方式,如光纤、双绞线 |
TCP/IP四层模型
- 应用层:面向用户的高层协议,如HTTP、DNS
- 传输层:提供端到端的数据传输服务(TCP/UDP)
- 网络层:基于IP协议完成主机间寻址与数据包转发
- 链路层:负责底层硬件连接,如以太网驱动
TCP与UDP对比
TCP 是一种面向连接的可靠传输协议,具有以下特点:
- 通过三次握手建立连接,四次挥手断开连接
- 保证数据顺序和完整性
- 适用于文件传输、网页访问等场景
UDP 是无连接的传输协议,其特性包括:
- 不保证送达,也不重传丢失数据
- 每个数据报最大64KB
- 适合实时性要求高的应用,如视频通话、在线游戏
IP地址详解
IP协议为每台联网设备分配唯一标识符:
- IPv4:32位地址,表示为四个0~255之间的十进制数,例如
192.168.1.1 - IPv6:128位地址,采用十六进制分段表示,如
2001:0db8::1
IPv4地址分类:
- A类:1.0.0.0 ~ 126.255.255.255(大型组织)
- B类:128.0.0.0 ~ 191.255.255.255(中型企业)
- C类:192.0.0.0 ~ 223.255.255.255(个人/小型网络)
- D类:组播地址
- E类:科研实验用途
特殊地址:
127.0.0.1表示本地回环地址,常用于测试- 常用命令:
ping测试连通性,ipconfig查看本机IP配置
端口机制
端口号用于区分同一台机器上的不同网络服务,取值范围0~65535:
- 公认端口:0–1023,由系统保留(如HTTP:80, FTP:21)
- 注册端口:1024–49151,供第三方应用程序注册使用
- 动态/私有端口:49152–65535,临时分配给客户端程序
常见服务端口:
- MySQL: 3306
- Oracle: 1521
- Tomcat: 8080
- SMTP邮件发送: 25
InetAddress 类使用
Java中的 InetAddress 类封装了IP地址相关信息,常用方法如下:
import java.net.InetAddress;
import java.io.IOException;
public class AddressDemo {
public static void main(String[] args) throws IOException {
// 获取本地主机信息
InetAddress local = InetAddress.getLocalHost();
System.out.println("本机IP: " + local.getHostAddress());
System.out.println("主机名: " + local.getHostName());
// 根据域名获取公网IP
InetAddress remote = InetAddress.getByName("www.baidu.com");
System.out.println("百度IP: " + remote.getHostAddress());
System.out.println("是否可达: " + remote.isReachable(3000));
// 获取所有关联IP(DNS轮询)
InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
for (InetAddress addr : addresses) {
System.out.println("Google IP: " + addr.getHostAddress());
}
}
}
TCP Socket 编程原理
Socket 是网络通信的基本单元,由IP地址和端口号组成唯一通信端点。
基本通信流程
服务器端步骤:
- 创建
ServerSocket并绑定监听端口 - 调用
accept()等待客户端连接(阻塞操作) - 通过输入流接收客户端请求数据
- 通过输出流向客户端发送响应
- 关闭资源
客户端步骤:
- 创建
Socket指定目标服务器IP和端口 - 通过输出流发送请求
- 通过输入流读取服务器响应
- 关闭连接
案例一:文本消息收发
服务器端实现:
ServerSocket listener = new ServerSocket(8899);
System.out.println("服务启动,等待连接...");
Socket client = listener.accept();
InputStream input = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
String msg = reader.readLine();
System.out.println("收到消息:" + msg);
reader.close(); client.close(); listener.close();
客户端实现:
Socket socket = new Socket("192.168.0.103", 8899);
OutputStream out = socket.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
writer.write("你好服务器!");
writer.newLine();
writer.flush();
writer.close(); socket.close();
案例二:文件上传功能
服务器端接收图片:
ServerSocket server = new ServerSocket(9999);
Socket conn = server.accept();
InputStream netIn = conn.getInputStream();
FileOutputStream fileOut = new FileOutputStream("received_image.jpg");
byte[] buffer = new byte[4096];
int len;
while ((len = netIn.read(buffer)) != -1) {
fileOut.write(buffer, 0, len);
}
fileOut.close(); netIn.close(); conn.close(); server.close();
客户端发送文件:
Socket sock = new Socket("192.168.0.103", 9999);
FileInputStream fileIn = new FileInputStream("local_photo.jpg");
OutputStream netOut = sock.getOutputStream();
byte[] buf = new byte[4096];
int length;
while ((length = fileIn.read(buf)) != -1) {
netOut.write(buf, 0, length);
}
netOut.flush(); // 强制发送剩余数据
fileIn.close(); netOut.close(); sock.close();
案例三:多客户端并发聊天室
为了支持多个客户端同时连接,服务器需使用多线程处理每个连接。
ServerSocket server = new ServerSocket(1122);
while (true) {
Socket client = server.accept();
new Thread(() -> {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(client.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(client.getInetAddress() + "说:" + line);
if ("bye".equalsIgnoreCase(line)) break;
}
} catch (IOException ignored) {}
finally {
try { client.close(); } catch (IOException e) {}
}
}).start();
}
客户端可循环读取键盘输入并发送:
Scanner scanner = new Scanner(System.in);
BufferedWriter sender = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
String input;
while (!(input = scanner.nextLine()).equalsIgnoreCase("bye")) {
sender.write(input);
sender.newLine();
sender.flush();
}
案例四:用户注册登录系统
利用Socket实现简单的账户管理系统,数据持久化至 properties 文件。
工具类 Tools.java:
public class Tools {
private static final String FILE_PATH = "users.properties";
public static Properties loadUserConfig() {
Properties props = new Properties();
File file = new File(FILE_PATH);
if (file.exists()) {
try (FileInputStream fis = new FileInputStream(file)) {
props.load(fis);
} catch (IOException e) {
e.printStackTrace();
}
}
return props;
}
public static void saveUserInfo(String userId, String userInfo) {
try (FileOutputStream fos = new FileOutputStream(FILE_PATH, true)) {
Properties props = new Properties();
props.setProperty(userId, userInfo);
props.store(fos, "Auto-saved user data");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注册线程 RegisterHandler:
public class RegisterHandler extends Thread {
public void run() {
try (ServerSocket srv = new ServerSocket(666);
Socket cli = srv.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(cli.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(cli.getOutputStream()))) {
System.out.println("注册服务已就绪...");
String userData = in.readLine();
String uid = parseField(userData, "id");
Properties allUsers = Tools.loadUserConfig();
if (allUsers.containsKey(uid)) {
out.write("该账号已存在");
} else {
Tools.saveUserInfo(uid, userData);
out.write("注册成功");
}
out.newLine(); out.flush();
} catch (IOException e) { e.printStackTrace(); }
}
private String parseField(String json, String key) {
String pattern = key + "\s*:\s*\"?([^,\"]+)\"?";
java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern);
java.util.regex.Matcher m = p.matcher(json);
return m.find() ? m.group(1) : null;
}
}
登录验证逻辑 LoginHandler:
public class LoginHandler extends Thread {
public void run() {
try (ServerSocket srv = new ServerSocket(777);
Socket cli = srv.accept();
BufferedReader in = new BufferedReader(
new InputStreamReader(cli.getInputStream()));
BufferedWriter out = new BufferedWriter(
new OutputStreamWriter(cli.getOutputStream()))) {
String cred = in.readLine();
String uid = parseField(cred, "id");
String pwdInput = parseField(cred, "pwd");
Properties users = Tools.loadUserConfig();
if (!users.containsKey(uid)) {
out.write("用户名或密码错误");
} else {
String stored = users.getProperty(uid);
String correctPwd = extractPassword(stored);
if (pwdInput.equals(correctPwd)) {
out.write("登录成功");
} else {
out.write("密码错误");
}
}
out.newLine(); out.flush();
} catch (IOException e) { e.printStackTrace(); }
}
private String extractPassword(String record) {
return java.util.regex.Pattern.compile("pwd\\s*:\\s*\"?([^,\"]+)\"?")
.matcher(record).find() ?
java.util.regex.Pattern.compile("pwd\\s*:\\s*\"?([^,\"]+)\"?")
.matcher(record).group(1) : "";
}
}
客户端主程序 UserClient:
public class UserClient {
public static void main(String[] args) throws Exception {
System.out.println("=== 欢迎 ===\n1. 注册 2. 登录");
Scanner scan = new Scanner(System.in);
int choice = scan.nextInt();
switch (choice) {
case 1 -> performRegistration(scan);
case 2 -> performLogin(scan);
default -> System.out.println("无效选项");
}
}
static void performRegistration(Scanner s) throws Exception {
Socket sock = new Socket("192.168.0.103", 666);
sendAndReceive(sock, buildJson(s, true));
}
static void performLogin(Scanner s) throws Exception {
Socket sock = new Socket("192.168.0.103", 777);
sendAndReceive(sock, buildJson(s, false));
}
static String buildJson(Scanner s, boolean isReg) {
System.out.print("ID: "); int id = s.nextInt();
System.out.print("密码: "); String pwd = s.next();
if (isReg) {
System.out.print("姓名: "); String name = s.next();
System.out.print("年龄: "); int age = s.nextInt();
return String.format("{\"id\":%d,\"name\":\"%s\",\"pwd\":\"%s\",\"age\":%d}",
id, name, pwd, age);
} else {
return String.format("{\"id\":%d,\"pwd\":\"%s\"}", id, pwd);
}
}
static void sendAndReceive(Socket sock, String data) throws Exception {
try (BufferedWriter w = new BufferedWriter(
new OutputStreamWriter(sock.getOutputStream()));
BufferedReader r = new BufferedReader(
new InputStreamReader(sock.getInputStream()))) {
w.write(data); w.newLine(); w.flush();
System.out.println("服务器回复:" + r.readLine());
}
}
}