Java网络编程
网络编程是Java语言中非常重要的一个方面,它允许Java应用程序通过网络与其他应用程序进行通信。本文将深入探讨Java网络编程的核心概念、API使用以及高级主题。
网络编程基础
网络模型概述
OSI七层模型
OSI(Open Systems Interconnection)七层模型是一个概念性框架,用于理解网络通信的工作原理:
- 物理层(Physical Layer):传输原始比特流
- 数据链路层(Data Link Layer):在网络节点间传输数据帧
- 网络层(Network Layer):负责IP寻址和路由选择
- 传输层(Transport Layer):提供端到端的通信服务
- 会话层(Session Layer):管理会话和同步
- 表示层(Presentation Layer):数据格式转换和加密
- 应用层(Application Layer):直接为应用程序提供服务
TCP/IP四层模型
TCP/IP模型是实际网络中使用最广泛的协议族,它将OSI七层模型简化为四层:
- 网络接口层(Network Interface Layer):对应OSI的物理层和数据链路层
- 网络层(Internet Layer):对应OSI的网络层,主要协议是IP
- 传输层(Transport Layer):对应OSI的传输层,主要协议有TCP和UDP
- 应用层(Application Layer):对应OSI的会话层、表示层和应用层
IP地址和端口
IP地址
IP地址是网络上设备的唯一标识:
- IPv4:32位地址,格式为点分十进制(如192.168.1.1)
- IPv6:128位地址,格式为冒分十六进制(如2001:0db8:85a3:0000:0000:8a2e:0370:7334)
端口
端口是应用程序在设备上的唯一标识,范围从0到65535:
- 知名端口(Well-known Ports):0-1023,由IANA分配给特定服务
- 注册端口(Registered Ports):1024-49151,用于特定应用程序
- 动态/私有端口(Dynamic/Private Ports):49152-65535,临时使用
TCP和UDP协议
TCP协议
TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层协议:
- 面向连接:通信前需要建立连接(三次握手),通信后需要关闭连接(四次挥手)
- 可靠传输:通过序列号、确认应答、超时重传、流量控制、拥塞控制等机制确保数据可靠传输
- 字节流:数据以字节流形式传输,没有消息边界
- 全双工:允许数据在两个方向上同时传输
UDP协议
UDP(User Datagram Protocol)是一种无连接的传输层协议:
- 无连接:通信前不需要建立连接,通信后不需要关闭连接
- 不可靠传输:不保证数据的可靠传输,可能丢失、重复或乱序
- 数据报:数据以数据报形式传输,有消息边界
- 开销小:头部开销比TCP小,传输效率高
- 适用于实时应用:如视频流、语音通话、在线游戏等
Java网络编程API
InetAddress类
InetAddress类用于表示IP地址。
// InetAddress类的使用示例
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressDemo {
public static void main(String[] args) {
try {
// 获取本地主机的InetAddress对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("本地主机名: " + localHost.getHostName());
System.out.println("本地IP地址: " + localHost.getHostAddress());
// 根据主机名获取InetAddress对象
InetAddress[] addresses = InetAddress.getAllByName("www.baidu.com");
System.out.println("百度的IP地址:");
for (InetAddress address : addresses) {
System.out.println(" - " + address.getHostAddress());
}
// 根据IP地址获取InetAddress对象
InetAddress address = InetAddress.getByName("114.114.114.114");
System.out.println("114.114.114.114的主机名: " + address.getHostName());
// 检查主机是否可达
boolean reachable = address.isReachable(5000); // 超时5秒
System.out.println("主机是否可达: " + reachable);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}URL和URLConnection类
URL类用于表示统一资源定位符,URLConnection类用于建立与URL指定资源的连接。
// URL和URLConnection类的使用示例
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
public class URLDemo {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("https://www.example.com/");
// 获取URL的各个部分
System.out.println("协议: " + url.getProtocol());
System.out.println("主机名: " + url.getHost());
System.out.println("端口: " + url.getPort());
System.out.println("路径: " + url.getPath());
System.out.println("查询参数: " + url.getQuery());
// 打开URL连接
URLConnection connection = url.openConnection();
// 设置连接属性
connection.setRequestProperty("User-Agent", "Mozilla/5.0");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
// 获取响应信息
System.out.println("内容类型: " + connection.getContentType());
System.out.println("内容长度: " + connection.getContentLength());
System.out.println("最后修改时间: " + connection.getLastModified());
// 读取响应内容
try (InputStream inputStream = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
StringBuilder content = new StringBuilder();
while ((line = reader.readLine()) != null) {
content.append(line).append("\n");
}
System.out.println("响应内容:");
System.out.println(content.substring(0, Math.min(content.length(), 200)) + "...");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}Socket编程
TCP Socket编程
TCP Socket编程使用Socket类(客户端)和ServerSocket类(服务端)。
TCP服务端示例:
// TCP服务端示例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) {
int port = 8080;
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("TCP服务端已启动,监听端口: " + port);
// 循环接受客户端连接
while (true) {
// 接受客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接: " + clientSocket.getInetAddress().getHostAddress());
// 为每个客户端创建一个新线程处理
new Thread(() -> {
try (Socket socket = clientSocket;
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)) {
// 读取客户端发送的消息
String message;
while ((message = reader.readLine()) != null) {
System.out.println("收到客户端消息: " + message);
// 向客户端发送响应
writer.println("服务端已收到: " + message);
// 如果收到exit消息,关闭连接
if ("exit".equalsIgnoreCase(message)) {
break;
}
}
System.out.println("客户端已断开连接: " + socket.getInetAddress().getHostAddress());
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}TCP客户端示例:
// TCP客户端示例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args) {
String host = "localhost";
int port = 8080;
try (Socket socket = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in))) {
System.out.println("已连接到服务端: " + host + ":" + port);
System.out.println("请输入消息(输入exit退出):");
// 创建一个线程用于读取服务端响应
Thread responseThread = new Thread(() -> {
try {
String response;
while ((response = reader.readLine()) != null) {
System.out.println("服务端响应: " + response);
}
} catch (IOException e) {
// 连接关闭时会抛出异常,这里可以忽略
}
});
responseThread.start();
// 从控制台读取用户输入并发送给服务端
String message;
while ((message = consoleReader.readLine()) != null) {
writer.println(message);
if ("exit".equalsIgnoreCase(message)) {
break;
}
}
System.out.println("已断开与服务端的连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}UDP Socket编程
UDP Socket编程使用DatagramSocket类和DatagramPacket类。
UDP服务端示例:
// UDP服务端示例
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String[] args) {
int port = 8080;
byte[] buffer = new byte[1024];
try (DatagramSocket socket = new DatagramSocket(port)) {
System.out.println("UDP服务端已启动,监听端口: " + port);
while (true) {
// 创建数据包用于接收客户端消息
DatagramPacket receivePacket = new DatagramPacket(buffer, buffer.length);
// 接收客户端消息
socket.receive(receivePacket);
// 获取客户端地址和端口
InetAddress clientAddress = receivePacket.getAddress();
int clientPort = receivePacket.getPort();
// 解析收到的消息
String message = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到客户端[" + clientAddress.getHostAddress() + ":" + clientPort + "]的消息: " + message);
// 准备响应消息
String response = "服务端已收到: " + message;
byte[] responseData = response.getBytes();
// 创建响应数据包
DatagramPacket sendPacket = new DatagramPacket(responseData, responseData.length, clientAddress, clientPort);
// 发送响应
socket.send(sendPacket);
System.out.println("已向客户端发送响应");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}UDP客户端示例:
// UDP客户端示例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPClient {
public static void main(String[] args) {
String host = "localhost";
int port = 8080;
byte[] receiveBuffer = new byte[1024];
try (DatagramSocket socket = new DatagramSocket();
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in))) {
InetAddress serverAddress = InetAddress.getByName(host);
System.out.println("UDP客户端已启动,请输入消息(输入exit退出):");
while (true) {
// 读取用户输入
String message = consoleReader.readLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
// 准备发送数据
byte[] sendData = message.getBytes();
// 创建数据包
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, port);
// 发送数据包
socket.send(sendPacket);
System.out.println("已发送消息: " + message);
// 准备接收响应
DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length);
// 设置超时时间为3秒
socket.setSoTimeout(3000);
try {
// 接收响应
socket.receive(receivePacket);
// 解析响应消息
String response = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到服务端响应: " + response);
} catch (IOException e) {
System.out.println("接收超时,未收到服务端响应");
}
}
System.out.println("UDP客户端已关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}NIO(New I/O)
NIO是Java SE 1.4引入的新I/O API,提供了更高效的I/O操作方式。NIO主要包括三个核心组件:Channel(通道)、Buffer(缓冲区)和Selector(选择器)。
Buffer(缓冲区)
Buffer是一个对象,它包含一些要写入或读取的数据。在NIO中,数据是通过Buffer读写的。
// Buffer的使用示例
import java.nio.ByteBuffer;
public class BufferDemo {
public static void main(String[] args) {
// 创建一个容量为1024字节的ByteBuffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 输出初始状态
System.out.println("初始状态:");
System.out.println("position: " + buffer.position());
System.out.println("limit: " + buffer.limit());
System.out.println("capacity: " + buffer.capacity());
// 写入数据到buffer
String message = "Hello, NIO!";
buffer.put(message.getBytes());
// 输出写入后的状态
System.out.println("\n写入数据后:");
System.out.println("position: " + buffer.position());
System.out.println("limit: " + buffer.limit());
// 切换到读模式
buffer.flip();
// 输出切换到读模式后的状态
System.out.println("\n切换到读模式后:");
System.out.println("position: " + buffer.position());
System.out.println("limit: " + buffer.limit());
// 读取数据
byte[] data = new byte[buffer.limit()];
buffer.get(data);
String readMessage = new String(data);
System.out.println("\n读取的数据: " + readMessage);
// 输出读取后的状态
System.out.println("\n读取数据后:");
System.out.println("position: " + buffer.position());
System.out.println("limit: " + buffer.limit());
// 清空buffer,准备再次写入
buffer.clear();
// 输出清空后的状态
System.out.println("\n清空buffer后:");
System.out.println("position: " + buffer.position());
System.out.println("limit: " + buffer.limit());
}
}Channel(通道)
Channel是一个可以读写数据的通道,与传统的流不同,Channel是双向的。
// Channel的使用示例(文件Channel)
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelDemo {
public static void main(String[] args) {
String sourceFile = "source.txt";
String destFile = "dest.txt";
try (FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(destFile);
FileChannel sourceChannel = fis.getChannel();
FileChannel destChannel = fos.getChannel()) {
// 创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 从源文件读取数据并写入目标文件
int bytesRead;
while ((bytesRead = sourceChannel.read(buffer)) != -1) {
// 切换到读模式
buffer.flip();
// 写入目标通道
while (buffer.hasRemaining()) {
destChannel.write(buffer);
}
// 清空缓冲区,准备下一次读取
buffer.clear();
}
System.out.println("文件复制完成");
} catch (IOException e) {
e.printStackTrace();
}
}
}Selector(选择器)
Selector是一个多路复用器,可以监控多个Channel的事件。使用Selector可以实现单线程管理多个连接。
// NIO服务端示例(使用Selector)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
public static void main(String[] args) {
try {
// 创建Selector
Selector selector = Selector.open();
// 创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 将ServerSocketChannel注册到Selector,关注OP_ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO服务端已启动,监听端口: 8080");
while (true) {
// 阻塞等待事件发生
int readyChannels = selector.select();
if (readyChannels == 0) {
continue;
}
// 获取所有就绪的SelectionKey
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
// 处理接受连接事件
if (key.isAcceptable()) {
handleAccept(key, selector);
}
// 处理读取事件
if (key.isReadable()) {
handleRead(key);
}
// 移除已处理的SelectionKey
keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
// 将SocketChannel注册到Selector,关注OP_READ事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("客户端已连接: " + socketChannel.getRemoteAddress());
}
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead == -1) {
// 客户端关闭连接
socketChannel.close();
System.out.println("客户端已断开连接");
return;
}
// 切换到读模式
buffer.flip();
// 读取数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到客户端消息: " + message);
// 回显消息给客户端
ByteBuffer responseBuffer = ByteBuffer.wrap("服务端已收到: ".getBytes());
socketChannel.write(responseBuffer);
}
}// NIO客户端示例
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;
public class NIOClient {
public static void main(String[] args) {
try {
// 创建SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8080));
socketChannel.configureBlocking(false);
System.out.println("NIO客户端已连接到服务端");
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入消息(输入exit退出): ");
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
// 发送消息给服务端
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(buffer);
// 读取服务端响应
buffer.clear();
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] responseData = new byte[buffer.remaining()];
buffer.get(responseData);
String response = new String(responseData);
System.out.println("服务端响应: " + response);
}
// 短暂休眠,避免CPU占用过高
Thread.sleep(100);
}
// 关闭连接
socketChannel.close();
scanner.close();
System.out.println("NIO客户端已关闭");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}Java NIO.2(AIO)
NIO.2(Asynchronous I/O)是Java SE 7引入的异步I/O API,提供了真正的异步非阻塞I/O操作。
AsynchronousSocketChannel和AsynchronousServerSocketChannel
// AIO服务端示例
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AIOServer {
public static void main(String[] args) {
try {
// 创建AsynchronousServerSocketChannel
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
System.out.println("AIO服务端已启动,监听端口: 8080");
// 接受连接
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
// 继续接受其他连接
serverSocketChannel.accept(null, this);
try {
System.out.println("客户端已连接: " + socketChannel.getRemoteAddress());
// 准备读取数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取数据
socketChannel.read(buffer, buffer, new ReadHandler(socketChannel));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("接受连接失败: " + exc.getMessage());
}
});
// 保持主线程运行
Thread.sleep(Integer.MAX_VALUE);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
// 读取处理器
private static class ReadHandler implements CompletionHandler<Integer, ByteBuffer> {
private final AsynchronousSocketChannel socketChannel;
public ReadHandler(AsynchronousSocketChannel socketChannel) {
this.socketChannel = socketChannel;
}
@Override
public void completed(Integer result, ByteBuffer buffer) {
if (result > 0) {
// 切换到读模式
buffer.flip();
// 读取数据
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("收到客户端消息: " + message);
// 回显消息给客户端
ByteBuffer responseBuffer = ByteBuffer.wrap("服务端已收到: ".getBytes());
socketChannel.write(responseBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
// 继续读取数据
buffer.clear();
socketChannel.read(buffer, buffer, ReadHandler.this);
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("写入数据失败: " + exc.getMessage());
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
} else if (result == -1) {
// 客户端关闭连接
try {
System.out.println("客户端已断开连接");
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
System.out.println("读取数据失败: " + exc.getMessage());
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}// AIO客户端示例
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
public class AIOClient {
public static void main(String[] args) {
try {
// 创建AsynchronousSocketChannel
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
CountDownLatch latch = new CountDownLatch(1);
// 连接服务端
socketChannel.connect(new InetSocketAddress("localhost", 8080), latch, new CompletionHandler<Void, CountDownLatch>() {
@Override
public void completed(Void result, CountDownLatch attachment) {
System.out.println("已连接到服务端");
attachment.countDown();
}
@Override
public void failed(Throwable exc, CountDownLatch attachment) {
System.out.println("连接服务端失败: " + exc.getMessage());
attachment.countDown();
}
});
// 等待连接完成
latch.await();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入消息(输入exit退出): ");
String message = scanner.nextLine();
if ("exit".equalsIgnoreCase(message)) {
break;
}
// 发送消息给服务端
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
CountDownLatch writeLatch = new CountDownLatch(1);
socketChannel.write(buffer, writeLatch, new CompletionHandler<Integer, CountDownLatch>() {
@Override
public void completed(Integer result, CountDownLatch attachment) {
// 读取服务端响应
ByteBuffer responseBuffer = ByteBuffer.allocate(1024);
socketChannel.read(responseBuffer, responseBuffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
if (result > 0) {
buffer.flip();
byte[] responseData = new byte[buffer.remaining()];
buffer.get(responseData);
String response = new String(responseData);
System.out.println("服务端响应: " + response);
}
attachment.countDown();
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
System.out.println("读取响应失败: " + exc.getMessage());
attachment.countDown();
}
});
}
@Override
public void failed(Throwable exc, CountDownLatch attachment) {
System.out.println("发送消息失败: " + exc.getMessage());
attachment.countDown();
}
});
// 等待写入和读取完成
writeLatch.await();
}
// 关闭连接
socketChannel.close();
scanner.close();
System.out.println("AIO客户端已关闭");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}高级主题
线程池在网络编程中的应用
在高并发的网络应用中,使用线程池可以有效地管理线程资源,提高系统性能。
// 使用线程池的TCP服务端示例
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTCPServer {
private static final int PORT = 8080;
private static final int THREAD_POOL_SIZE = 10;
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
System.out.println("线程池TCP服务端已启动,监听端口: " + PORT);
System.out.println("线程池大小: " + THREAD_POOL_SIZE);
while (true) {
// 接受客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接: " + clientSocket.getInetAddress().getHostAddress());
// 提交任务给线程池
executorService.submit(() -> handleClient(clientSocket));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭线程池
executorService.shutdown();
}
}
private static void handleClient(Socket socket) {
try (Socket clientSocket = socket;
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true)) {
// 读取客户端发送的消息
String message;
while ((message = reader.readLine()) != null) {
System.out.println("收到客户端消息: " + message);
// 向客户端发送响应
writer.println("服务端已收到: " + message);
// 如果收到exit消息,关闭连接
if ("exit".equalsIgnoreCase(message)) {
break;
}
}
System.out.println("客户端已断开连接: " + socket.getInetAddress().getHostAddress());
} catch (IOException e) {
System.out.println("处理客户端连接时发生错误: " + e.getMessage());
}
}
}安全的网络通信(SSL/TLS)
SSL(Secure Sockets Layer)和TLS(Transport Layer Security)是用于在网络上提供安全通信的协议。Java提供了JSSE(Java Secure Socket Extension)来支持SSL/TLS。
// SSL/TLS服务端示例
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.security.KeyStore;
public class SSLServer {
private static final int PORT = 8443;
private static final String KEYSTORE_PATH = "server.keystore";
private static final String KEYSTORE_PASSWORD = "password";
private static final String KEY_PASSWORD = "password";
public static void main(String[] args) {
try {
// 初始化SSL上下文
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore keyStore = KeyStore.getInstance("JKS");
// 加载密钥库
try (FileInputStream fis = new FileInputStream(KEYSTORE_PATH)) {
keyStore.load(fis, KEYSTORE_PASSWORD.toCharArray());
}
// 初始化密钥管理器
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, KEY_PASSWORD.toCharArray());
// 初始化SSL上下文
sslContext.init(kmf.getKeyManagers(), null, null);
// 创建SSL服务器套接字工厂
SSLServerSocketFactory socketFactory = sslContext.getServerSocketFactory();
// 创建SSL服务器套接字
try (SSLServerSocket serverSocket = (SSLServerSocket) socketFactory.createServerSocket(PORT)) {
// 设置需要客户端认证(可选)
// serverSocket.setNeedClientAuth(true);
System.out.println("SSL服务端已启动,监听端口: " + PORT);
while (true) {
// 接受客户端连接
try (SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream(), true)) {
System.out.println("客户端已连接: " + clientSocket.getInetAddress().getHostAddress());
// 读取客户端发送的消息
String message;
while ((message = reader.readLine()) != null) {
System.out.println("收到客户端消息: " + message);
// 向客户端发送响应
writer.println("服务端已收到: " + message);
// 如果收到exit消息,关闭连接
if ("exit".equalsIgnoreCase(message)) {
break;
}
}
System.out.println("客户端已断开连接: " + clientSocket.getInetAddress().getHostAddress());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}// SSL/TLS客户端示例
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.security.KeyStore;
public class SSLClient {
private static final String HOST = "localhost";
private static final int PORT = 8443;
private static final String TRUSTSTORE_PATH = "client.truststore";
private static final String TRUSTSTORE_PASSWORD = "password";
public static void main(String[] args) {
try {
// 初始化SSL上下文
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore trustStore = KeyStore.getInstance("JKS");
// 加载信任库
try (FileInputStream fis = new FileInputStream(TRUSTSTORE_PATH)) {
trustStore.load(fis, TRUSTSTORE_PASSWORD.toCharArray());
}
// 初始化信任管理器
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
// 初始化SSL上下文
sslContext.init(null, tmf.getTrustManagers(), null);
// 创建SSL套接字工厂
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
// 创建SSL套接字
try (SSLSocket socket = (SSLSocket) socketFactory.createSocket(HOST, PORT);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in))) {
System.out.println("已连接到SSL服务端: " + HOST + ":" + PORT);
System.out.println("请输入消息(输入exit退出):");
// 创建一个线程用于读取服务端响应
Thread responseThread = new Thread(() -> {
try {
String response;
while ((response = reader.readLine()) != null) {
System.out.println("服务端响应: " + response);
}
} catch (Exception e) {
// 连接关闭时会抛出异常,这里可以忽略
}
});
responseThread.start();
// 从控制台读取用户输入并发送给服务端
String message;
while ((message = consoleReader.readLine()) != null) {
writer.println(message);
if ("exit".equalsIgnoreCase(message)) {
break;
}
}
System.out.println("已断开与SSL服务端的连接");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}HTTP客户端编程
Java提供了多种HTTP客户端API,包括传统的HttpURLConnection、Apache HttpClient和Java 11引入的HttpClient。
使用Java 11 HttpClient
// Java 11 HttpClient示例
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
public class HttpClientDemo {
public static void main(String[] args) {
// 创建HttpClient
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.connectTimeout(Duration.ofSeconds(10))
.build();
// 创建HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/octocat"))
.header("Accept", "application/json")
.GET()
.build();
try {
// 同步发送请求
System.out.println("发送同步请求...");
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 处理响应
System.out.println("响应状态码: " + response.statusCode());
System.out.println("响应头:");
response.headers().map().forEach((key, values) -> {
System.out.println(" " + key + ": " + String.join(", ", values));
});
System.out.println("响应体(前200字符):");
System.out.println(response.body().substring(0, Math.min(response.body().length(), 200)) + "...");
// 异步发送请求
System.out.println("\n发送异步请求...");
CompletableFuture<HttpResponse<String>> futureResponse =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
// 处理异步响应
futureResponse.thenAccept(resp -> {
System.out.println("异步响应状态码: " + resp.statusCode());
System.out.println("异步响应体长度: " + resp.body().length() + " 字符");
}).join(); // 等待异步操作完成
} catch (Exception e) {
e.printStackTrace();
}
// 发送POST请求示例
sendPostRequest(client);
}
private static void sendPostRequest(HttpClient client) {
try {
String jsonBody = "{\"name\": \"test\", \"description\": \"test repository\"}";
HttpRequest postRequest = HttpRequest.newBuilder()
.uri(URI.create("https://httpbin.org/post"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
System.out.println("\n发送POST请求...");
HttpResponse<String> response = client.send(postRequest, HttpResponse.BodyHandlers.ofString());
System.out.println("POST响应状态码: " + response.statusCode());
System.out.println("POST响应体(前200字符):");
System.out.println(response.body().substring(0, Math.min(response.body().length(), 200)) + "...");
} catch (Exception e) {
e.printStackTrace();
}
}
}WebSocket编程
WebSocket提供了全双工通信通道,允许服务器主动向客户端推送数据。Java提供了对WebSocket的支持。
// WebSocket客户端示例
import javax.websocket.ClientEndpoint;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import java.net.URI;
import java.util.Scanner;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;
@ClientEndpoint
public class WebSocketClient {
private Session session;
@OnOpen
public void onOpen(Session session) {
this.session = session;
System.out.println("WebSocket连接已建立");
}
@OnMessage
public void onMessage(String message) {
System.out.println("收到消息: " + message);
}
public void sendMessage(String message) {
try {
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://echo.websocket.org";
System.out.println("连接到WebSocket服务端: " + uri);
WebSocketClient client = new WebSocketClient();
container.connectToServer(client, URI.create(uri));
Scanner scanner = new Scanner(System.in);
System.out.println("WebSocket客户端已启动,请输入消息(输入exit退出):");
String message;
while ((message = scanner.nextLine()) != null) {
if ("exit".equalsIgnoreCase(message)) {
break;
}
client.sendMessage(message);
}
scanner.close();
System.out.println("WebSocket客户端已关闭");
} catch (Exception e) {
e.printStackTrace();
}
}
}网络编程最佳实践
异常处理
在网络编程中,异常处理非常重要,需要妥善处理各种可能的异常情况。
// 异常处理示例
public class ExceptionHandlingDemo {
public static void connectToServer(String host, int port) {
Socket socket = null;
try {
socket = new Socket(host, port);
System.out.println("已连接到服务器: " + host + ":" + port);
// 执行网络操作...
} catch (java.net.UnknownHostException e) {
System.err.println("未知主机: " + host);
} catch (java.net.ConnectException e) {
System.err.println("连接被拒绝,服务器可能未运行或端口不正确: " + host + ":" + port);
} catch (java.net.SocketTimeoutException e) {
System.err.println("连接超时: " + host + ":" + port);
} catch (java.io.IOException e) {
System.err.println("IO异常: " + e.getMessage());
} catch (Exception e) {
System.err.println("发生未知异常: " + e.getMessage());
} finally {
// 确保关闭Socket
if (socket != null && !socket.isClosed()) {
try {
socket.close();
System.out.println("Socket已关闭");
} catch (IOException e) {
System.err.println("关闭Socket时发生错误: " + e.getMessage());
}
}
}
}
public static void main(String[] args) {
connectToServer("localhost", 8080);
}
}资源管理
在网络编程中,需要确保正确关闭所有网络资源,避免资源泄漏。
// 使用try-with-resources进行资源管理
public class ResourceManagementDemo {
public static void readFromServer(String host, int port) {
try (Socket socket = new Socket(host, port);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)) {
// 发送请求
writer.println("GET / HTTP/1.1");
writer.println("Host: " + host);
writer.println();
// 读取响应
String line;
StringBuilder response = new StringBuilder();
while ((line = reader.readLine()) != null) {
response.append(line).append("\n");
}
System.out.println("服务器响应:");
System.out.println(response.substring(0, Math.min(response.length(), 500)) + "...");
} catch (Exception e) {
e.printStackTrace();
}
// 所有资源会自动关闭,不需要手动在finally块中关闭
}
public static void main(String[] args) {
readFromServer("www.example.com", 80);
}
}超时设置
为网络操作设置超时时间可以防止线程长时间阻塞。
// 设置网络超时示例
public class TimeoutDemo {
public static void connectWithTimeout(String host, int port, int connectTimeout, int readTimeout) {
try (Socket socket = new Socket()) {
// 设置连接超时
socket.connect(new InetSocketAddress(host, port), connectTimeout);
// 设置读取超时
socket.setSoTimeout(readTimeout);
System.out.println("已连接到服务器: " + host + ":" + port);
// 执行网络操作...
} catch (SocketTimeoutException e) {
System.err.println("操作超时: " + e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 连接超时5秒,读取超时10秒
connectWithTimeout("www.example.com", 80, 5000, 10000);
}
}性能优化
网络编程中的性能优化涉及多个方面:
- 使用缓冲区:减少实际的网络传输次数
- 选择合适的协议:根据需求选择TCP或UDP
- 使用NIO:对于高并发场景,使用NIO可以提高性能
- 线程池管理:合理使用线程池处理并发连接
- 连接复用:尽量复用连接,避免频繁创建和关闭连接
// 使用缓冲区优化网络传输
public class BufferOptimizationDemo {
public static void sendLargeData(Socket socket, byte[] data) throws IOException {
try (OutputStream out = socket.getOutputStream()) {
// 创建缓冲区
byte[] buffer = new byte[8192];
int bytesRead;
// 使用缓冲区发送数据
for (int i = 0; i < data.length; i += buffer.length) {
int length = Math.min(buffer.length, data.length - i);
System.arraycopy(data, i, buffer, 0, length);
out.write(buffer, 0, length);
out.flush();
}
}
}
}总结
Java网络编程提供了丰富的API和功能,从基础的Socket编程到高级的NIO和AIO,从同步阻塞到异步非阻塞,Java为开发者提供了多种选择来实现网络通信。在实际开发中,需要根据应用的需求和场景选择合适的技术和API,并遵循最佳实践,如异常处理、资源管理、超时设置和性能优化等,以确保网络应用的稳定性、可靠性和高性能。