「Netty系列」使用Netty开发,踩坑到解决全过程(附解决方案源码)

​周五没事干,对之前文章进行整理下。来到现公司已经有半年时间。从当初对网络编程的懵懵懂懂,到现在使用Netty对服务端开发完成并上线。

今天这篇文章总结下在这个过程困扰我的最多点--“TCP协议进行通信,出现粘包和拆包”。使用TCP协议通信一定会出现粘包和拆包?为什么加上固定包头就可以解决粘包和拆包?

首先我们先分析下,什么情况下会出现粘包?是TCP通信都有可能出现粘包?来通过一个Demo模拟下TCP协议通信,一起分析下粘包出现的可能性。来来来!

1.1 实例不出现粘包

1.1.1 代码实现

客户端以每500毫秒间隔向服务端发送相同字节数组。

1. 服务端的配置类

public class TcpServer {
    private final int port;

    public TcpServer(int port) {
        this.port = port;
    }

    public void init() {
        NioEventLoopGroup boss = new NioEventLoopGroup();//主线程组
        NioEventLoopGroup work = new NioEventLoopGroup();//工作线程组
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();//引导对象
            bootstrap.group(boss, work);//配置工作线程组
            bootstrap.channel(NioServerSocketChannel.class);//配置为NIO的socket通道
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel ch) throws Exception {//绑定通道参数
                    ch.pipeline().addLast("logging", new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程
                    ch.pipeline().addLast("encode", new EncoderHandler());//编码器。发送消息时候用过
                    ch.pipeline().addLast("decode", new DecoderHandler());//解码器,接收消息时候用
                    ch.pipeline().addLast("handler", new TcpServerHandler());//业务处理类,最终的消息会在这个handler中进行业务处理
                }
            });
            //使用了Future来启动线程,并绑定了端口
            ChannelFuture future = bootstrap.bind(port).sync();
            //以异步的方式关闭端口
            future.channel().closeFuture().sync();
        } catch (Exception e) {
        } finally {
            work.shutdownGracefully();
            //出现异常后,关闭线程组
            boss.shutdownGracefully();
        }

    }
}

2. 服务端解码器(decode)

public class DecoderHandler extends ByteToMessageDecoder {

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("byteBuf的容量为:" + in.capacity());
        System.out.println("byteBuf的可读容量为:" + in.readableBytes());
        System.out.println("byteBuf的可写容量为:" + in.writableBytes());
        byte[] data = new byte[in.readableBytes()];
        //读取核心的数据
        in.readBytes(data);
        out.add(data);
    }
}

3. 服务端编码器(encode)

public class EncoderHandler extends MessageToByteEncoder {
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        out.writeBytes((byte[]) msg);
    }
}

4. 服务端的ChannelHandler

public class TcpServerHandler extends ChannelInboundHandlerAdapter {


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        byte[] recevierByte = (byte[]) msg;
        String recevierString = ByteTransform.bytesToHexString(recevierByte);
        System.out.println("-------------------长度为:" + recevierString.length());
        System.out.println("---tcp服务接受设备端加密数据:" + recevierString);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    /**
     * 客户端断开连接触发方法
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }

    /**
     * 方法中报错,触发方法
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}

5. 服务端启动类

public class StartServer {
    public static void main(String[] args) {
        try {
            new TcpServer(5566).init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6. 工具类(知道方法是干嘛的就行)

public class ByteTransformIntUtil {
    /**
     * 根据byte转化为int
     */
    public static int getIntFromBytes(byte high_h, byte high_l, byte low_h, byte low_l) {
        return (high_h & 0xff) << 24 | (high_l & 0xff) << 16 | (low_h & 0xff) << 8 | low_l & 0xff;
    }
    /**
     * 根据byte转化为int
     */
    public static int getIntFromBytes(byte low_h, byte low_l) {
        return ByteTransformIntUtil.getIntFromBytes((byte)0,(byte)0,low_h,low_l);
    }
}

7. 客户端的配置类

public class MyRPCClient {

    public void start(String host, int port) throws Exception {
        //定义工作线程组
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            //注意:client使用的是Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(worker)
                    .channel(NioSocketChannel.class) //注意:client使用的是NioSocketChannel
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
       
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值