承接上一章,接下来开始进入Netty入门,本文所采用的Netty版本号是5.0,这个请你们注意下。bootstrap
仍是上一个场景,一个简单的客户端和服务端直接发送字符串的程序,若是使用Netty框架如何展现呢?废话很少说,直接上代码。服务器
服务端:框架
package com.dlb.note.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * 功能:服务器 * 版本:1.0 * 日期:2016年12月8日14:58:06 * 做者:馟苏 */ public class TimeServer { /** * 主函数 */ public static void main(String []args) { // 配置服务端的NIO线程池 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度 .option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChildChannelHandler()); // 绑定端口,同步等待成功 ChannelFuture future = serverBootstrap.bind(8888).sync(); System.out.println("服务器在8888端口监听hello"); // 等待服务端监听端口关闭 future.channel().closeFuture().sync(); System.out.println("服务器关闭bye"); } catch (Exception e) { e.printStackTrace(); } finally { // 优雅退出,释放线程池资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } class ChildChannelHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new TimerServerHandler()); } } class TimerServerHandler extends ChannelHandlerAdapter { // 可读 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 读数据 ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "UTF-8"); System.out.println("receive:" + body); // 写数据 ByteBuf res = Unpooled.copiedBuffer("hello,client!".getBytes()); ctx.write(res); ctx.flush(); } // 链接 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("client come,ip:" + ctx.channel().remoteAddress()); } // 关闭 @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("client close,ip:" + ctx.channel().remoteAddress()); ctx.close(); } // 异常 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println(cause.toString()); ctx.close(); } }
如今来分析一下:首先服务端作了两个线程池,一个是boss线程池,一个worker线程,这是Netty框架给咱们提供的。而且在使用完毕后,能够优雅的关闭。而后作一个服务器启动引导serverBootStrap,咱们能够设置TCP链接的属性,Netty给咱们提供了不少属性。接下来,注册管道,设置处理器,绑定端口监听。socket
其中比较重要的是处理器,咱们能够这么考虑,Netty为咱们提供了一个管道,管道中存在许多个处理器。一个客户端连接对应着一个管道。这样咱们就能够把处理逻辑写在处理器中。在处理器中咱们主要关心这么几个方法,可读:channelRead、连接到来:channelActive、连接断开:channelInactive、异常:exceptionCaught。而且在连接关闭后,直接经过ctx.close();能够将关联的通道等都关闭,而且释放资源。tcp
客户端:ide
package com.dlb.note.client; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; /** * 功能:支持tcp粘包/拆包的时间客户端 * 版本:1.0 * 日期:2016/12/9 15:40 * 做者:馟苏 */ public class TimeClient { /** * main函数 * @param args */ public static void main(String []args) { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer() { protected void initChannel(Channel channel) throws Exception { channel.pipeline().addLast(new MyHandler()); } }); // 等待客户端连接成功 ChannelFuture future = bootstrap.connect("localhost", 8888).sync(); System.out.println("客户端连接成功!"); // 等待客户端连接关闭 future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } } class MyHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 0; i < 1000; i++) { // 1. 以回车/换行符区分 // ByteBuf msg = Unpooled.copiedBuffer(("你好啊,服务器,我是董鲁北啊!" // + System.getProperty("line.separator")).getBytes()); // 2. 采用特殊分隔符区分 ByteBuf msg = Unpooled.copiedBuffer(("你好啊,服务器,我是revoid!$_".getBytes())); ctx.writeAndFlush(msg); } ctx.close(); super.channelActive(ctx); } }
客户端也是很是简单的,首先建立一个线程池,而后注册管道,处理器,在连接服务器成功后,给服务器发送消息。函数
经过上述介绍,咱们能够发现,经过Netty框架编写一个简单的客户端和服务端通讯程序,只须要寥寥几行就能够实现。可是仍然还有一个问题没有解决,那就是没有解决,TCP的粘包和分包问题,这个问题,将会在下一章介绍。oop