网络编程

1. 概述

1.1 网络编程的重要性

网络编程是Java开发中的重要技能,涉及Socket编程、TCP/UDP通信、NIO、Netty等核心技术,是构建分布式系统的基础。

本文内容

  • 网络基础:TCP/IP协议、Socket概念
  • Socket编程:TCP和UDP Socket编程
  • NIO:非阻塞IO和选择器
  • Netty框架:高性能网络框架
  • HTTP客户端:HTTP请求处理
  • 实战案例:网络编程实战

1.2 本文内容结构

本文将从以下几个方面深入探讨网络编程:

  1. 网络基础:TCP/IP协议和网络模型
  2. Socket编程:TCP和UDP Socket
  3. NIO编程:非阻塞IO和选择器
  4. Netty框架:Netty的使用和原理
  5. HTTP编程:HTTP客户端和服务器
  6. 实战案例:网络编程实战

2. 网络基础

2.1 TCP/IP协议

2.1.1 协议栈

TCP/IP协议栈

1
2
3
4
5
应用层    HTTP, FTP, SMTP, DNS
传输层 TCP, UDP
网络层 IP, ICMP, ARP
数据链路层 以太网, WiFi
物理层 网线, 光纤

TCP vs UDP

特性 TCP UDP
连接 面向连接 无连接
可靠性 可靠传输 不可靠传输
速度 较慢 较快
适用场景 文件传输、Web 视频、游戏

2.2 Socket概念

2.2.1 Socket简介

Socket:网络通信的端点,是IP地址和端口号的组合。

Socket通信流程

1
2
3
4
5
6
7
8
9
10
11
服务器端:
1. 创建ServerSocket,绑定端口
2. 监听客户端连接
3. 接受连接,创建Socket
4. 通过Socket进行通信
5. 关闭连接

客户端:
1. 创建Socket,连接服务器
2. 通过Socket进行通信
3. 关闭连接

3. Socket编程

3.1 TCP Socket

3.1.1 TCP服务器

TCP服务器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.io.*;
import java.net.*;

public class TCPServer {

public static void main(String[] args) {
try {
// 1. 创建ServerSocket,绑定端口
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("Server started on port 8888");

while (true) {
// 2. 监听客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());

// 3. 处理客户端请求(每个客户端一个线程)
new Thread(() -> {
try {
handleClient(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}

private static void handleClient(Socket socket) throws IOException {
try {
// 4. 获取输入输出流
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true
);

// 5. 读取客户端消息
String message;
while ((message = in.readLine()) != null) {
System.out.println("Received: " + message);

// 6. 发送响应
out.println("Echo: " + message);

if ("bye".equalsIgnoreCase(message)) {
break;
}
}
} finally {
// 7. 关闭连接
socket.close();
System.out.println("Client disconnected");
}
}
}

3.1.2 TCP客户端

TCP客户端示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.*;
import java.net.*;

public class TCPClient {

public static void main(String[] args) {
try {
// 1. 创建Socket,连接服务器
Socket socket = new Socket("localhost", 8888);
System.out.println("Connected to server");

// 2. 获取输入输出流
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true
);
BufferedReader userInput = new BufferedReader(
new InputStreamReader(System.in)
);

// 3. 发送消息
String userMessage;
while ((userMessage = userInput.readLine()) != null) {
out.println(userMessage);

// 4. 接收响应
String response = in.readLine();
System.out.println("Server: " + response);

if ("bye".equalsIgnoreCase(userMessage)) {
break;
}
}

// 5. 关闭连接
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.2 UDP Socket

3.2.1 UDP服务器

UDP服务器示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import java.net.*;

public class UDPServer {

public static void main(String[] args) {
try {
// 1. 创建DatagramSocket,绑定端口
DatagramSocket socket = new DatagramSocket(8888);
System.out.println("UDP Server started on port 8888");

byte[] buffer = new byte[1024];

while (true) {
// 2. 接收数据包
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);

// 3. 处理数据
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println("Received: " + message + " from " +
packet.getSocketAddress());

// 4. 发送响应
String response = "Echo: " + message;
byte[] responseData = response.getBytes();
DatagramPacket responsePacket = new DatagramPacket(
responseData,
responseData.length,
packet.getSocketAddress()
);
socket.send(responsePacket);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.2.2 UDP客户端

UDP客户端示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.net.*;
import java.util.Scanner;

public class UDPClient {

public static void main(String[] args) {
try {
// 1. 创建DatagramSocket
DatagramSocket socket = new DatagramSocket();

InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 8888;

Scanner scanner = new Scanner(System.in);

while (true) {
// 2. 读取用户输入
System.out.print("Enter message: ");
String message = scanner.nextLine();

// 3. 发送数据包
byte[] data = message.getBytes();
DatagramPacket packet = new DatagramPacket(
data,
data.length,
serverAddress,
serverPort
);
socket.send(packet);

// 4. 接收响应
byte[] buffer = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
socket.receive(responsePacket);

String response = new String(
responsePacket.getData(),
0,
responsePacket.getLength()
);
System.out.println("Server: " + response);

if ("bye".equalsIgnoreCase(message)) {
break;
}
}

socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

3.3 多线程服务器

3.3.1 线程池服务器

使用线程池的TCP服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class ThreadPoolTCPServer {

private static final int PORT = 8888;
private static final int THREAD_POOL_SIZE = 10;

public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

try {
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("Server started on port " + PORT);

while (true) {
Socket clientSocket = serverSocket.accept();
System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());

// 使用线程池处理客户端
threadPool.submit(() -> {
try {
handleClient(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
});
}
} catch (IOException e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}

private static void handleClient(Socket socket) throws IOException {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(
socket.getOutputStream(), true)) {

String message;
while ((message = in.readLine()) != null) {
System.out.println("Received: " + message);
out.println("Echo: " + message);

if ("bye".equalsIgnoreCase(message)) {
break;
}
}
} finally {
socket.close();
System.out.println("Client disconnected");
}
}
}

4. NIO编程

4.1 NIO基础

4.1.1 NIO核心组件

NIO核心组件

  1. Channel(通道):数据读写通道
  2. Buffer(缓冲区):数据容器
  3. Selector(选择器):多路复用器

NIO vs BIO

特性 BIO NIO
阻塞 阻塞IO 非阻塞IO
线程模型 一个连接一个线程 一个线程处理多个连接
性能 较低 较高
适用场景 连接数少 连接数多

4.2 Channel和Buffer

4.2.1 Channel使用

Channel示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class ChannelExample {

public void fileChannelExample() throws IOException {
// FileChannel:文件通道
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel channel = file.getChannel();

// 写入数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hello NIO".getBytes());
buffer.flip(); // 切换为读模式
channel.write(buffer);

// 读取数据
buffer.clear();
channel.read(buffer);
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println(new String(data));

channel.close();
file.close();
}
}

4.2.2 Buffer使用

Buffer示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.nio.*;

public class BufferExample {

public void bufferExample() {
// 创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);

// 写入数据
buffer.put("Hello".getBytes());

// 切换为读模式
buffer.flip();

// 读取数据
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}

// 清空Buffer(准备再次写入)
buffer.clear();

// Buffer状态
// position: 当前位置
// limit: 限制位置
// capacity: 容量
}
}

4.3 Selector

4.3.1 多路复用

Selector示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;

public class NIOSelectorServer {

public static void main(String[] args) {
try {
// 1. 创建ServerSocketChannel
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(8888));
serverChannel.configureBlocking(false); // 非阻塞模式

// 2. 创建Selector
Selector selector = Selector.open();

// 3. 注册ServerSocketChannel到Selector
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("NIO Server started on port 8888");

while (true) {
// 4. 选择就绪的通道
int readyChannels = selector.select();
if (readyChannels == 0) continue;

// 5. 处理就绪的通道
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();

if (key.isAcceptable()) {
// 接受连接
handleAccept(key, selector);
} else if (key.isReadable()) {
// 读取数据
handleRead(key);
}

keyIterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

private static void handleAccept(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);

// 注册读事件
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("Client connected: " + clientChannel.getRemoteAddress());
}

private static void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);

int bytesRead = channel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
String message = new String(data);
System.out.println("Received: " + message);

// 回显
buffer.clear();
buffer.put(("Echo: " + message).getBytes());
buffer.flip();
channel.write(buffer);
} else if (bytesRead < 0) {
// 客户端断开连接
channel.close();
System.out.println("Client disconnected");
}
}
}

5. Netty框架

5.1 Netty基础

5.1.1 Netty简介

Netty:高性能、异步事件驱动的网络框架。

Netty优势

  1. 高性能:基于NIO,性能优异
  2. 易用性:API简单,易于使用
  3. 稳定性:久经考验,稳定可靠
  4. 功能丰富:支持多种协议

5.2 Netty服务器

5.2.1 Netty服务器示例

Netty服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyServer {

private static final int PORT = 8888;

public static void main(String[] args) throws InterruptedException {
// 1. 创建EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接收连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理连接

try {
// 2. 创建ServerBootstrap
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 添加编解码器
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
// 添加业务处理器
pipeline.addLast(new NettyServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);

// 3. 绑定端口
ChannelFuture future = bootstrap.bind(PORT).sync();
System.out.println("Netty Server started on port " + PORT);

// 4. 等待服务器关闭
future.channel().closeFuture().sync();
} finally {
// 5. 关闭EventLoopGroup
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}

// 业务处理器
class NettyServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String message = (String) msg;
System.out.println("Received: " + message);

// 回显
ctx.writeAndFlush("Echo: " + message);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}

@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Client connected: " + ctx.channel().remoteAddress());
}

@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("Client disconnected: " + ctx.channel().remoteAddress());
}
}

5.3 Netty客户端

5.3.1 Netty客户端示例

Netty客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyClient {

private static final String HOST = "localhost";
private static final int PORT = 8888;

public static void main(String[] args) throws InterruptedException {
// 1. 创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();

try {
// 2. 创建Bootstrap
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new NettyClientHandler());
}
});

// 3. 连接服务器
ChannelFuture future = bootstrap.connect(HOST, PORT).sync();
System.out.println("Connected to server");

// 4. 发送消息
Channel channel = future.channel();
channel.writeAndFlush("Hello Netty");

// 5. 等待连接关闭
channel.closeFuture().sync();
} finally {
// 6. 关闭EventLoopGroup
group.shutdownGracefully();
}
}
}

// 客户端处理器
class NettyClientHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String message = (String) msg;
System.out.println("Server: " + message);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}

5.4 Netty编解码器

5.4.1 自定义编解码器

自定义编解码器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;

import java.util.List;

// 自定义解码器
public class CustomDecoder extends ByteToMessageDecoder {

@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
if (in.readableBytes() < 4) {
return; // 数据不足,等待
}

int length = in.readInt();
if (in.readableBytes() < length) {
in.resetReaderIndex();
return; // 数据不足,等待
}

byte[] data = new byte[length];
in.readBytes(data);
String message = new String(data);
out.add(message);
}
}

// 自定义编码器
public class CustomEncoder extends MessageToByteEncoder<String> {

@Override
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) {
byte[] data = msg.getBytes();
out.writeInt(data.length); // 写入长度
out.writeBytes(data); // 写入数据
}
}

6. HTTP编程

6.1 HTTP客户端

6.1.1 Java原生HTTP客户端

Java 11+ HTTP客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.net.http.*;
import java.net.URI;
import java.time.Duration;

public class HttpClientExample {

public void httpGetExample() throws Exception {
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.example.com"))
.GET()
.build();

HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);

System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());
}

public void httpPostExample() throws Exception {
HttpClient client = HttpClient.newHttpClient();

String json = "{\"name\":\"test\"}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/data"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();

HttpResponse<String> response = client.send(
request,
HttpResponse.BodyHandlers.ofString()
);

System.out.println("Response: " + response.body());
}
}

6.2 HTTP服务器

6.2.1 简单HTTP服务器

使用Netty的HTTP服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;

public class HttpServer {

private static final int PORT = 8080;

public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
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) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new HttpServerHandler());
}
});

ChannelFuture future = bootstrap.bind(PORT).sync();
System.out.println("HTTP Server started on port " + PORT);
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}

class HttpServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
String uri = request.uri();
String method = request.method().name();

System.out.println("Method: " + method + ", URI: " + uri);

// 构建响应
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK
);

String content = "Hello from HTTP Server";
response.content().writeBytes(content.getBytes());
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.length());

ctx.writeAndFlush(response);
}
}

7. 实战案例

7.1 聊天室服务器

7.1.1 多客户端聊天室

使用Netty实现聊天室

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

public class ChatServer {

private static final int PORT = 8888;
private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
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) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());
pipeline.addLast(new ChatServerHandler());
}
});

ChannelFuture future = bootstrap.bind(PORT).sync();
System.out.println("Chat Server started on port " + PORT);
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

static class ChatServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelActive(ChannelHandlerContext ctx) {
channels.add(ctx.channel());
String message = "User " + ctx.channel().remoteAddress() + " joined";
channels.writeAndFlush(message);
System.out.println(message);
}

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
String message = (String) msg;
String sender = ctx.channel().remoteAddress().toString();
String broadcast = "[" + sender + "] " + message;

// 广播消息给所有客户端
channels.writeAndFlush(broadcast);
System.out.println(broadcast);
}

@Override
public void channelInactive(ChannelHandlerContext ctx) {
channels.remove(ctx.channel());
String message = "User " + ctx.channel().remoteAddress() + " left";
channels.writeAndFlush(message);
System.out.println(message);
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}

7.2 文件传输

7.2.1 文件上传下载

文件传输示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.stream.ChunkedWriteHandler;

public class FileTransferServer {

public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
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) {
ChannelPipeline pipeline = ch.pipeline();
// 处理大文件传输
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new FileTransferHandler());
}
});

ChannelFuture future = bootstrap.bind(8888).sync();
System.out.println("File Transfer Server started");
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}

8. 总结

8.1 核心要点

  1. Socket编程:TCP和UDP Socket是网络编程基础
  2. NIO:非阻塞IO提高性能,适合高并发场景
  3. Netty:高性能网络框架,简化网络编程
  4. HTTP编程:HTTP客户端和服务器实现

8.2 关键理解

  1. BIO vs NIO:BIO阻塞,NIO非阻塞,NIO性能更高
  2. Netty优势:基于NIO,API简单,性能优异
  3. 线程模型:合理使用线程池,避免线程过多
  4. 编解码:正确处理数据编解码,避免数据丢失

8.3 最佳实践

  1. 使用Netty:优先使用Netty进行网络编程
  2. 合理配置:根据场景配置线程数和缓冲区大小
  3. 异常处理:完善的异常处理机制
  4. 资源管理:及时关闭连接和释放资源

相关文章