1. Java网络编程基础概念
网络编程是现代软件开发中不可或缺的核心技能之一。作为Java开发者,掌握网络编程意味着能够构建分布式系统、实现服务间通信以及处理各种网络协议。Java从诞生之初就内置了强大的网络编程支持,这使得它成为企业级应用开发的首选语言。
1.1 网络协议基础
在Java网络编程中,我们主要处理两种传输层协议:TCP和UDP。TCP(传输控制协议)提供可靠的、面向连接的通信,确保数据按顺序到达且不丢失。而UDP(用户数据报协议)则是无连接的,提供更高效的传输但不保证可靠性。
TCP协议的三次握手过程:
- 客户端发送SYN包到服务器,进入SYN_SENT状态
- 服务器收到SYN包,回应SYN-ACK包,进入SYN_RCVD状态
- 客户端收到SYN-ACK包,发送ACK包,双方进入ESTABLISHED状态
提示:TCP的可靠性是通过序列号、确认应答、超时重传等机制实现的,这也是为什么它比UDP更适合需要可靠传输的场景。
1.2 Java网络编程核心类
Java.net包提供了网络编程的核心类:
- InetAddress:表示IP地址
- Socket:客户端TCP套接字
- ServerSocket:服务器端TCP套接字
- DatagramSocket:UDP套接字
- URL/URLConnection:用于HTTP协议访问
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. TCP编程实战
2.1 基础TCP客户端/服务器实现
下面是一个最简单的TCP服务器实现:
java复制// TCP服务器端
public class SimpleServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("服务器启动,等待连接...");
while (true) {
Socket socket = serverSocket.accept(); // 阻塞等待客户端连接
System.out.println("客户端连接成功:" + socket.getInetAddress());
// 获取输入输出流
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
// 读取客户端消息
String msg = in.readLine();
System.out.println("收到客户端消息:" + msg);
// 回复客户端
out.println("服务器已收到你的消息:" + msg);
// 关闭连接
socket.close();
}
}
}
对应的TCP客户端:
java复制// TCP客户端
public class SimpleClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", 8080);
// 获取输入输出流
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);
// 关闭连接
socket.close();
}
}
2.2 多线程TCP服务器
实际应用中,服务器需要同时处理多个客户端连接。下面是一个多线程服务器的实现:
java复制public class MultiThreadServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("多线程服务器启动...");
while (true) {
Socket clientSocket = serverSocket.accept();
// 为每个客户端创建一个新线程
new Thread(new ClientHandler(clientSocket)).start();
}
}
static class ClientHandler implements Runnable {
private final Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
String inputLine;
while ((inputLine = in.readLine()) != null) {
System.out.println("收到消息:" + inputLine);
out.println("已处理:" + inputLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意:虽然多线程服务器可以处理并发连接,但线程创建和销毁的开销较大。在实际生产环境中,通常会使用线程池(NIO或Netty等框架)来提高性能。
3. UDP编程实战
3.1 基础UDP通信实现
UDP编程相比TCP更简单,因为它不需要建立连接。下面是一个UDP服务器和客户端的实现:
java复制// UDP服务器
public class UDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(9090);
byte[] buffer = new byte[1024];
System.out.println("UDP服务器启动...");
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); // 阻塞等待数据
String received = new String(
packet.getData(), 0, packet.getLength());
System.out.println("收到UDP消息:" + received);
// 获取客户端地址和端口
InetAddress clientAddress = packet.getAddress();
int clientPort = packet.getPort();
// 发送响应
String response = "UDP响应:" + received;
byte[] responseData = response.getBytes();
DatagramPacket responsePacket = new DatagramPacket(
responseData, responseData.length, clientAddress, clientPort);
socket.send(responsePacket);
}
}
}
对应的UDP客户端:
java复制// UDP客户端
public class UDPClient {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
// 服务器地址和端口
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 9090;
// 发送消息
String message = "Hello UDP Server";
byte[] sendData = message.getBytes();
DatagramPacket sendPacket = new DatagramPacket(
sendData, sendData.length, serverAddress, serverPort);
socket.send(sendPacket);
// 接收响应
byte[] receiveData = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(
receiveData, receiveData.length);
socket.receive(receivePacket); // 阻塞等待响应
String response = new String(
receivePacket.getData(), 0, receivePacket.getLength());
System.out.println("收到UDP响应:" + response);
socket.close();
}
}
3.2 UDP广播实现
UDP支持广播和多播,这在某些场景下非常有用。下面是一个UDP广播的示例:
java复制public class UDPBroadcaster {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
socket.setBroadcast(true); // 启用广播
String message = "这是广播消息";
byte[] buffer = message.getBytes();
// 广播地址
InetAddress broadcastAddress = InetAddress.getByName("255.255.255.255");
int port = 9090;
DatagramPacket packet = new DatagramPacket(
buffer, buffer.length, broadcastAddress, port);
socket.send(packet);
System.out.println("广播消息已发送");
socket.close();
}
}
4. 高级网络编程技术
4.1 NIO非阻塞IO
Java NIO(New IO)提供了非阻塞IO支持,可以构建高性能的网络应用。核心组件包括:
- Channel:类似流,但可以同时读写
- Buffer:数据容器
- Selector:多路复用器,可以监控多个Channel的状态
下面是一个简单的NIO服务器示例:
java复制public class NIOServer {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO服务器启动...");
while (true) {
selector.select(); // 阻塞直到有事件发生
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
if (key.isAcceptable()) {
// 处理新连接
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println("客户端连接:" + client.getRemoteAddress());
} else if (key.isReadable()) {
// 处理读事件
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = client.read(buffer);
if (bytesRead == -1) {
client.close();
continue;
}
buffer.flip();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
String message = new String(bytes);
System.out.println("收到消息:" + message);
// 回写响应
String response = "NIO响应:" + message;
ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
client.write(responseBuffer);
}
iter.remove();
}
}
}
}
4.2 使用Netty框架
Netty是一个高性能的网络应用框架,简化了NIO编程。下面是一个简单的Netty服务器示例:
java复制public class NettyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println("收到消息:" + msg);
ctx.writeAndFlush("Netty响应:" + msg);
}
});
}
});
ChannelFuture future = bootstrap.bind(8080).sync();
System.out.println("Netty服务器启动...");
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
5. 常见问题与性能优化
5.1 网络编程常见问题
-
连接超时:客户端连接服务器时可能因网络问题导致超时
- 解决方案:设置合理的连接超时时间
java复制Socket socket = new Socket(); socket.connect(new InetSocketAddress("host", port), 5000); // 5秒超时 -
粘包/拆包问题:TCP是流式协议,消息可能会被合并或拆分
- 解决方案:使用固定长度、分隔符或自定义协议头
-
资源泄漏:忘记关闭Socket或流
- 解决方案:使用try-with-resources
java复制try (Socket socket = new Socket("host", port); InputStream in = socket.getInputStream()) { // 使用socket }
5.2 性能优化技巧
- 使用连接池:对于频繁创建销毁的连接,使用连接池管理
- 合理设置缓冲区大小:根据应用场景调整Socket缓冲区
java复制socket.setReceiveBufferSize(64 * 1024); // 64KB socket.setSendBufferSize(64 * 1024); - 启用TCP_NODELAY:禁用Nagle算法,减少小数据包的延迟
java复制socket.setTcpNoDelay(true); - 使用异步IO:对于高并发场景,考虑使用NIO或Netty
6. 实际应用场景
6.1 实现简单的HTTP服务器
java复制public class SimpleHttpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("HTTP服务器启动,访问 http://localhost:8080");
while (true) {
try (Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()))) {
// 读取HTTP请求头
String requestLine = in.readLine();
System.out.println("请求: " + requestLine);
// 发送HTTP响应
out.println("HTTP/1.1 200 OK");
out.println("Content-Type: text/html; charset=utf-8");
out.println();
out.println("<html><body>");
out.println("<h1>简单的HTTP服务器</h1>");
out.println("<p>当前时间: " + new Date() + "</p>");
out.println("</body></html>");
}
}
}
}
6.2 实现文件传输功能
java复制public class FileTransferServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9090);
System.out.println("文件传输服务器启动...");
while (true) {
try (Socket socket = serverSocket.accept();
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream())) {
// 读取文件名和长度
String fileName = dis.readUTF();
long fileLength = dis.readLong();
// 创建文件输出流
try (FileOutputStream fos = new FileOutputStream("received_" + fileName)) {
byte[] buffer = new byte[4096];
int read = 0;
long remaining = fileLength;
// 接收文件内容
while ((read = dis.read(buffer, 0, (int) Math.min(buffer.length, remaining))) > 0) {
fos.write(buffer, 0, read);
remaining -= read;
}
}
System.out.println("文件接收完成: " + fileName);
dos.writeUTF("文件接收成功");
}
}
}
}
在实际开发中,Java网络编程的应用远不止这些基础示例。从简单的客户端/服务器通信,到复杂的分布式系统架构,网络编程都是核心技术之一。掌握好这些基础知识后,可以进一步学习RPC框架、消息队列、微服务等更高级的网络应用开发技术。
