序言:简单开发前端发送消息,后端接收消息并返回。前端
今天经过一个故事来说解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); }