序言:簡單開發前端發送消息,後端接收消息並返回。前端
今天經過一個故事來說解netty,主要講client和server端和下面的主要實現類。bootstrap
客戶要去ktv唱歌,進入ktv以前,門口會有招待服務生,而後招待服務生看到客戶以後,會安排大家給ktv內部服務生給大家安排房間,而後帶大家進入ktv房間唱歌,準備開始唱歌就要打開音樂,選好歌曲,打開燈光等因素,以後這個期間大家可能會讓唱歌的服務生來助唱,讓會跳舞的服務生給大家跳舞,甚至讓服務生帶來酒或者吃的,直到大家唱歌結束付完錢離開爲止,整個過程就算結束。後端
專有名詞解釋:瀏覽器
client端:客戶服務器
server端:ktvsocket
mainGroup或者boosGroup:主線程組,至關於ktv門口外的招待服務生ide
subGroup或者workerGroup:子線程組,至關於ktv內部服務生,負責安排房間的,由招待服務生通知內部服務生安排給他們的函數
ServerBootstrap :客戶進入ktv以前到離開ktv以後的整個流程工具
HelloServerInitializer:進入ktv房間以後的整個流程oop
CustomHandler :分別表明唱歌服務生、端茶送水服務生、跳舞服務生各個實現的功能等等
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * @Description: 實現客戶端發送一個請求,服務器會返回 hello netty */ public class HelloServer { public static void main(String[] args) throws Exception { // 定義一對線程組 // 主線程組, 用於接受客戶端的鏈接,可是不作任何處理,跟老闆同樣,不作事 EventLoopGroup bossGroup = new NioEventLoopGroup(); // 從線程組, 老闆線程組會把任務丟給他,讓手下線程組去作任務 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { // netty服務器的建立, ServerBootstrap 是一個啓動類 ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) // 設置主從線程組 .channel(NioServerSocketChannel.class) // 設置nio的雙向通道 .childHandler(new HelloServerInitializer()); // 子處理器,用於處理workerGroup // 啓動server,而且設置8088爲啓動的端口號,同時啓動方式爲同步 ChannelFuture channelFuture = serverBootstrap.bind(8088).sync(); // 監聽關閉的channel,設置位同步方式 channelFuture.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpServerCodec; /** * @Description: 初始化器,channel註冊後,會執行裏面的相應的初始化方法 */ public class HelloServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel channel) throws Exception { // 經過SocketChannel去得到對應的管道 ChannelPipeline pipeline = channel.pipeline(); // 經過管道,添加handler // HttpServerCodec是由netty本身提供的助手類,能夠理解爲攔截器 // 當請求到服務端,咱們須要作解碼,響應到客戶端作編碼 pipeline.addLast("HttpServerCodec", new HttpServerCodec()); // 添加自定義的助手類,返回 "hello netty~" pipeline.addLast("customHandler", new CustomHandler()); } }
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.util.CharsetUtil; /** * @Description: 建立自定義助手類 */ // SimpleChannelInboundHandler: 對於請求來說,其實至關於[入站,入境] public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> { @Override protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception { // 獲取channel Channel channel = ctx.channel(); if (msg instanceof HttpRequest) { // 顯示客戶端的遠程地址 System.out.println(channel.remoteAddress()); // 定義發送的數據消息 ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8); // 構建一個http response FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content); // 爲響應增長數據類型和長度 response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain"); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); // 把響應刷到客戶端 ctx.writeAndFlush(response); } } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channel。。。註冊"); super.channelRegistered(ctx); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channel。。。移除"); super.channelUnregistered(ctx); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channel。。。活躍"); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("channel。。。不活躍"); super.channelInactive(ctx); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { System.out.println("channeld讀取完畢。。。"); super.channelReadComplete(ctx); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { System.out.println("用戶事件觸發。。。"); super.userEventTriggered(ctx, evt); } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { System.out.println("channel可寫更改"); super.channelWritabilityChanged(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("補貨到異常"); super.exceptionCaught(ctx, cause); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("助手類添加"); super.handlerAdded(ctx); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { System.out.println("助手類移除"); super.handlerRemoved(ctx); } }
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.25.Final</version> </dependency>
打開瀏覽器:localhost:8088,出現下面錯誤狀況,若是用postman工具的話,就會發現不報錯,由於瀏覽器會自動關閉。
解決方案:將super.exceptionCauht(ctx,case);註釋掉就行了
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { //super.exceptionCaught(ctx, cause); }