暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【Netty】自定义协议

爱编码 2021-07-20
468

小知识:绣花理论当一个人的职业生涯开始时,或者是职业生涯处于低谷时,他都必须努力借助他人的“资源”并主动义务或只取比市场更低的价格去为提供资源的人工作,在这个工作过程中,完成自己技能、关系、资金(或其他资源)的积累,求得个人人力资本质的飞跃,以获取职业发展的成功。

简介

Netty中,通讯的双方建立连接后,会把数据按照ByteBuf的方式进行传输,例如http协议中,就是通过HttpRequestDecoder对ByteBuf数据流进行处理,转换成http的对象。

实现的原理是通过Encoder把java对象转换成ByteBuf流进行传输,通过Decoder把ByteBuf转换成java对象进行处理,处理逻辑如下图所示:

步骤

  1. 制定协议(如表头,内容字节大小,内容,校验位等)

  2. 写好编码器Encoder,将数据进行编码的操作。

  3. 写好解码器Decoder,将数据进行解码的操作。

  4. 服务端和客户端的Handler处理类中处理数据。

  5. Netty服务端和客户端的pipline中添加编解码器。

实现

1.制定协议(如表头,内容字节大小,内容,校验位等)

自定义传输的实体类,其实本质上你可以将它当做自定义的协议。这里为了方便入门,就没有写正式的协议。我这里主要是走个整体流程,至于其他的查看后面的参考文章。

  1. public class UavEntity implements Serializable{


  2. private String id;//id

  3. private String name;//名称

  4. private String brand;//品牌



  5. public String getId() {

  6. return id;

  7. }


  8. public void setId(String id) {

  9. this.id = id;

  10. }


  11. public String getName() {

  12. return name;

  13. }


  14. public void setName(String name) {

  15. this.name = name;

  16. }


  17. public String getBrand() {

  18. return brand;

  19. }


  20. public void setBrand(String brand) {

  21. this.brand = brand;

  22. }



  23. @Override

  24. public String toString() {

  25. return "UavEntity{" +

  26. "id='" + id + '\'' +

  27. ", name='" + name + '\'' +

  28. ", brand='" + brand + '\'' +

  29. '}';

  30. }

  31. }

2.编码器Encoder

主要工作是将对象转换为字节写进Channel中。

  1. public class UavEncoder extends MessageToByteEncoder<UavEntity> {


  2. @Override

  3. protected void encode(ChannelHandlerContext ctx, UavEntity msg, ByteBuf out) throws Exception {

  4. byte[] datas = ByteObjConverter.ObjectToByte(msg);

  5. out.writeBytes(datas);

  6. ctx.flush();

  7. }

  8. }

3.解码器Decoder

主要是从读取bytebuf中的字节数据,将其转换为对象实体。

  1. public class UavDecoder extends ByteToMessageDecoder {

  2. @Override

  3. protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {


  4. byte[] bytes = new byte[in.readableBytes()];

  5. in.readBytes(bytes);

  6. Object obj = ByteObjConverter.ByteToObject(bytes);

  7. out.add(obj);

  8. }


  9. }

编解码工具类ByteObjConverter

  1. public class ByteObjConverter {


  2. public static Object ByteToObject(byte[] bytes) {

  3. Object obj = null;

  4. ByteArrayInputStream bi = new ByteArrayInputStream(bytes);

  5. ObjectInputStream oi = null;

  6. try {

  7. oi = new ObjectInputStream(bi);

  8. obj = oi.readObject();

  9. } catch (Exception e) {

  10. e.printStackTrace();

  11. } finally {

  12. try {

  13. bi.close();

  14. } catch (IOException e) {

  15. e.printStackTrace();

  16. }

  17. try {

  18. oi.close();

  19. } catch (IOException e) {

  20. e.printStackTrace();

  21. }

  22. }

  23. return obj;

  24. }


  25. public static byte[] ObjectToByte(Object obj) {

  26. byte[] bytes = null;

  27. ByteArrayOutputStream bo = new ByteArrayOutputStream();

  28. ObjectOutputStream oo = null;

  29. try {

  30. oo = new ObjectOutputStream(bo);

  31. oo.writeObject(obj);

  32. bytes = bo.toByteArray();

  33. } catch (Exception e) {

  34. e.printStackTrace();

  35. } finally {

  36. try {

  37. bo.close();

  38. } catch (IOException e) {

  39. e.printStackTrace();

  40. }

  41. try {

  42. oo.close();

  43. } catch (IOException e) {

  44. e.printStackTrace();

  45. }

  46. }

  47. return (bytes);

  48. }

  49. }

4.服务端和客户端Handler处理类

客户端Handler处理类

主要负责往服务端写数据。

  1. public class ClientInitHandler extends ChannelInboundHandlerAdapter {

  2. @Override

  3. public void channelActive(ChannelHandlerContext ctx) throws Exception {

  4. System.out.println("HelloClientIntHandler.channelActive");

  5. UavEntity ua = new UavEntity();

  6. ua.setName("四翼无人机Plus");

  7. ua.setId(UUID.randomUUID().toString());

  8. ua.setBrand("大疆");

  9. ctx.writeAndFlush(ua);

  10. }

  11. }

服务端Handler处理类

主要是负责读取服务端接收到的数据直接打印。

  1. public class UavHandler extends ChannelInboundHandlerAdapter {


  2. @Override

  3. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

  4. UavEntity uav = (UavEntity) msg;

  5. System.out.println("UavHandler read msg from client :" + uav);

  6. }


  7. @Override

  8. public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {

  9. ctx.flush();

  10. }


  11. @Override

  12. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {


  13. }

  14. }

5.往服务端和客户端添加编解码器。

Netty客户端代码如下:

主要是添加编码器和处理Handler

  1. /**

  2. * 客户端

  3. * 1.为初始化客户端,创建一个Bootstrap实例

  4. * 2.为进行事件处理分配了一个NioEventLoopGroup实例,其中事件处理包括创建新的连接以及处理入站和出站数据;

  5. * 3.当连接被建立时,一个EchoClientHandler实例会被安装到(该Channel的一个ChannelPipeline中;

  6. * 4.在一切都设置完成后,调用Bootstrap.connect()方法连接到远程节点。

  7. */

  8. public class NettyClient {

  9. public void connect(String host, int port) throws Exception {

  10. EventLoopGroup workerGroup = new NioEventLoopGroup();


  11. try {

  12. Bootstrap b = new Bootstrap();

  13. b.group(workerGroup);

  14. b.channel(NioSocketChannel.class);

  15. b.option(ChannelOption.SO_KEEPALIVE, true);

  16. b.handler(new ChannelInitializer<SocketChannel>() {

  17. @Override

  18. public void initChannel(SocketChannel ch) throws Exception {

  19. ch.pipeline().addLast(new UavEncoder());

  20. // ch.pipeline().addLast(new SmartCarEncoder());

  21. // ch.pipeline().addLast(new SmartCarDecoder());

  22. ch.pipeline().addLast(new ClientInitHandler());

  23. // ch.pipeline().addLast(new ClientHandler());

  24. }

  25. });


  26. ChannelFuture f = b.connect(host, port).sync();

  27. f.channel().closeFuture().sync();

  28. } finally {

  29. workerGroup.shutdownGracefully();

  30. }


  31. }


  32. public static void main(String[] args) throws Exception {

  33. NettyClient client = new NettyClient();

  34. client.connect("127.0.0.1", 8000);

  35. }

  36. }

Netty服务端代码如下:

主要是添加解码器和处理Handler

  1. public class NettyServer {

  2. public void start(int port) throws Exception {

  3. EventLoopGroup bossGroup = new NioEventLoopGroup();

  4. EventLoopGroup workerGroup = new NioEventLoopGroup();

  5. try {

  6. ServerBootstrap b = new ServerBootstrap();

  7. b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)

  8. .childHandler(new ChannelInitializer<SocketChannel>() {

  9. @Override

  10. public void initChannel(SocketChannel ch) throws Exception {

  11. ch.pipeline().addLast(new UavDecoder());

  12. // ch.pipeline().addLast(new SmartCarEncoder());

  13. // ch.pipeline().addLast(new SmartCarDecoder());

  14. ch.pipeline().addLast(new UavHandler());

  15. // ch.pipeline().addLast(new ServerHandler());

  16. }

  17. }).option(ChannelOption.SO_BACKLOG, 128)

  18. .childOption(ChannelOption.SO_KEEPALIVE, true);


  19. ChannelFuture f = b.bind(port).sync();


  20. f.channel().closeFuture().sync();

  21. } finally {

  22. workerGroup.shutdownGracefully();

  23. bossGroup.shutdownGracefully();

  24. }

  25. }


  26. public static void main(String[] args) throws Exception {

  27. NettyServer server = new NettyServer();

  28. server.start(8000);

  29. }

  30. }

参考文章

https://www.cnblogs.com/zeroone/p/8490904.html https://www.cnblogs.com/zeroone/p/8490921.html

总结

Netty提供了编解码器就让我们可以非常方便的自定义自己传输数据的格式,同时可以将数据进行加密等操作。

如想要了解防止socket流攻击、TCP粘包/拆包等更深入问题可以看参考文章中的两篇文章。

最后

如果对 Java、大数据感兴趣请长按二维码,关注公众号【爱编码】,小编会一直更新文章的哦。


文章转载自爱编码,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论