美文网首页
Netty粘拆包

Netty粘拆包

作者: 诺之林 | 来源:发表于2019-02-28 11:35 被阅读2次

    本文的示例代码参考NettySticky

    目录

    准备

    curl -s "https://get.sdkman.io" | bash
    
    sdk install gradle 4.6
    

    更多参考SDKMAN!

    mkdir NettySticky && cd NettySticky
    
    gradle init --type java-application
    
    gradle run
    # Hello world.
    

    记得添加.gitignore => "gi gradle >> .gitignore"

    Netty

    Startup

    vim build.gradle
    # compile 'io.netty:netty-all:4.1.25.Final'
    
    mkdir -p src/main/java/server
    
    mv src/main/java/App.java src/main/java/server/Server.java
    
    vim src/main/java/server/Server.java
    
    package server;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    
    public class Server {
    
        private static final int PORT = 8888;
    
        public static void main(String[] args) {
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
    
                        }
                    });
    
            serverBootstrap.bind(PORT).addListener(future -> {
                if (future.isSuccess()) {
                    System.out.println("端口[" + PORT + "] 绑定成功!");
                } else {
                    System.err.println("端口[" + PORT + "] 绑定失败!!!");
                }
            });
        }
    }
    
    sed -i "" "s/App/server.Server/g" build.gradle && gradle run
    # 端口[8888] 绑定成功!
    
    telnet 127.0.0.1 8888
    # Connected to localhost.
    

    Protocol

    vim build.gradle
    # compile 'com.alibaba:fastjson:1.2.55'
    # compileOnly 'org.projectlombok:lombok:1.18.6'
    
    mkdir -p src/main/java/protocol
    
    vim src/main/java/protocol/Packet.java
    
    package protocol;
    
    import com.alibaba.fastjson.annotation.JSONField;
    import lombok.Data;
    
    @Data
    public abstract class Packet {
        @JSONField(deserialize = false, serialize = false)
        private Byte version = 1;
    
        @JSONField(serialize = false)
        public abstract Byte getCommand();
    }
    
    vim src/main/java/protocol/PacketCodec.java
    
    package protocol;
    
    import com.alibaba.fastjson.JSON;
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.ByteBufAllocator;
    import protocol.command.Command;
    import protocol.request.LoginRequestPacket;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class PacketCodec {
        public static final PacketCodec INSTANCE = new PacketCodec();
    
        private static final int MAGIC_NUMBER = 0x12345678;
        private static final Map<Byte, Class<? extends Packet>> packetTypeMap;
    
        static {
            packetTypeMap = new HashMap<>();
            packetTypeMap.put(Command.LOGIN, LoginRequestPacket.class);
        }
    
        public ByteBuf encode(Packet packet) {
            byte[] bytes = JSON.toJSONBytes(packet);
            ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
    
            byteBuf.writeInt(MAGIC_NUMBER);
            byteBuf.writeByte(packet.getVersion());
            byteBuf.writeByte(packet.getCommand());
            byteBuf.writeInt(bytes.length);
            byteBuf.writeBytes(bytes);
    
            return byteBuf;
        }
    
        public Packet decode(ByteBuf byteBuf) {
            byteBuf.skipBytes(4);
            byteBuf.skipBytes(1);
            byte command = byteBuf.readByte();
            int length = byteBuf.readInt();
            byte[] bytes = new byte[length];
            byteBuf.readBytes(bytes);
    
            return JSON.parseObject(bytes, packetTypeMap.get(command));
        }
    }
    
    mkdir -p src/main/java/protocol/command
    
    vim src/main/java/protocol/command/Command.java
    
    package protocol.command;
    
    public interface Command {
        Byte LOGIN = 1;
    }
    
    mkdir -p src/main/java/protocol/request
    
    vim src/main/java/protocol/request/LoginRequestPacket.java
    
    package protocol.request;
    
    import lombok.Data;
    import protocol.Packet;
    import protocol.command.Command;
    
    @Data
    public class LoginRequestPacket extends Packet {
        private Integer userId;
        private String username;
    
        @Override
        public Byte getCommand() {
            return Command.LOGIN;
        }
    }
    

    Client

    mkdir -p src/main/java/client
    
    vim src/main/java/client/Client.java
    
    package client;
    
    import io.netty.bootstrap.Bootstrap;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    
    public class Client {
        private static final String HOST = "127.0.0.1";
        private static final int PORT = 8888;
    
        public static void main(String[] args) {
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    
            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                    .group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ClientHandler());
                        }
                    });
    
            bootstrap.connect(HOST, PORT).addListener(future -> {
                if (future.isSuccess()) {
                    System.out.println("连接成功!");
                } else {
                    System.err.println("连接失败!!!");
                }
            });
        }
    }
    
    vim src/main/java/client/ClientHandler.java
    
    package client;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import protocol.PacketCodec;
    import protocol.request.LoginRequestPacket;
    
    public class ClientHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            LoginRequestPacket packetLogin = new LoginRequestPacket();
            packetLogin.setUserId(1);
            packetLogin.setUsername("xiaowang");
    
            ByteBuf byteBuf = PacketCodec.INSTANCE.encode(packetLogin);
            ctx.channel().writeAndFlush(byteBuf);
            System.out.println("登录请求");
        }
    }
    

    Server

    vim src/main/java/server/Server.java
    
    package server;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    
    public class Server {
    
        private static final int PORT = 8888;
    
        public static void main(String[] args) {
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
    
            serverBootstrap.bind(PORT).addListener(future -> {
                if (future.isSuccess()) {
                    System.out.println("端口[" + PORT + "] 绑定成功!");
                } else {
                    System.err.println("端口[" + PORT + "] 绑定失败!!!");
                }
            });
        }
    }
    
    vim src/main/java/server/ServerHandler.java
    
    package server;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import protocol.Packet;
    import protocol.PacketCodec;
    import protocol.request.LoginRequestPacket;
    
    public class ServerHandler extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf byteBuf = (ByteBuf) msg;
            Packet packet = PacketCodec.INSTANCE.decode(byteBuf);
    
            if (packet instanceof LoginRequestPacket) {
                System.out.println(packet);
            } else {
                System.out.println("unknown packet");
            }
        }
    }
    

    测试

    gradle run
    # 端口[8888] 绑定成功!
    # LoginRequestPacket(userId=1, username=xiaowang)
    
    sed -i "" "s/server.Server/client.Client/g" build.gradle && gradle run
    # 连接成功!
    # 登录请求
    

    粘包

    vim src/main/java/client/ClientHandler.java
    
    package client;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import protocol.PacketCodec;
    import protocol.request.LoginRequestPacket;
    
    public class ClientHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            for (int i = 0; i < 10; i++) {
                LoginRequestPacket packetLogin = new LoginRequestPacket();
                packetLogin.setUserId(1);
                packetLogin.setUsername("xiaowang");
    
                ByteBuf byteBuf = PacketCodec.INSTANCE.encode(packetLogin);
                ctx.channel().writeAndFlush(byteBuf);
                System.out.println("登录请求");
            }
        }
    }
    
    vim build.gradle
    # mainClassName = 'Server'
    
    gradle run
    # 端口[8888] 绑定成功!
    # PacketLogin(userId=1, username=xiaowang)
    # PacketLogin(userId=1, username=xiaowang)
    
    vim build.gradle
    # mainClassName = 'Client'
    
    gradle run
    # 连接成功!
    # 登录请求
    # 登录请求
    # 登录请求
    # 登录请求
    # 登录请求
    
    vim src/main/java/Server.java
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.channel.socket.nio.NioSocketChannel;
    import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
    
    public class Server {
    
        private static final int PORT = 8888;
    
        public static void main(String[] args) {
            NioEventLoopGroup bossGroup = new NioEventLoopGroup();
            NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 6, 4));
                            ch.pipeline().addLast(new ServerHandler());
                        }
                    });
    
            serverBootstrap.bind(PORT).addListener(future -> {
                if (future.isSuccess()) {
                    System.out.println("端口[" + PORT + "] 绑定成功!");
                } else {
                    System.err.println("端口[" + PORT + "] 绑定失败!!!");
                }
            });
        }
    }
    
    vim build.gradle
    # mainClassName = 'Server'
    
    gradle run
    # 端口[8888] 绑定成功!
    # PacketLogin(userId=1, username=xiaowang)
    # PacketLogin(userId=1, username=xiaowang)
    # PacketLogin(userId=1, username=xiaowang)
    # PacketLogin(userId=1, username=xiaowang)
    # PacketLogin(userId=1, username=xiaowang)
    
    vim build.gradle
    # mainClassName = 'Client'
    
    gradle run
    # 连接成功!
    # 登录请求
    # 登录请求
    # 登录请求
    # 登录请求
    # 登录请求
    

    下一步

    鉴权

    心跳

    相关文章

      网友评论

          本文标题:Netty粘拆包

          本文链接:https://www.haomeiwen.com/subject/zrffuqtx.html