• netty 支持多种通讯协议


    通讯协议,指的是把Netty通讯管道中的二进制流转换为对象、把对象转换成二进制流的过程。转换过程追根究底还是ChannelInboundHandler、ChannelOutboundHandler的实现类在进行处理。ChannelInboundHandler负责把二进制流转换为对象,ChannelOutboundHandler负责把对象转换为二进制流。

    接下来要构建一个Server,同时支持Person通讯协议和String通讯协议。

    • Person通讯协议:二进制流与Person对象间的互相转换。
    • String通讯协议:二进制流与有固定格式要求的String的相互转换。String格式表示的也是一个Person对象,格式规定为:name:xx;age:xx;sex:xx;
    这时候,来自客户端的请求,会依次传递给两个通讯解析接口进行解析,每个通讯接口判断是否是匹配的协议,如果是则进行解析,如果不是则传递给其它通讯接口进行解析。
     
    实体类:Person
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import java.io.Serializable;  
    4.   
    5. public class Person implements Serializable{  
    6.     private static final long   serialVersionUID    = 1L;  
    7.     private String  name;  
    8.     private String  sex;  
    9.     private int     age;  
    10.   
    11.     public String toString() {  
    12.         return "name:" + name + " sex:" + sex + " age:" + age;  
    13.     }  
    14.   
    15.     public String getName() {  
    16.         return name;  
    17.     }  
    18.   
    19.     public void setName(String name) {  
    20.         this.name = name;  
    21.     }  
    22.   
    23.     public String getSex() {  
    24.         return sex;  
    25.     }  
    26.   
    27.     public void setSex(String sex) {  
    28.         this.sex = sex;  
    29.     }  
    30.   
    31.     public int getAge() {  
    32.         return age;  
    33.     }  
    34.   
    35.     public void setAge(int age) {  
    36.         this.age = age;  
    37.     }  
    38. }  

    Server端的类为:Server PersonDecoder StringDecoder BusinessHandler
    1、Server 开启Netty服务
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import io.netty.bootstrap.ServerBootstrap;  
    4. import io.netty.channel.ChannelFuture;  
    5. import io.netty.channel.ChannelInitializer;  
    6. import io.netty.channel.ChannelOption;  
    7. import io.netty.channel.EventLoopGroup;  
    8. import io.netty.channel.nio.NioEventLoopGroup;  
    9. import io.netty.channel.socket.SocketChannel;  
    10. import io.netty.channel.socket.nio.NioServerSocketChannel;  
    11.   
    12. // 测试coder 和 handler 的混合使用  
    13. public class Server {  
    14.     public void start(int port) throws Exception {  
    15.         EventLoopGroup bossGroup = new NioEventLoopGroup();  
    16.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
    17.         try {  
    18.             ServerBootstrap b = new ServerBootstrap();  
    19.             b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)  
    20.                     .childHandler(new ChannelInitializer<SocketChannel>() {  
    21.                         @Override  
    22.                         public void initChannel(SocketChannel ch) throws Exception {  
    23.                             ch.pipeline().addLast(new PersonDecoder());  
    24.                             ch.pipeline().addLast(new StringDecoder());  
    25.                             ch.pipeline().addLast(new BusinessHandler());  
    26.                         }  
    27.                     }).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);  
    28.   
    29.             ChannelFuture f = b.bind(port).sync();  
    30.   
    31.             f.channel().closeFuture().sync();  
    32.         } finally {  
    33.             workerGroup.shutdownGracefully();  
    34.             bossGroup.shutdownGracefully();  
    35.         }  
    36.     }  
    37.   
    38.     public static void main(String[] args) throws Exception {  
    39.         Server server = new Server();  
    40.         server.start(8000);  
    41.     }  
    42. }  
    2、PersonDecoder  把二进制流转换成Person对象
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import io.netty.buffer.ByteBuf;  
    4. import io.netty.channel.ChannelHandlerContext;  
    5. import io.netty.handler.codec.ByteToMessageDecoder;  
    6.   
    7. import java.util.List;  
    8.   
    9. import com.guowl.utils.ByteBufToBytes;  
    10. import com.guowl.utils.ByteObjConverter;  
    11.   
    12. public class PersonDecoder extends ByteToMessageDecoder {  
    13.     @Override  
    14.     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {  
    15.         byte n = "n".getBytes()[0];  
    16.         byte p = in.readByte();  
    17.         in.resetReaderIndex();  
    18.         if (n != p) {  
    19.             // 把读取的起始位置重置  
    20.             ByteBufToBytes reader = new ByteBufToBytes();  
    21.             out.add(ByteObjConverter.byteToObject(reader.read(in)));  
    22.         } else {  
    23.             // 执行其它的decode  
    24.             ctx.fireChannelRead(in);  
    25.         }  
    26.     }  
    27. }  
    3、StringDecoder 把满足条件的字符串转换成Person对象
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import io.netty.buffer.ByteBuf;  
    4. import io.netty.channel.ChannelHandlerContext;  
    5. import io.netty.handler.codec.ByteToMessageDecoder;  
    6.   
    7. import java.util.List;  
    8.   
    9. import com.guowl.utils.ByteBufToBytes;  
    10.   
    11. public class StringDecoder extends ByteToMessageDecoder {  
    12.     @Override  
    13.     protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {  
    14.         // 判断是否是String协议  
    15.         byte n = "n".getBytes()[0];  
    16.         byte p = in.readByte();  
    17.         // 把读取的起始位置重置  
    18.         in.resetReaderIndex();  
    19.         if (n == p) {  
    20.             ByteBufToBytes reader = new ByteBufToBytes();  
    21.             String msg = new String(reader.read(in));  
    22.             Person person = buildPerson(msg);  
    23.             out.add(person);  
    24.             //in.release();  
    25.         } else {  
    26.             ctx.fireChannelRead(in);  
    27.         }  
    28.     }  
    29.   
    30.     private Person buildPerson(String msg) {  
    31.         Person person = new Person();  
    32.         String[] msgArray = msg.split(";|:");  
    33.         person.setName(msgArray[1]);  
    34.         person.setAge(Integer.parseInt(msgArray[3]));  
    35.         person.setSex(msgArray[5]);  
    36.         return person;  
    37.     }  
    38. }  
    4、BusinessHandler 展现客户端请求的内容
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import io.netty.channel.ChannelHandlerContext;  
    4. import io.netty.channel.ChannelInboundHandlerAdapter;  
    5.   
    6. import org.slf4j.Logger;  
    7. import org.slf4j.LoggerFactory;  
    8.   
    9. public class BusinessHandler extends ChannelInboundHandlerAdapter {  
    10.     private Logger  logger  = LoggerFactory.getLogger(BusinessHandler.class);  
    11.   
    12.     @Override  
    13.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
    14.         Person person = (Person) msg;  
    15.         logger.info("BusinessHandler read msg from client :" + person);  
    16.     }  
    17.   
    18.     @Override  
    19.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
    20.         ctx.flush();  
    21.     }  
    [java] view plain copy
    1. <span style="white-space:pre">    </span>// 解决注意事项1中的问题。  
    [java] view plain copy
    1. <pre name="code" class="java"><span style="white-space:pre">    </span>@Override  
    2.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
    3.         ctx.close();  
    4.     }  
    }
    客户端1发送Person格式的协议:Client ClientInitHandler PersonEncoder
    1、Client 
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import io.netty.bootstrap.Bootstrap;  
    4. import io.netty.channel.ChannelFuture;  
    5. import io.netty.channel.ChannelInitializer;  
    6. import io.netty.channel.ChannelOption;  
    7. import io.netty.channel.EventLoopGroup;  
    8. import io.netty.channel.nio.NioEventLoopGroup;  
    9. import io.netty.channel.socket.SocketChannel;  
    10. import io.netty.channel.socket.nio.NioSocketChannel;  
    11.   
    12. public class Client {  
    13.     public void connect(String host, int port) throws Exception {  
    14.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
    15.   
    16.         try {  
    17.             Bootstrap b = new Bootstrap();   
    18.             b.group(workerGroup);   
    19.             b.channel(NioSocketChannel.class);   
    20.             b.option(ChannelOption.SO_KEEPALIVE, true);   
    21.             b.handler(new ChannelInitializer<SocketChannel>() {  
    22.                 @Override  
    23.                 public void initChannel(SocketChannel ch) throws Exception {  
    24.                     ch.pipeline().addLast(new PersonEncoder());  
    25.                     Person person = new Person();  
    26.                     person.setName("guowl");  
    27.                     person.setSex("man");  
    28.                     person.setAge(30);  
    29.                     ch.pipeline().addLast(new ClientInitHandler(person));  
    30.                 }  
    31.             });  
    32.   
    33.             ChannelFuture f = b.connect(host, port).sync();  
    34.             f.channel().closeFuture().sync();  
    35.         } finally {  
    36.             workerGroup.shutdownGracefully();  
    37.         }  
    38.   
    39.     }  
    40.   
    41.     public static void main(String[] args) throws Exception {  
    42.         Client client = new Client();  
    43.         client.connect("127.0.0.1", 8000);  
    44.     }  
    45. }  
    2、ClientInitHandler 向服务端发送Person对象
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import io.netty.channel.ChannelHandlerContext;  
    4. import io.netty.channel.ChannelInboundHandlerAdapter;  
    5.   
    6. import org.slf4j.Logger;  
    7. import org.slf4j.LoggerFactory;  
    8.   
    9. public class ClientInitHandler extends ChannelInboundHandlerAdapter {  
    10.     private static Logger   logger  = LoggerFactory.getLogger(ClientInitHandler.class);  
    11.     private Person person;  
    12.     public ClientInitHandler(Person person){  
    13.         this.person = person;  
    14.     }  
    15.     @Override  
    16.     public void channelActive(ChannelHandlerContext ctx) throws Exception {  
    17.         logger.info("ClientInitHandler.channelActive");  
    18.         ctx.write(person);  
    19.         ctx.flush();  
    20.     }  
    21. }  
    3、PersonEncoder 把Person对象转换成二进制进行传送
    [java] view plain copy
    1. package com.guowl.testobjcoder;  
    2.   
    3. import com.guowl.utils.ByteObjConverter;  
    4.   
    5. import io.netty.buffer.ByteBuf;  
    6. import io.netty.channel.ChannelHandlerContext;  
    7. import io.netty.handler.codec.MessageToByteEncoder;  
    8.   
    9. public class PersonEncoder extends MessageToByteEncoder<Person>  {  
    10.   
    11.     @Override  
    12.     protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception {  
    13.         out.writeBytes(ByteObjConverter.objectToByte(msg));  
    14.     }  
    15. }  
    客户端2发送String格式的协议:Client2 StringEncoder 同样使用了客户端1中定义的ClientInitHandler 进行数据发送操作。
    1、Client2 
    [java] view plain copy
    1. package com.guowl.testobjcoder.client2;  
    2.   
    3. import io.netty.bootstrap.Bootstrap;  
    4. import io.netty.channel.ChannelFuture;  
    5. import io.netty.channel.ChannelInitializer;  
    6. import io.netty.channel.ChannelOption;  
    7. import io.netty.channel.EventLoopGroup;  
    8. import io.netty.channel.nio.NioEventLoopGroup;  
    9. import io.netty.channel.socket.SocketChannel;  
    10. import io.netty.channel.socket.nio.NioSocketChannel;  
    11.   
    12. import com.guowl.testobjcoder.ClientInitHandler;  
    13. import com.guowl.testobjcoder.Person;  
    14.   
    15. public class Client2 {  
    16.     public void connect(String host, int port) throws Exception {  
    17.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
    18.   
    19.         try {  
    20.             Bootstrap b = new Bootstrap();   
    21.             b.group(workerGroup);   
    22.             b.channel(NioSocketChannel.class);   
    23.             b.option(ChannelOption.SO_KEEPALIVE, true);   
    24.             b.handler(new ChannelInitializer<SocketChannel>() {  
    25.                 @Override  
    26.                 public void initChannel(SocketChannel ch) throws Exception {  
    27.                     ch.pipeline().addLast(new StringEncoder());  
    28.                     Person person = new Person();  
    29.                     person.setName("guoxy");  
    30.                     person.setSex("girl");  
    31.                     person.setAge(4);  
    32.                     ch.pipeline().addLast(new ClientInitHandler(person));  
    33.                 }  
    34.             });  
    35.   
    36.             ChannelFuture f = b.connect(host, port).sync();  
    37.             f.channel().closeFuture().sync();  
    38.         } finally {  
    39.             workerGroup.shutdownGracefully();  
    40.         }  
    41.   
    42.     }  
    43.   
    44.     public static void main(String[] args) throws Exception {  
    45.         Client2 client = new Client2();  
    46.         client.connect("127.0.0.1", 8000);  
    47.     }  
    48. }  
    2、StringEncoder 把Person对象转换成固定格式的String的二进制流进行传送
    [java] view plain copy
    1. package com.guowl.testobjcoder.client2;  
    2.   
    3. import io.netty.buffer.ByteBuf;  
    4. import io.netty.channel.ChannelHandlerContext;  
    5. import io.netty.handler.codec.MessageToByteEncoder;  
    6.   
    7. import com.guowl.testobjcoder.Person;  
    8.   
    9. public class StringEncoder extends MessageToByteEncoder<Person> {  
    10.   
    11.     @Override  
    12.     protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception {  
    13.         // 转成字符串:name:xx;age:xx;sex:xx;  
    14.         StringBuffer sb = new StringBuffer();  
    15.         sb.append("name:").append(msg.getName()).append(";");  
    16.         sb.append("age:").append(msg.getAge()).append(";");  
    17.         sb.append("sex:").append(msg.getSex()).append(";");  
    18.         out.writeBytes(sb.toString().getBytes());  
    19.     }  
    20. }  
    其它:工具类ByteBufToBytes(读取ByteBuf数据的工具类)、ByteObjConverter(Object与byte互转的工具类)在以前的文章中已经存在,在此省略。
     
    注意事项:
    1、该段代码能运行出结果,但是运行的时候会报 io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1 异常,已经解决。日志中的提示信息为:
    An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception
    说明缺少exceptionCaught方法,在server端最后一个Handler中增加这个方法即可。
    2、PersonDecoder和StringDecoder中有一个if判断,是为了判断消息究竟是什么协议。如果是String协议的话,格式是【name:xx;age:xx;sex:xx;】,第一个字母是英文字母n,所以判断协议类型时候是读取二进制流的第一个字符进行判断,当然这种判断方式非常幼稚,以后有机会可以进行改善。
  • 相关阅读:
    塔吊滑车移动
    csv合并某几列
    合并两个csv
    QFile
    python alphashape
    pip install econml
    csv增加一列
    连接redis数据库日志
    yolov5训练识别钢筋模型
    redis查看某个key的类型
  • 原文地址:https://www.cnblogs.com/zeroone/p/8490895.html
Copyright © 2020-2023  润新知