载入门之后,更多的细节进行整理。
载入门之后,更多的细节进行整理。
一:序列化
1.顺序
进入:从上到下,走inbound
出站:从下往上,走outbound
2.String序列化
客户端往服务端发送string,然后服务端进行解码出来
NettyServer:
package com.jun.netty.codec.string; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.string.StringDecoder; public class NettyServer { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup boosGroup = new NioEventLoopGroup(1); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boosGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringDecoder()); pipeline.addLast(new ServerHandler()); } }); System.out.println("netty server start"); ChannelFuture channelFuture = serverBootstrap.bind(9000).sync(); channelFuture.channel().closeFuture().sync(); }finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
处理器:
package com.jun.netty.codec.string; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("从客户端读取的数据:"+ msg.toString()); } }
NettyClient:
package com.jun.netty.codec.string; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; public class NettyClient { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringEncoder()); pipeline.addLast(new ClientHandler()); } }); System.out.println("netty client start"); ChannelFuture ch = bootstrap.connect("127.0.0.1", 9000).sync(); ch.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } }
处理器
package com.jun.netty.codec.string; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush("hello server"); } }
效果:
3.Object序列化
客户端往服务端发送object,然后服务端进行解码出来
NettyServer:
package com.jun.netty.codec.object; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolver; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.string.StringDecoder; public class NettyServer { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup boosGroup = new NioEventLoopGroup(1); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boosGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new ObjectDecoder(10240, ClassResolvers.cacheDisabled(null))); pipeline.addLast(new ServerHandler()); } }); System.out.println("netty server start"); ChannelFuture channelFuture = serverBootstrap.bind(9000).sync(); channelFuture.channel().closeFuture().sync(); }finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
处理器:
package com.jun.netty.codec.object; import com.jun.netty.model.User; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("从客户端读取到Object:" + ((User)msg).toString()); } }
NettyClient
package com.jun.netty.codec.object; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ObjectEncoder; import io.netty.handler.codec.string.StringEncoder; public class NettyClient { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new ObjectEncoder()); pipeline.addLast(new ClientHandler()); } }); System.out.println("netty client start"); ChannelFuture ch = bootstrap.connect("127.0.0.1", 9000).sync(); ch.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } }
处理器
package com.jun.netty.codec.object; import com.jun.netty.model.User; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(new User(1,"jun")); } }
4.protostuff序列化
先做一个protostuff
protostuff是一个基于protobuf实现的序列化方法,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。使用它也非常简单,代码如下:
<!-- protostuff依赖包 begin --> <dependency> <groupId>com.dyuproject.protostuff</groupId> <artifactId>protostuff-api</artifactId> <version>1.0.10</version> </dependency> <dependency> <groupId>com.dyuproject.protostuff</groupId> <artifactId>protostuff-core</artifactId> <version>1.0.10</version> </dependency> <dependency> <groupId>com.dyuproject.protostuff</groupId> <artifactId>protostuff-runtime</artifactId> <version>1.0.10</version> </dependency> <!-- protostuff依赖包 end -->
使用:
package com.jun.netty.utils; import com.dyuproject.protostuff.LinkedBuffer; import com.dyuproject.protostuff.ProtostuffIOUtil; import com.dyuproject.protostuff.Schema; import com.dyuproject.protostuff.runtime.RuntimeSchema; import com.jun.netty.model.User; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * protostuff 序列化工具类,基于protobuf封装 */ public class ProtostuffUtil { private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>(); private static <T> Schema<T> getSchema(Class<T> clazz) { @SuppressWarnings("unchecked") Schema<T> schema = (Schema<T>) cachedSchema.get(clazz); if (schema == null) { schema = RuntimeSchema.getSchema(clazz); if (schema != null) { cachedSchema.put(clazz, schema); } } return schema; } /** * 序列化 * * @param obj * @return */ public static <T> byte[] serializer(T obj) { @SuppressWarnings("unchecked") Class<T> clazz = (Class<T>) obj.getClass(); LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); try { Schema<T> schema = getSchema(clazz); return ProtostuffIOUtil.toByteArray(obj, schema, buffer); } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } finally { buffer.clear(); } } /** * 反序列化 * * @param data * @param clazz * @return */ public static <T> T deserializer(byte[] data, Class<T> clazz) { try { T obj = clazz.newInstance(); Schema<T> schema = getSchema(clazz); ProtostuffIOUtil.mergeFrom(data, obj, schema); return obj; } catch (Exception e) { throw new IllegalStateException(e.getMessage(), e); } } public static void main(String[] args) { byte[] userBytes = ProtostuffUtil.serializer(new User(1, "caojun")); User user = ProtostuffUtil.deserializer(userBytes, User.class); System.out.println(user); } }
在实际中使用:
NettyServer:
package com.jun.netty.codec.protostuff; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; public class NettyServer { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup boosGroup = new NioEventLoopGroup(1); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boosGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new ServerHandler()); } }); System.out.println("netty server start"); ChannelFuture channelFuture = serverBootstrap.bind(9000).sync(); channelFuture.channel().closeFuture().sync(); }finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
处理器:
package com.jun.netty.codec.protostuff; import com.jun.netty.model.User; import com.jun.netty.utils.ProtostuffUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); System.out.println("从客户端读取到Object:" + ProtostuffUtil.deserializer(bytes, User.class)); } }
NettyClient
package com.jun.netty.codec.protostuff; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.serialization.ObjectEncoder; public class NettyClient { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new ClientHandler()); } }); System.out.println("netty client start"); ChannelFuture ch = bootstrap.connect("127.0.0.1", 9000).sync(); ch.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } }
处理器
package com.jun.netty.codec.protostuff; import com.jun.netty.model.User; import com.jun.netty.utils.ProtostuffUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ByteBuf buf = Unpooled.copiedBuffer(ProtostuffUtil.serializer(new User(1, "junjun"))); ctx.writeAndFlush(buf); } }
二:拆包粘包
1.说明
TCP是一个流协议,就是没有界限的一长串二进制数据。TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。面向流的通信是无消息保护边界的。
2.解决方案
- LineBasedFrameDecoder (回车换行分包)
- DelimiterBasedFrameDecoder(特殊分隔符分包)
- FixedLengthFrameDecoder(固定长度报文来分包)
3.展示粘包与拆包
使用聊天chat包的程序。
先启动服务端,进行转发程序
启动启动一个客户端1。
再修改客户端的程序,然后启动,作为客户端2。
package com.tuling.netty.chat; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class ChatClient { public static void main(String[] args) throws Exception { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //pipeline.addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("_" // .getBytes()))); //向pipeline加入解码器 pipeline.addLast("decoder", new StringDecoder()); //向pipeline加入编码器 pipeline.addLast("encoder", new StringEncoder()); //加入自己的业务处理handler pipeline.addLast(new ChatClientHandler()); } }); ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9000).sync(); //得到 channel Channel channel = channelFuture.channel(); System.out.println("========" + channel.localAddress() + "========"); //客户端需要输入信息, 创建一个扫描器 // Scanner scanner = new Scanner(System.in); // while (scanner.hasNextLine()) { // String msg = scanner.nextLine(); // //通过 channel 发送到服务器端 // channel.writeAndFlush(msg); // } for (int i = 0; i < 200; i++) { channel.writeAndFlush("hello,诸葛!" + "_"); } } finally { group.shutdownGracefully(); } } }
在客户端1小效果:
D:\jdk1.8.0_202\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:52388,suspend=y,server=n -javaagent:C:\Users\Administrator\.IntelliJIdea2019.3\system\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\jdk1.8.0_202\jre\lib\charsets.jar;D:\jdk1.8.0_202\jre\lib\deploy.jar;D:\jdk1.8.0_202\jre\lib\ext\access-bridge-64.jar;D:\jdk1.8.0_202\jre\lib\ext\cldrdata.jar;D:\jdk1.8.0_202\jre\lib\ext\dnsns.jar;D:\jdk1.8.0_202\jre\lib\ext\jaccess.jar;D:\jdk1.8.0_202\jre\lib\ext\jfxrt.jar;D:\jdk1.8.0_202\jre\lib\ext\localedata.jar;D:\jdk1.8.0_202\jre\lib\ext\nashorn.jar;D:\jdk1.8.0_202\jre\lib\ext\sunec.jar;D:\jdk1.8.0_202\jre\lib\ext\sunjce_provider.jar;D:\jdk1.8.0_202\jre\lib\ext\sunmscapi.jar;D:\jdk1.8.0_202\jre\lib\ext\sunpkcs11.jar;D:\jdk1.8.0_202\jre\lib\ext\zipfs.jar;D:\jdk1.8.0_202\jre\lib\javaws.jar;D:\jdk1.8.0_202\jre\lib\jce.jar;D:\jdk1.8.0_202\jre\lib\jfr.jar;D:\jdk1.8.0_202\jre\lib\jfxswt.jar;D:\jdk1.8.0_202\jre\lib\jsse.jar;D:\jdk1.8.0_202\jre\lib\management-agent.jar;D:\jdk1.8.0_202\jre\lib\plugin.jar;D:\jdk1.8.0_202\jre\lib\resources.jar;D:\jdk1.8.0_202\jre\lib\rt.jar;C:\Users\Administrator\Desktop\netty\30-深入Hotspot源码与Linux内核理解NIO与Epoll-诸葛\netty(2)\netty\target\classes;C:\Users\zhuge\.m2\repository\com\dyuproject\protostuff\protostuff-api\1.0.10\protostuff-api-1.0.10.jar;C:\Users\zhuge\.m2\repository\com\dyuproject\protostuff\protostuff-core\1.0.10\protostuff-core-1.0.10.jar;C:\Users\zhuge\.m2\repository\com\dyuproject\protostuff\protostuff-runtime\1.0.10\protostuff-runtime-1.0.10.jar;C:\Users\zhuge\.m2\repository\com\dyuproject\protostuff\protostuff-collectionschema\1.0.10\protostuff-collectionschema-1.0.10.jar;C:\Users\zhuge\.m2\repository\io\netty\netty-all\4.1.35.Final\netty-all-4.1.35.Final.jar;D:\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar" com.tuling.netty.chat.ChatClient Connected to the target VM, address: '127.0.0.1:52388', transport: 'socket' ========/127.0.0.1:52431======== [ 客户端 ]/127.0.0.1:52482 上线了 2022-09-22 21:43:22 [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息 :hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_hello,诸葛!_hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 发送了消息:hello,诸葛!_ [ 客户端 ]/127.0.0.1:52482 下线了
4.自定义
新定义传输协议:
package com.jun.netty.model; /** * 自定义传输协议包 */ public class MessageProtocol { /** * 长度 */ private int len; /** * 二进制内容 */ private byte[] content; public int getLen() { return len; } public void setLen(int len) { this.len = len; } public byte[] getContent() { return content; } public void setContent(byte[] content) { this.content = content; } }
server
package com.jun.netty.splitandstickpacket; import com.jun.netty.splitandstickpacket.code.NettyMessageDecode; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer { public static void main(String[] args) { NioEventLoopGroup boosGroup = new NioEventLoopGroup(1); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boosGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new NettyMessageDecode()); pipeline.addLast(new NettyServerHandler()); } }); System.out.println("netty server start"); ChannelFuture future = serverBootstrap.bind(9000).sync(); future.channel().closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
解码器
package com.jun.netty.splitandstickpacket.code; import com.jun.netty.model.MessageProtocol; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; /** * 信息解码器 */ public class NettyMessageDecode extends ByteToMessageDecoder { private int length = 0; @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { System.out.println("服务端接收到的数据" + byteBuf); if (byteBuf.readableBytes() >= 4) { if (length == 0) { length = byteBuf.readInt(); } if (byteBuf.readableBytes() < length) { System.out.println("读取到的数据不符合length长度"); return; } byte[] content = new byte[length]; if (byteBuf.readableBytes() >= length) { byteBuf.readBytes(content); // 将信息组装成dto,传递给下一个业务处理器 MessageProtocol messageProtocol = new MessageProtocol(); messageProtocol.setLen(length); messageProtocol.setContent(content); list.add(messageProtocol); } length = 0; } } }
处理器
package com.jun.netty.splitandstickpacket; import com.jun.netty.model.MessageProtocol; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.CharsetUtil; public class NettyServerHandler extends SimpleChannelInboundHandler<MessageProtocol> { private int count; @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocol messageProtocol) throws Exception { System.out.println("服务端接收到的数据长度=" + messageProtocol.getLen()); System.out.println("服务端接收到的数据=" + new String(messageProtocol.getContent(), CharsetUtil.UTF_8)); System.out.println("服务端接收到的消息包的数量=" + (++this.count)); } }
client
package com.jun.netty.splitandstickpacket; import com.jun.netty.splitandstickpacket.code.NettyMessageEncode; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; public class NettyClient { public static void main(String[] args) { NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new NettyMessageEncode()); pipeline.addLast(new NettyClientHandler()); } }); System.out.println("netty client start"); ChannelFuture future = bootstrap.connect("127.0.0.1", 9000).sync(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } }
处理器
package com.jun.netty.splitandstickpacket; import com.jun.netty.model.MessageProtocol; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.util.CharsetUtil; public class NettyClientHandler extends SimpleChannelInboundHandler<MessageProtocol> { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocol messageProtocol) throws Exception { } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i=0; i< 100; i++){ String msg = "你好啊,netty"; MessageProtocol messageProtocol = new MessageProtocol(); messageProtocol.setLen(msg.getBytes(CharsetUtil.UTF_8).length); messageProtocol.setContent(msg.getBytes(CharsetUtil.UTF_8)); ctx.writeAndFlush(messageProtocol); } } }
编码器
package com.jun.netty.splitandstickpacket.code; import com.jun.netty.model.MessageProtocol; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; /** * 信息编码器 */ public class NettyMessageEncode extends MessageToByteEncoder<MessageProtocol> { @Override protected void encode(ChannelHandlerContext channelHandlerContext, MessageProtocol messageProtocol, ByteBuf byteBuf) throws Exception { byteBuf.writeInt(messageProtocol.getLen()); byteBuf.writeBytes(messageProtocol.getContent()); } }
三:心跳检测机制
1.client
package com.jun.netty.heartbeat; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.Random; public class HeatBeatClient { public static void main(String[] args) throws InterruptedException { NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new HeatBeatClientHandler()); } }); System.out.println("netty client start"); Channel channel = bootstrap.connect("127.0.0.1", 9000).sync().channel(); String text = "packet heatBeat"; Random random = new Random(); while (channel.isActive()) { int num = random.nextInt(8); Thread.sleep(num * 1000); channel.writeAndFlush(text); } } finally { group.shutdownGracefully(); } } static class HeatBeatClientHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(" client received :" + msg); if (msg != null && msg.equals("idle close")) { System.out.println(" 服务端关闭连接,客户端也关闭"); ctx.channel().closeFuture(); } } } }
2.server
package com.tuling.netty.heartbeat; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; 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.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.timeout.IdleStateHandler; import java.util.concurrent.TimeUnit; public class HeartBeatServer { public static void main(String[] args) throws Exception { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, worker) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("decoder", new StringDecoder()); pipeline.addLast("encoder", new StringEncoder()); //IdleStateHandler的readerIdleTime参数指定超过3秒还没收到客户端的连接, //会触发IdleStateEvent事件并且交给下一个handler处理,下一个handler必须 //实现userEventTriggered方法处理对应事件 pipeline.addLast(new IdleStateHandler(3, 0, 0, TimeUnit.SECONDS)); pipeline.addLast(new HeartBeatServerHandler()); } }); System.out.println("netty server start。。"); ChannelFuture future = bootstrap.bind(9000).sync(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { worker.shutdownGracefully(); boss.shutdownGracefully(); } } }
处理器
package com.jun.netty.heartbeat; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.timeout.IdleStateEvent; public class HeatBeatServerHandler extends SimpleChannelInboundHandler<String> { int readIdleTimes = 0; @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception { if ("packet heatBeat".equals(s)) { System.out.println("回复ok"); channelHandlerContext.channel().writeAndFlush("ok"); } else { System.out.println("其他信息"); } } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { IdleStateEvent event = (IdleStateEvent) evt; String eventType = ""; switch (event.state()) { case ALL_IDLE: eventType = "读写空闲"; break; case READER_IDLE: readIdleTimes++; eventType = "读空闲"; break; case WRITER_IDLE: eventType = "写空闲"; break; } System.out.println(ctx.channel().remoteAddress() + "超时事件" + eventType); if (readIdleTimes > 3) { System.out.println("[server]读空闲超过3次,关闭连接,释放更多资源"); ctx.channel().writeAndFlush("idle close"); ctx.channel().close(); } } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.err.println("=== " + ctx.channel().remoteAddress() + " is active ==="); } }
3.效果
"D:\IntelliJ IDEA 2019.3.1\jbr\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:51972,suspend=y,server=n -javaagent:C:\Users\Administrator\.IntelliJIdea2019.3\system\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "E:\study\mygitee\netty-pro\target\classes;C:\Users\Administrator\.m2\repository\io\netty\netty-all\4.1.35.Final\netty-all-4.1.35.Final.jar;C:\Users\Administrator\.m2\repository\com\dyuproject\protostuff\protostuff-api\1.0.10\protostuff-api-1.0.10.jar;C:\Users\Administrator\.m2\repository\com\dyuproject\protostuff\protostuff-core\1.0.10\protostuff-core-1.0.10.jar;C:\Users\Administrator\.m2\repository\com\dyuproject\protostuff\protostuff-runtime\1.0.10\protostuff-runtime-1.0.10.jar;C:\Users\Administrator\.m2\repository\com\dyuproject\protostuff\protostuff-collectionschema\1.0.10\protostuff-collectionschema-1.0.10.jar;D:\IntelliJ IDEA 2019.3.1\lib\idea_rt.jar" com.jun.netty.heartbeat.HeartBeatServer Connected to the target VM, address: '127.0.0.1:51972', transport: 'socket' OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended netty server start === /127.0.0.1:52060 is active === /127.0.0.1:52060超时事件读空闲 回复ok /127.0.0.1:52060超时事件读空闲 其他信息 回复ok /127.0.0.1:52060超时事件读空闲 /127.0.0.1:52060超时事件读空闲 [server]读空闲超过3次,关闭连接,释放更多资源 Disconnected from the target VM, address: '127.0.0.1:51972', transport: 'socket' Process finished with exit code 130
四:重连
1.server
package com.jun.netty.reconnect; import com.jun.netty.reconnect.handler.NettyClientHandler; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; 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 java.util.concurrent.TimeUnit; public class NettyClient { private String host; private int port; private Bootstrap bootstrap; private EventLoopGroup eventLoopGroup; public NettyClient(String host, int port) { this.host = host; this.port = port; init(); } /** * 初始化 */ private void init() { eventLoopGroup = new NioEventLoopGroup(); //创建客户端启动对象 // bootstrap 可重用, 只需在NettyClient实例化的时候初始化即可. bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //加入处理器 ch.pipeline().addLast(new NettyClientHandler(NettyClient.this)); } }); } /** * 连接 */ public void connect() throws InterruptedException { System.out.println("netty client start。。"); //启动客户端去连接服务器端 ChannelFuture cf = bootstrap.connect(host, port); cf.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { //重连交给后端线程执行 future.channel().eventLoop().schedule(() -> { System.err.println("重连服务端..."); try { connect(); } catch (Exception e) { e.printStackTrace(); } }, 3000, TimeUnit.MILLISECONDS); } else { System.out.println("服务端连接成功..."); } } }); //对通道关闭进行监听 cf.channel().closeFuture().sync(); } /** * 启动 */ public static void main(String[] args) throws InterruptedException { NettyClient nettyClient = new NettyClient("127.0.0.1", 9000); nettyClient.connect(); } }
处理器1
package com.jun.netty.reconnect.handler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class LifeCycleInBoundHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive: channel准备就绪"); super.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelInactive: channel被关闭"); super.channelInactive(ctx); } }
处理器2
package com.jun.netty.reconnect.handler; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 读取客户端发送的数据 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("服务器读取线程 " + Thread.currentThread().getName()); ByteBuf buf = (ByteBuf) msg; System.out.println("客户端发送消息是:" + buf.toString(CharsetUtil.UTF_8)); } /** * 数据读取完毕处理方法 */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ByteBuf buf = Unpooled.copiedBuffer("HelloClient".getBytes(CharsetUtil.UTF_8)); ctx.writeAndFlush(buf); } /** * 处理异常, 一般是需要关闭通道 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
2.client
package com.jun.netty.reconnect; import com.jun.netty.reconnect.handler.NettyClientHandler; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; 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 java.util.concurrent.TimeUnit; public class NettyClient { private String host; private int port; private Bootstrap bootstrap; private EventLoopGroup eventLoopGroup; public NettyClient(String host, int port) { this.host = host; this.port = port; init(); } /** * 初始化 */ private void init() { eventLoopGroup = new NioEventLoopGroup(); //创建客户端启动对象 // bootstrap 可重用, 只需在NettyClient实例化的时候初始化即可. bootstrap = new Bootstrap(); bootstrap.group(eventLoopGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //加入处理器 ch.pipeline().addLast(new NettyClientHandler(NettyClient.this)); } }); } /** * 连接 */ public void connect() throws InterruptedException { System.out.println("netty client start。。"); //启动客户端去连接服务器端 ChannelFuture cf = bootstrap.connect(host, port); cf.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (!future.isSuccess()) { //重连交给后端线程执行 future.channel().eventLoop().schedule(() -> { System.err.println("重连服务端..."); try { connect(); } catch (Exception e) { e.printStackTrace(); } }, 3000, TimeUnit.MILLISECONDS); } else { System.out.println("服务端连接成功..."); } } }); //对通道关闭进行监听 cf.channel().closeFuture().sync(); } /** * 启动 */ public static void main(String[] args) throws InterruptedException { NettyClient nettyClient = new NettyClient("127.0.0.1", 9000); nettyClient.connect(); } }
处理器
package com.jun.netty.reconnect.handler; import com.jun.netty.reconnect.NettyClient; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; public class NettyClientHandler extends ChannelInboundHandlerAdapter { private NettyClient nettyClient; public NettyClientHandler(NettyClient nettyClient) { this.nettyClient = nettyClient; } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.err.println("运行中断开重连。。。"); nettyClient.connect(); } /** * 当客户端连接服务器完成就会触发该方法 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ByteBuf buf = Unpooled.copiedBuffer("HelloServer".getBytes(CharsetUtil.UTF_8)); ctx.writeAndFlush(buf); } //当通道有读取事件时会触发,即服务端发送数据给客户端 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8)); System.out.println("服务端的地址: " + ctx.channel().remoteAddress()); } }
效果
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程使用 AI 从 0 到 1 写了个小工具
· 快收藏!一个技巧从此不再搞混缓存穿透和缓存击穿
· AI 插件第二弹,更强更好用
· Blazor Hybrid适配到HarmonyOS系统
· 支付宝 IoT 设备入门宝典(下)设备经营篇