| 1) 使用自定义协议 + 编解码器 来解决 |
| 2) 关键就是要解决 服务器端每次读取数据长度的问题, 这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免的TCP 粘包、拆包 |
| 1) 要求客户端发送5个Message对象, 客户端每次发送一个Message对象 |
| 2) 服务器端每次接收一个Message, 分5次进行解码,每读取到一个Message, 会回复一个Message对象给客户端. |

| 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; |
| } |
| |
| } |
| public class MyServer { |
| public static void main(String[] args) throws Exception{ |
| |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workerGroup = new NioEventLoopGroup(); |
| |
| try { |
| |
| ServerBootstrap serverBootstrap = new ServerBootstrap(); |
| serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new MyServerInitializer()); |
| |
| |
| ChannelFuture channelFuture = serverBootstrap.bind(7000).sync(); |
| channelFuture.channel().closeFuture().sync(); |
| |
| }finally { |
| bossGroup.shutdownGracefully(); |
| workerGroup.shutdownGracefully(); |
| } |
| |
| } |
| } |
| |
| public class MyServerInitializer extends ChannelInitializer<SocketChannel> { |
| |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| ChannelPipeline pipeline = ch.pipeline(); |
| |
| pipeline.addLast(new MyMessageDecoder()); |
| pipeline.addLast(new MyMessageEncoder()); |
| pipeline.addLast(new MyServerHandler()); |
| } |
| } |
| |
| public class MyServerHandler extends SimpleChannelInboundHandler<MessageProtocol>{ |
| |
| private int count; |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| |
| ctx.close(); |
| } |
| |
| @Override |
| protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { |
| |
| |
| int len = msg.getLen(); |
| byte[] content = msg.getContent(); |
| |
| System.out.println(); |
| System.out.println(); |
| System.out.println(); |
| System.out.println("服务器接收到信息如下"); |
| System.out.println("长度=" + len); |
| System.out.println("内容=" + new String(content, Charset.forName("utf-8"))); |
| |
| System.out.println("服务器接收到消息包数量=" + (++this.count)); |
| |
| |
| |
| String responseContent = UUID.randomUUID().toString(); |
| int responseLen = responseContent.getBytes("utf-8").length; |
| byte[] responseContent2 = responseContent.getBytes("utf-8"); |
| |
| MessageProtocol messageProtocol = new MessageProtocol(); |
| messageProtocol.setLen(responseLen); |
| messageProtocol.setContent(responseContent2); |
| |
| ctx.writeAndFlush(messageProtocol); |
| |
| } |
| } |
| public class MyMessageDecoder extends ReplayingDecoder<Void> { |
| @Override |
| protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { |
| System.out.println("MyMessageDecoder decode 被调用"); |
| |
| int length = in.readInt(); |
| |
| byte[] content = new byte[length]; |
| in.readBytes(content); |
| |
| |
| MessageProtocol messageProtocol = new MessageProtocol(); |
| messageProtocol.setLen(length); |
| messageProtocol.setContent(content); |
| |
| out.add(messageProtocol); |
| } |
| } |
| |
| public class MyMessageEncoder extends MessageToByteEncoder<MessageProtocol> { |
| @Override |
| protected void encode(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) throws Exception { |
| System.out.println("MyMessageEncoder encode 方法被调用"); |
| out.writeInt(msg.getLen()); |
| out.writeBytes(msg.getContent()); |
| } |
| } |
| public class MyClient { |
| 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 MyClientInitializer()); |
| |
| ChannelFuture channelFuture = bootstrap.connect("localhost", 7000).sync(); |
| |
| channelFuture.channel().closeFuture().sync(); |
| |
| }finally { |
| group.shutdownGracefully(); |
| } |
| } |
| } |
| |
| public class MyClientInitializer extends ChannelInitializer<SocketChannel> { |
| @Override |
| protected void initChannel(SocketChannel ch) throws Exception { |
| |
| ChannelPipeline pipeline = ch.pipeline(); |
| pipeline.addLast(new MyMessageEncoder()); |
| pipeline.addLast(new MyMessageDecoder()); |
| pipeline.addLast(new MyClientHandler()); |
| } |
| } |
| |
| public class MyClientHandler extends SimpleChannelInboundHandler<MessageProtocol> { |
| |
| private int count; |
| @Override |
| public void channelActive(ChannelHandlerContext ctx) throws Exception { |
| |
| |
| for(int i = 0; i< 5; i++) { |
| String mes = "今天天气冷,吃火锅"; |
| byte[] content = mes.getBytes(Charset.forName("utf-8")); |
| int length = mes.getBytes(Charset.forName("utf-8")).length; |
| |
| |
| MessageProtocol messageProtocol = new MessageProtocol(); |
| messageProtocol.setLen(length); |
| messageProtocol.setContent(content); |
| ctx.writeAndFlush(messageProtocol); |
| } |
| } |
| |
| |
| protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { |
| |
| int len = msg.getLen(); |
| byte[] content = msg.getContent(); |
| |
| System.out.println("客户端接收到消息如下"); |
| System.out.println("长度=" + len); |
| System.out.println("内容=" + new String(content, Charset.forName("utf-8"))); |
| |
| System.out.println("客户端接收消息数量=" + (++this.count)); |
| } |
| |
| @Override |
| public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { |
| System.out.println("异常消息=" + cause.getMessage()); |
| ctx.close(); |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
2021-08-11 SpringSecurity入门
2021-08-11 gradle入门