在Netty中解决拆包和粘包的问题,我们只需要将解码器添加到ChannelPipeline中就可以了。
LineBasedFrameDecoder的工作原理就是它依次遍历ByteBuf中的可读字节,如果有\n和\r\n,就以此为结束位置,它是以换行符为结束标志的解码器。如果读取到行的最大长度还没有发现换行,就会抛出异常,同时忽略掉之前读到的异常码流。
StringDecoder就是讲收到的对象转换成字符串。
上面这两种解码器结合起来其实就是换行切换的文本解码器。
后续还会涉及到分隔符的解码器和定长解码器
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; /** * @FileName TimeServer.java * @Description: * * @Date 2016年3月2日 * @author Administroter * @version 1.0 * */ public class TimeServer { public void bind(int port) throws Exception { // 配置服务端的NIO线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { /** * 创建ServerBootstrap,它是Netty用于启动NIO服务端的辅助启动类 */ ServerBootstrap b = new ServerBootstrap(); /** * 创建管道NioServerSocketChannel,也就是NIO中的ServerSocketChannel,然后设置TCP的参数 * ,设置为1024,最后 * 创建ChildChannelHandler,也就是Reactor模式中的handler类,用于处理网络IO时间 * ,比如对消息的编解码。 */ b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024) .childHandler(new ChildChannelHandler()); /** * 绑定端口,同步等待成功 * 绑定完成之后会返回一个ChannelFuture,这里类似于JDK的java.util.concurrent.Future。 * 用于异步操作的通知回调 */ ChannelFuture f = b.bind(port).sync(); // 等待服务端监听端口关闭 f.channel().closeFuture().sync(); } finally { // 优雅退出,释放线程池资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } private class ChildChannelHandler extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel arg0) throws Exception { //添加解码器 arg0.pipeline().addLast(new LineBasedFrameDecoder(1024)); arg0.pipeline().addLast(new StringDecoder()); arg0.pipeline().addLast(new TimeServerHandler()); } } /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { // 采用默认值 } } new TimeServer().bind(port); } }
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; /** * @FileName TimeServerHandler.java * @Description:用于对网络事件读写操作 * * @Date 2016年3月2日 * @author Administroter * @version 1.0 * */ public class TimeServerHandler extends ChannelHandlerAdapter { private int counter; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //添加解码器后不需要考虑处理读半包的问题,也不需要对客户端请求的消息msg进行编码 String body = (String) msg; System.out.println("The time server receive order : " + body +";记录数 -----: " + ++counter); String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER"; currentTime = currentTime + System.getProperty("line.separator"); ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes()); ctx.writeAndFlush(resp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.close(); } }
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; /** * @FileName TimeClient.java * @Description: * * @Date 2016年3月2日 * @author Administroter * @version 1.0 * */ public class TimeClient { public void connect(int port, String host) throws Exception { // 配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); try { //客户端辅助启动类 Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler(new ChannelInitializer<SocketChannel>() { public void initChannel(SocketChannel ch) throws Exception { //添加解码器 ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new TimeClientHandler()); } }); // 发起异步连接操作 ChannelFuture f = b.connect(host, port).sync(); // 当代客户端链路关闭 f.channel().closeFuture().sync(); } finally { // 优雅退出,释放NIO线程组 group.shutdownGracefully(); } } /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { // 采用默认值 } } new TimeClient().connect(port, "127.0.0.1"); } }
import java.util.logging.Logger; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; /** * @FileName TimeClientHandler.java * @Description: * * @Date 2016年3月2日 * @author Administroter * @version 1.0 * */ public class TimeClientHandler extends ChannelHandlerAdapter { private static final Logger logger = Logger.getLogger(TimeClientHandler.class.getName()); private int counter; private byte[] request; /** * Creates a client-side handler. */ public TimeClientHandler() { request = ("QUERY TIME ORDER"+System.getProperty("line.separator")).getBytes(); } /** * TCP链路建立成功后,调用这个方法发送查询指令给服务器 */ @Override public void channelActive(ChannelHandlerContext ctx) { ByteBuf message = null; for (int i = 0; i < 100; i++) { message = Unpooled.buffer(request.length); message.writeBytes(request); ctx.writeAndFlush(message); } } /** * 服务器你响应结果后调用这个方法,获取服务器响应的结果 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String body = (String)msg; System.out.println("服务器器响应结果 : " + body + "; the counter is : " + ++counter); } /** * 链路建立失败,释放资源 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 释放资源 logger.warning("Unexpected exception from downstream : " + cause.getMessage()); ctx.close(); } }