快速搞定netty自定义通信协议,就是这么简单!!

一、要搞定一个自定义通信协议其实有三个关键步骤,其他和普通开发流程都一样。

1、首先需要定义一个协议;

2、其次定制一个编码器,便于发送端把自定义的协议给解码成数据流发送到网络上;

3、最后定制一个解码器,便于接收端把收到的数据流再解码成自定协议的数据结构;

下面我们分别纤细介绍这三个数据结构。

首先自定一个协议

@Data
@AllArgsConstructor
public class NettyProtocol {
    /**
     * 0x00 表示请求数据;
     * 0x01 表示响应数据;
     */
    byte type;
    //表示数据部分数据长度,便于在解码时通过这个长度解决拆沾包问题
    int bodyLength;
    //有效数据体
    String body;
}

定制一个编码器

public class NettyProtocolEncoder extends MessageToByteEncoder<NettyProtocol> {
    @Override
    protected void encode(ChannelHandlerContext ctx, NettyProtocol msg, ByteBuf out) throws Exception {
        //将数据结构的标识写入数据流
        out.writeByte(msg.getType());
        //将字符串转字节数组,然后写入数据流
        byte[] body = msg.getBody().getBytes(CharsetUtil.UTF_8);
        msg.setBodyLength(body.length);
        out.writeInt(msg.getBodyLength());
        //将主体数据写入数据流
        out.writeBytes(body);
    }
}

定制一个解码器(理解解码器中state变量使用逻辑是解决粘包拆包问题关键)

public class NettyProtocolDecoder extends ByteToMessageDecoder {
    //注意每次数据写入都会触发一次,解码调用,state作用是为了保存目前正在处理协议头还是数据体的状态,便于在数据不完整时,保存处理状态并等待下次被触发调用
    private enum State {
        Header,
        Body
    }
    private State state = State.Header;
    private int bodyLength;
    private String body;

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        switch (state) {
            case Header:
                if (in.readableBytes() < 5) {
                    System.out.println("Header is not received over!");
                    return;
                }
                in.skipBytes(1);
                bodyLength = in.readInt();
                state = State.Body;
            case Body:
                if (in.readableBytes() < bodyLength) {
                    System.out.println("Body is not received over!");
                    return;
                }
                ByteBuf bodyBytes = in.readBytes(bodyLength);
                body = bodyBytes.toString(CharsetUtil.UTF_8);
                out.add(new NettyProtocol((byte)0x01,bodyLength,body));
                state = State.Header;
        }

    }
}

二、组装服务端代码 

public class NettyServer {
    public static void startServer(String hostName, int port) {
        startServer0(hostName, port);
    }

    private static void startServer0(String hostname, int port) {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile).trustManager(rootFile).clientAuth(ClientAuth.REQUIRE).build();
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                                      @Override
                                      protected void initChannel(SocketChannel ch) throws Exception {
                                          //添加编码器
                                          pipeline.addLast(new NettyProtocolEncoder());
                                          //添加解码器
                                          pipeline.addLast(new NettyProtocolDecoder());
                                          //添加处理器
                                          pipeline.addLast(new NettyServerHandler());
                                      }
                                  }

                    );

            ChannelFuture channelFuture = serverBootstrap.bind(hostname, port).sync();
            System.out.println("服务提供方开始提供服务~~");
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

三、组装客户端

private static void initClient() {
        try {
        SslContext sslCtx = SslContextBuilder.forClient().keyManager(certChainFile, keyFile).trustManager(rootFile).build();
        //这里是自定义的客户端处理器
        client = new NettyClientHandler();
        //创建EventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(
                        new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();
                                pipeline.addLast(new NettyProtocolEncoder());
                                pipeline.addLast(new NettyProtocolDecoder());
                                pipeline.addLast(client);
                            }
                        }
                );


            bootstrap.connect("127.0.0.1", 7000).sync();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单Netty自定义协议的示例代码,该协议由消息头和消息体组成,消息头包含消息的长度信息,消息体包含具体的消息内容。 1. 定义消息类 ```java public class MyMessage { private int length; private String content; // getter and setter methods } ``` 2. 编写编解码器 ```java public class MyMessageCodec extends MessageToByteEncoder<MyMessage> { @Override protected void encode(ChannelHandlerContext ctx, MyMessage msg, ByteBuf out) throws Exception { byte[] contentBytes = msg.getContent().getBytes(CharsetUtil.UTF_8); out.writeInt(contentBytes.length); out.writeBytes(contentBytes); } } public class MyMessageDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { if (in.readableBytes() < 4) { return; } in.markReaderIndex(); int length = in.readInt(); if (in.readableBytes() < length) { in.resetReaderIndex(); return; } byte[] contentBytes = new byte[length]; in.readBytes(contentBytes); String content = new String(contentBytes, CharsetUtil.UTF_8); MyMessage message = new MyMessage(); message.setLength(length); message.setContent(content); out.add(message); } } ``` 3. 编写服务端和客户端代码 服务端代码: ```java public class MyServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new MyMessageDecoder()); pipeline.addLast(new MyServerHandler()); pipeline.addLast(new MyMessageCodec()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(8888).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } public class MyServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { MyMessage message = (MyMessage) msg; System.out.println("Server receive message: " + message.getContent()); message.setContent("Hello, " + message.getContent() + "!"); ctx.writeAndFlush(message); } } ``` 客户端代码: ```java public class MyClient { public static void main(String[] args) throws Exception { EventLoopGroup workerGroup = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(workerGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new MyMessageCodec()); pipeline.addLast(new MyMessageDecoder()); pipeline.addLast(new MyClientHandler()); } }); ChannelFuture f = b.connect("localhost", 8888).sync(); for (int i = 0; i < 10; i++) { MyMessage message = new MyMessage(); message.setContent("world-" + i); f.channel().writeAndFlush(message); Thread.sleep(1000); } f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); } } } public class MyClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { MyMessage message = (MyMessage) msg; System.out.println("Client receive message: " + message.getContent()); } } ``` 以上代码演示了如何使用Netty实现自定义协议,其中MyMessageCodec和MyMessageDecoder负责编解码,MyServer和MyServerHandler负责服务端逻辑,MyClient和MyClientHandler负责客户端逻辑。在本示例中,自定义协议包含消息头和消息体,消息头包含消息长度信息,消息体包含具体的消息内容。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值