Netty学习笔记(四)Springboot实现自定义协议

背景

工作场合肯定是需要自定义协议的,下面是个用Springboot写的独立的服务端、客户端程序。

上代码

  1. 添加Netty依赖,这里博主同时也消除Tomcat依赖

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
         <exclusions>
             <exclusion>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter-tomcat</artifactId>
             </exclusion>
         </exclusions>
     </dependency>
     
     <dependency>
         <groupId>io.netty</groupId>
         <artifactId>netty-all</artifactId>
         <version>4.1.31.Final</version>
     </dependency>
    
  2. 写一个自定义协议的类:

     //这里博主写了个仅有长度和内容的协议
     public class PersonProtocal {
     
     private int length;
     private byte[] content;
    
     public int getLength() {
     		return length;
     	}
    
     public void setLength(int length) {
     		this.length = length;
     	}
    
     public byte[] getContent() {
     		return content;
     	}
    
     public void setContent(byte[] content) {
    		 this.content = content;
     	}
     }
    
  3. 写编码MyPersonEncoder类:

     public class MyPersonEncoder extends MessageToByteEncoder<PersonProtocal> {
     @Override
     protected void encode(ChannelHandlerContext ctx, PersonProtocal msg, ByteBuf out) throws Exception {
     	System.out.println("MyPersonEncoder encode invoked");
     	out.writeInt(msg.getLength());
     	out.writeBytes(msg.getContent());
      	}
      }
    
  4. 写译码MyPersonDecoder类:

     public class MyPersonDecoder  extends ReplayingDecoder<Void> {
     @Override
     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
     	System.out.println("MyPersonDecoder decode invoked ");
     	int length = in.readInt();
     	byte[] content = new byte[length];
     	in.readBytes(content);
    
     	PersonProtocal personProtocal = new PersonProtocal();
     	personProtocal.setLength(length);
     	personProtocal.setContent(content);
    
     	out.add(personProtocal);
     	}
     }
    

    注:ByteToMessageDecoder 可以在阻塞的i/o模式下实现非阻塞的解码。
    ReplayingDecoder 和ByteToMessageDecoder 最大的不同就是ReplayingDecoder ,不需要判断可用的字节。

  5. 服务端启动类:

    public class EchoServer {
    	private final int port;
    	private Logger log = LoggerFactory.getLogger(this.getClass());
    
    	 public EchoServer(int port)
     	{
    		this.port=port;
    	}
    
    	public void start() throws Exception{
    		final EchoServerHandler serverHandler = new EchoServerHandler();
    		EventLoopGroup group = new NioEventLoopGroup();
     		try
     			{
         			ServerBootstrap serverBootstrap=new ServerBootstrap();
         			serverBootstrap.group(group)
                 			.localAddress(new InetSocketAddress(port))
                 			.channel(NioServerSocketChannel.class)
                 			.childHandler(new ChannelInitializer<SocketChannel>() {
                     			@Override
                     			protected void initChannel(SocketChannel channel) throws Exception {
                         		channel.pipeline().addLast(new MyPersonDecoder());
                         		channel.pipeline().addLast(new MyPersonEncoder());
                         		channel.pipeline().addLast(serverHandler);
                     				}
                			 });
         		ChannelFuture f=serverBootstrap.bind().sync();
         		f.channel().closeFuture().sync();
    			 }
     				finally {
         					group.shutdownGracefully().sync();
     						}
    				}
    			}
    
    1. 服务端Handler类:

       @ChannelHandler.Sharable   //没写这个,当服务端关闭再连接时会报错Failed to initialize a channel. Closing
       public class EchoServerHandler extends SimpleChannelInboundHandler<PersonProtocal> {
           private int count;
       	@Override
       	protected void channelRead0(ChannelHandlerContext ctx, PersonProtocal msg) throws Exception {
       			System.out.println("服务端接受到的数据:");
       			System.out.println("数据长度:"+msg.getLength());
       			System.out.println("数据内容:"+ new String(msg.getContent(), Charset.forName("utf-8")) );
       			System.out.println("服务端接收到的消息数量:"+(++count));
      
       			String responseMessage = UUID.randomUUID().toString();
       			int responseLength = responseMessage.getBytes(Charset.forName("utf-8")).length;
       			byte[] responseContent = responseMessage.getBytes(Charset.forName("utf-8"));
       			PersonProtocal personProtocal = new PersonProtocal();
       			personProtocal.setLength(responseLength);
       			personProtocal.setContent(responseContent);
       			ctx.writeAndFlush(personProtocal);
       			}
      
       	@Override
       	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
       			cause.printStackTrace();
       			ctx.close();
       			}
       		}
      
  6. Application,博主没有用tomcat做容器

     @SpringBootApplication
     public class Nettyserver3Application implements CommandLineRunner {
    
     public static void main(String[] args) {
     	SpringApplication.run(Nettyserver3Application.class, args);
     	 }
    
     @Override
     public void run(String... strings) throws Exception {
     		EchoServer echoServer = new EchoServer(7000);
     		echoServer.start();
     	}
     }
    

———————————————————————我是分割线,下面是客户端部分———————————————————

  1. 编码,解码,协议包都是不变的

  2. 客户端启动类:

     public class EchoClient {
    
     @Value("${netty.host}")
     private String host;
     @Value("${netty.port}")
     private int port;
    
     //@PostConstruct
     public void start() throws Exception {
     	EventLoopGroup group = new NioEventLoopGroup();
    
     try {
         Bootstrap b = new Bootstrap();
         b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
                     .handler(new ChannelInitializer<Channel>() {
                         @Override
                         protected void initChannel(Channel ch) throws Exception {
                             ch.pipeline().addLast(new MyPersonDecoder());
                             ch.pipeline().addLast(new MyPersonEncoder());
                             ch.pipeline().addLast(new EchoClientHandler());
    
                         }
                 });
         ChannelFuture f = b.connect().sync();
         f.channel().writeAndFlush("hello");
    
         f.channel().closeFuture().sync();
     } finally {
         group.shutdownGracefully().sync();
     		}
     	}
     }
    
  3. 客户端Handler类:

     @ChannelHandler.Sharable
     public class EchoServerHandler extends ChannelInboundHandlerAdapter {
     @Override
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception{
     	ByteBuf in = (ByteBuf) msg;
     	System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));
     	ctx.write(in);
     }
    
     @Override
     public void channelReadComplete(ChannelHandlerContext ctx){
     	ctx.writeAndFlush(ChannelFutureListener.CLOSE);
     }
    
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
     	cause.printStackTrace();
     	ctx.close();
     	}
     }
    
  4. Application:

     @SpringBootApplication
     public class ServerApplication implements CommandLineRunner {
    
      public static void main(String[] args) {
     		SpringApplication.run(ServerApplication.class, args);
     	}
    
     @Override
     public void run(String... strings) throws Exception {
     	EchoServer echoServer = new EchoServer(7000);
     	echoServer.start();
     	}
    
     }
    

—————————————————— 参考文章,感谢该博主———————————————————————————
https://blog.csdn.net/u012150590/article/details/81255567
代码完整版:

猜你喜欢

转载自blog.csdn.net/qq_41850449/article/details/88914426