Netty學習路線圖 - 第四階段:Netty基礎應用
📚 Netty學習系列之四
本文是Netty學習路線的第四篇,我們將用大白話講解Netty的基礎應用,帶你從理論走向實踐。
寫在前面
大家好!在前面三篇文章中,我們學習了Java基礎、NIO編程和Netty的核心概念。但是光有理論可不行,這次我們就動手實踐,看看Netty到底能干些啥!
本文我們會通過幾個實用的例子,一步步帶你掌握Netty的基礎應用,包括:
- 搭建簡單的Netty服務器和客戶端
- 實現一個迷你HTTP服務器
- 開發一個WebSocket聊天室
- 設計自定義協議
- 玩轉編解碼器
好了,話不多說,我們直接開始動手吧!
一、搭建簡單Netty服務器與客戶端
1. 先來個Echo服務器
Echo服務器是最簡單的網絡應用之一 - 它就像個"復讀機",客戶端發啥它就回啥。我們就從這個入手。
首先,我們需要創建一個Maven項目,并添加Netty依賴:
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.86.Final</version>
</dependency>
接下來,我們創建一個處理器(Handler),它負責處理客戶端消息:
public class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {// 接收到消息后直接發回去ctx.write(msg);}@Overridepublic void channelReadComplete(ChannelHandlerContext ctx) {// 刷新隊列中的數據ctx.flush();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {// 異常處理cause.printStackTrace();ctx.close();}
}
然后是服務器主類:
public class EchoServer {public static void main(String[] args) throws Exception {// 創建兩個線程池EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接收連接EventLoopGroup workerGroup = new NioEventLoopGroup(); // 處理數據try {// 創建服務器啟動器ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 使用NIO.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new EchoServerHandler());}});// 綁定端口并啟動ChannelFuture f = b.bind(8888).sync();System.out.println("Echo服務器已啟動,端口:8888");// 等待服務器關閉f.channel().closeFuture().sync();} finally {// 優雅地關閉線程池bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
2. 再來個Echo客戶端
服務器有了,我們還需要一個客戶端來測試:
public class EchoClientHandler extends ChannelInboundHandlerAdapter {private final ByteBuf message;public EchoClientHandler() {// 創建一條測試消息message = Unpooled.buffer();message.writeBytes("你好,Netty!".getBytes());}@Overridepublic void channelActive(ChannelHandlerContext ctx) {// 連接建立后發送消息ctx.writeAndFlush(message.copy());}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {// 收到服務器回復ByteBuf in = (ByteBuf) msg;System.out.println("收到服務器回復: " + in.toString(CharsetUtil.UTF_8));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}
客戶端主類:
public class EchoClient {public static void main(String[] args) throws Exception {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new EchoClientHandler());}});// 連接服務器ChannelFuture f = b.connect("localhost", 8888).sync();System.out.println("已連接到服務器");// 等待連接關閉f.channel().closeFuture().sync();} finally {group.shutdownGracefully();}}
}
運行這兩個程序,你就能看到客戶端發送的消息被服務器原樣返回了!這就是一個最基礎的Netty應用。
3. 關于這個例子的幾點解釋
可能有人會問,這個例子看起來代碼挺多的,比Socket編程復雜啊?別急,我來解釋一下Netty的優勢:
- 異步非阻塞:雖然代碼看著多,但Netty是完全異步的,可以處理成千上萬的連接
- 線程模型清晰:BossGroup負責接收連接,WorkerGroup負責處理數據
- Pipeline機制:可以輕松添加多個處理器,形成處理鏈
- 擴展性強:這個例子很簡單,但架構適用于任何復雜度的應用
二、實現HTTP服務器
接下來,我們來實現一個簡單的HTTP服務器。這比Echo服務器稍微復雜一點,但Netty已經為我們提供了HTTP編解碼器,省了不少事。
1. HTTP服務器處理器
public class HttpServerHandler extends SimpleChannelInboundHandler<HttpObject> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) {if (msg instanceof HttpRequest) {HttpRequest request = (HttpRequest) msg;// 獲取請求URIString uri = request.uri();System.out.println("收到請求: " + uri);// 構建響應內容StringBuilder content = new StringBuilder();content.append("<!DOCTYPE html>\r\n");content.append("<html>\r\n");content.append("<head><title>Netty HTTP 服務器</title></head>\r\n");content.append("<body>\r\n");content.append("<h1>你好,這是一個Netty HTTP服務器</h1>\r\n");content.append("<p>請求路徑: ").append(uri).append("</p>\r\n");content.append("</body>\r\n");content.append("</html>\r\n");// 創建響應FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,Unpooled.copiedBuffer(content.toString(), CharsetUtil.UTF_8));// 設置響應頭response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());// 發送響應ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}
2. HTTP服務器主類
public class HttpServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline pipeline = ch.pipeline();// 添加HTTP編解碼器pipeline.addLast(new HttpServerCodec());// 添加HTTP對象聚合器pipeline.addLast(new HttpObjectAggregator(65536));// 添加我們的處理器pipeline.addLast(new HttpServerHandler());}});ChannelFuture f = b.bind(8080).sync();System.out.println("HTTP服務器已啟動,訪問地址: http://localhost:8080");f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
啟動這個服務器后,打開瀏覽器訪問 http://localhost:8080 ,你就能看到一個網頁了!是不是很神奇?
3. 關鍵點解析
看起來我們只寫了幾十行代碼,就實現了一個HTTP服務器,這是怎么做到的?關鍵在于Netty的幾個特殊處理器:
- HttpServerCodec:HTTP編解碼器,負責將字節流轉換為HTTP請求/響應
- HttpObjectAggregator:HTTP消息聚合器,將HTTP消息的多個部分合并成一個完整的HTTP請求或響應
- SimpleChannelInboundHandler:簡化的入站處理器,幫我們自動釋放資源
通過這些處理器的組合,我們可以輕松處理HTTP請求,而不用關心底層的編解碼細節。
三、WebSocket應用開發
接下來,我們實現一個簡單的WebSocket聊天室。WebSocket是HTML5的一個新特性,允許瀏覽器和服務器建立持久連接,非常適合聊天應用。
1. WebSocket處理器
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {// 用于保存所有WebSocket連接private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);@Overrideprotected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {if (msg instanceof HttpRequest) {// 處理HTTP請求,WebSocket握手HttpRequest request = (HttpRequest) msg;// 如果是WebSocket請求,進行升級if (isWebSocketRequest(request)) {// 將HTTP升級為WebSocketctx.pipeline().replace(this, "websocketHandler", new WebSocketFrameHandler());// 執行握手handleHandshake(ctx, request);} else {// 如果不是WebSocket請求,返回HTML頁面sendHttpResponse(ctx, request, getWebSocketHtml());}} else {// 如果不是HTTP請求,傳遞給下一個處理器ctx.fireChannelRead(msg);}}private boolean isWebSocketRequest(HttpRequest req) {return req.headers().contains(HttpHeaderNames.UPGRADE, "websocket", true);}private void handleHandshake(ChannelHandlerContext ctx, HttpRequest req) {WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://" + req.headers().get(HttpHeaderNames.HOST) + "/websocket", null, false);WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);if (handshaker == null) {WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());} else {handshaker.handshake(ctx.channel(), req);}}private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, String html) {FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,Unpooled.copiedBuffer(html, CharsetUtil.UTF_8));res.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");ctx.writeAndFlush(res).addListener(ChannelFutureListener.CLOSE);}private String getWebSocketHtml() {return "<!DOCTYPE html>\r\n" +"<html>\r\n" +"<head>\r\n" +" <title>WebSocket聊天室</title>\r\n" +" <script type=\"text/javascript\">\r\n" +" var socket;\r\n" +" if (window.WebSocket) {\r\n" +" socket = new WebSocket(\"ws://\" + window.location.host + \"/websocket\");\r\n" +" socket.onmessage = function(event) {\r\n" +" var chat = document.getElementById('chat');\r\n" +" chat.innerHTML += event.data + '<br>';\r\n" +" };\r\n" +" socket.onopen = function(event) {\r\n" +" console.log(\"WebSocket已連接\");\r\n" +" };\r\n" +" socket.onclose = function(event) {\r\n" +" console.log(\"WebSocket已關閉\");\r\n" +" };\r\n" +" } else {\r\n" +" alert(\"瀏覽器不支持WebSocket!\");\r\n" +" }\r\n" +" \r\n" +" function send(message) {\r\n" +" if (!socket) return;\r\n" +" if (socket.readyState == WebSocket.OPEN) {\r\n" +" socket.send(message);\r\n" +" }\r\n" +" }\r\n" +" </script>\r\n" +"</head>\r\n" +"<body>\r\n" +" <h1>Netty WebSocket聊天室</h1>\r\n" +" <div id=\"chat\" style=\"height:300px;overflow:auto;border:1px solid #ccc;padding:10px;\"></div>\r\n" +" <input type=\"text\" id=\"message\" style=\"width:300px\">\r\n" +" <button onclick=\"send(document.getElementById('message').value)\">發送</button>\r\n" +"</body>\r\n" +"</html>";}
}
2. WebSocket幀處理器
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {private static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);@Overridepublic void channelActive(ChannelHandlerContext ctx) {// 有新連接加入channels.add(ctx.channel());System.out.println("客戶端加入: " + ctx.channel().remoteAddress());}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {if (frame instanceof TextWebSocketFrame) {// 處理文本幀String message = ((TextWebSocketFrame) frame).text();System.out.println("收到消息: " + message);// 廣播消息給所有連接String response = "用戶" + ctx.channel().remoteAddress() + " 說: " + message;channels.writeAndFlush(new TextWebSocketFrame(response));} else {// 不支持的幀類型System.out.println("不支持的幀類型: " + frame.getClass().getName());}}@Overridepublic void channelInactive(ChannelHandlerContext ctx) {// 連接斷開channels.remove(ctx.channel());System.out.println("客戶端斷開: " + ctx.channel().remoteAddress());}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}
}
3. WebSocket服務器主類
public class WebSocketServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();p.addLast(new HttpServerCodec());p.addLast(new HttpObjectAggregator(65536));p.addLast(new WebSocketServerHandler());}});ChannelFuture f = b.bind(8080).sync();System.out.println("WebSocket服務器已啟動,訪問地址: http://localhost:8080");f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
啟動服務器后,打開瀏覽器訪問 http://localhost:8080 ,你就能看到一個簡單的聊天室頁面。打開多個瀏覽器窗口,就可以實現多人聊天了!
4. WebSocket關鍵點解析
WebSocket應用比HTTP服務器稍微復雜一些,關鍵點在于:
- HTTP升級為WebSocket:客戶端首先發起HTTP請求,然后協議升級為WebSocket
- 握手過程:服務器需要返回特定的HTTP響應完成握手
- 不同類型的幀:WebSocket有多種幀類型,我們這里只處理了文本幀
- 廣播消息:使用ChannelGroup可以輕松地將消息廣播給所有連接的客戶端
四、自定義協議開發
在實際應用中,我們經常需要設計自己的通信協議。下面我們來實現一個簡單的自定義協議。
1. 協議設計
我們設計一個簡單的消息協議,格式如下:
+--------+------+--------+----------------+
| 魔數 | 版本 | 消息長度 | 消息內容 |
| 4字節 | 1字節 | 4字節 | 變長數據 |
+--------+------+--------+----------------+
- 魔數:固定值0xCAFEBABE,用于快速識別協議
- 版本:協議版本號,便于后續擴展
- 消息長度:消息內容的長度,單位為字節
- 消息內容:實際傳輸的數據,格式為JSON字符串
2. 消息對象定義
public class MyMessage {private String content;private int type;private long timestamp;// getter、setter和構造方法省略@Overridepublic String toString() {return "MyMessage{" +"content='" + content + '\'' +", type=" + type +", timestamp=" + timestamp +'}';}
}
3. 編碼器實現
public class MyMessageEncoder extends MessageToByteEncoder<MyMessage> {@Overrideprotected void encode(ChannelHandlerContext ctx, MyMessage msg, ByteBuf out) throws Exception {// 1. 寫入魔數out.writeInt(0xCAFEBABE);// 2. 寫入版本號out.writeByte(1);// 3. 將消息對象轉為JSONString json = new ObjectMapper().writeValueAsString(msg);byte[] content = json.getBytes(CharsetUtil.UTF_8);// 4. 寫入消息長度out.writeInt(content.length);// 5. 寫入消息內容out.writeBytes(content);}
}
4. 解碼器實現
public class MyMessageDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {// 1. 檢查是否有足夠的字節if (in.readableBytes() < 9) { // 魔數(4) + 版本(1) + 長度(4) = 9字節return;}// 2. 標記當前讀取位置in.markReaderIndex();// 3. 檢查魔數int magic = in.readInt();if (magic != 0xCAFEBABE) {in.resetReaderIndex();throw new CorruptedFrameException("Invalid magic number: " + magic);}// 4. 讀取版本號byte version = in.readByte();if (version != 1) {in.resetReaderIndex();throw new CorruptedFrameException("Invalid version: " + version);}// 5. 讀取消息長度int length = in.readInt();if (length < 0 || length > 65535) {in.resetReaderIndex();throw new CorruptedFrameException("Invalid length: " + length);}// 6. 檢查是否有足夠的字節if (in.readableBytes() < length) {in.resetReaderIndex();return;}// 7. 讀取消息內容byte[] content = new byte[length];in.readBytes(content);// 8. 將JSON轉換為對象MyMessage message = new ObjectMapper().readValue(content, MyMessage.class);// 9. 將對象添加到輸出列表out.add(message);}
}
5. 使用自定義協議的服務器
public class CustomProtocolServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();// 添加編解碼器p.addLast(new MyMessageDecoder());p.addLast(new MyMessageEncoder());// 添加業務處理器p.addLast(new SimpleChannelInboundHandler<MyMessage>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, MyMessage msg) {System.out.println("收到消息: " + msg);// 回復一條消息MyMessage response = new MyMessage();response.setContent("已收到你的消息: " + msg.getContent());response.setType(200);response.setTimestamp(System.currentTimeMillis());ctx.writeAndFlush(response);}});}});ChannelFuture f = b.bind(8888).sync();System.out.println("自定義協議服務器已啟動,端口: 8888");f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
6. 自定義協議的關鍵點
設計自定義協議時,需要注意以下幾點:
- 協議格式明確:需要明確定義消息的格式,包括頭部、長度、內容等
- 魔數校驗:使用魔數可以快速識別協議,過濾掉非法請求
- 版本控制:協議需要有版本號,便于后續升級
- 長度字段:必須有長度字段,便于拆包
- 編解碼器分離:將編碼和解碼邏輯分開,便于維護
五、編解碼器實踐
最后,我們來看幾種常見的編解碼器實現。
1. 基于長度的拆包器
TCP是流式協議,數據可能會被分成多個包傳輸,也可能多個消息會合并成一個包。為了正確地拆分消息,我們需要使用拆包器。
public class LengthBasedServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();// 添加長度字段解碼器,格式為: 長度(4字節) + 內容p.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 4, 0, 4));// 添加長度字段編碼器p.addLast(new LengthFieldPrepender(4));// 添加字符串編解碼器p.addLast(new StringDecoder(CharsetUtil.UTF_8));p.addLast(new StringEncoder(CharsetUtil.UTF_8));// 業務處理器p.addLast(new SimpleChannelInboundHandler<String>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println("收到消息: " + msg);ctx.writeAndFlush("回復: " + msg);}});}});ChannelFuture f = b.bind(8888).sync();System.out.println("拆包示例服務器已啟動,端口: 8888");f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
2. 基于分隔符的拆包器
有時候我們使用特定的字符作為消息分隔符,比如換行符。
public class DelimiterBasedServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();// 添加行分隔符解碼器p.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));// 添加字符串編解碼器p.addLast(new StringDecoder(CharsetUtil.UTF_8));p.addLast(new StringEncoder(CharsetUtil.UTF_8));// 業務處理器p.addLast(new SimpleChannelInboundHandler<String>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, String msg) {System.out.println("收到消息: " + msg);ctx.writeAndFlush("回復: " + msg + "\r\n");}});}});ChannelFuture f = b.bind(8888).sync();System.out.println("分隔符示例服務器已啟動,端口: 8888");f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
3. 對象序列化編解碼器
如果我們想直接傳輸Java對象,可以使用對象序列化編解碼器。
// 可序列化的消息對象
public class User implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;// getter、setter和構造方法省略@Overridepublic String toString() {return "User{name='" + name + "', age=" + age + "}";}
}
public class SerializationServer {public static void main(String[] args) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ChannelPipeline p = ch.pipeline();// 添加對象編解碼器p.addLast(new ObjectEncoder());p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));// 業務處理器p.addLast(new SimpleChannelInboundHandler<User>() {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, User user) {System.out.println("收到用戶: " + user);// 回復一個用戶對象User response = new User();response.setName("服務器用戶");response.setAge(20);ctx.writeAndFlush(response);}});}});ChannelFuture f = b.bind(8888).sync();System.out.println("序列化示例服務器已啟動,端口: 8888");f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}
4. 編解碼器的選擇
- LengthFieldBasedFrameDecoder:適合自定義協議,靈活性強
- DelimiterBasedFrameDecoder:適合基于文本的協議,如HTTP、SMTP等
- ObjectEncoder/ObjectDecoder:適合Java對象傳輸,但不適合跨語言通信
- JsonEncoder/JsonDecoder:適合跨語言通信
- ProtobufEncoder/ProtobufDecoder:高性能、跨語言通信的首選
總結與實踐建議
通過本文,我們學習了Netty的四種基礎應用:
- 簡單的Echo服務器與客戶端
- HTTP服務器
- WebSocket聊天室
- 自定義協議
在實際開發中,記住以下幾點:
- 選擇合適的編解碼器:根據需求選擇合適的編解碼器,避免重復造輪子
- 正確處理粘包/拆包:TCP是流式的,必須正確處理消息邊界
- 異常處理要完善:網絡編程中異常情況很多,一定要做好異常處理
- 資源要及時釋放:關閉連接、釋放ByteBuf等資源
- 避免阻塞EventLoop:耗時操作放到單獨的線程池中執行
Netty的基礎應用非常廣泛,掌握了這些基礎應用,你就能開發出各種高性能的網絡應用了。
在下一篇文章中,我們將探討Netty的高級特性,包括心跳檢測、空閑連接處理、內存管理等內容,敬請期待!
作者:by.G
如需轉載,請注明出處