netty之 协议处理数据粘包问题

本文详细介绍了自定义网络协议的设计与实现过程,包括协议格式的定义、服务端与客户端代码的编写,以及如何使用工具类进行协议的转换。通过具体示例,展示了如何在服务端和客户端之间进行基于自定义协议的数据通信。


一:前言

  基于 https://www.cnblogs.com/zhanyifan/p/11054738.html 进行深入

二: 正文

  1. 自定义协议格式

     HEADcontent-length:xxxxHEADBODYxxxxxBODY

  2  建立服务端代码

    2.1 建立Server端, (相比于helloworld的代码,仅仅修改了bootstrap.handler中的方法,添加入codec中给定的handler进行处理)

public ChannelFuture doAccept(int port, ChannelHandler ... acceptorHandlers) throws InterruptedException {
        /**
         * childHandler 使bootstrap 独有的方法,是用于提供处理对象的
         * 可以一次性添加若干个处理逻辑,是类似责任链模式处理方式。
         * 增加A,B两个处理逻辑,在处理客户端请求数据的时候,根据A->B 顺序依次处理
         * ChannelInitializer 用于提供处理器的模型对象
         *  其中定义了 initChannel方法,用于初始化处理逻辑责任链的。
         *  可以保证服务端的bootstrap只初始化一次处理器,提供处理逻辑的重用,避免反复创建处理器对象,节约资源
         */
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                socketChannel.pipeline().addLast(new StringDecoder(StandardCharsets.UTF_8));
                socketChannel.pipeline().addLast(acceptorHandlers);
            }
        });
        //bind 绑定端口,可以绑定多个端口
        //sync 开启监听,返回ChannelFuture,代表监听成功后的一个对应的未来结果
        //可以使用此对象实现后续的服务器和客户端的交互
        return bootstrap.bind(port).sync();
    }

    2.2 建立服务端处理逻辑

    

@Override
    public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
        String message = msg.toString();
        System.out.println("service receive content" + message);

        message = ProtocolParser.parse(message);
        if(null == message){
            System.out.println("message is null");
            return;
        }

        System.out.println("from client:" +message);
        String line = "from server send";

        line = ProtocolParser.transferTo(line);
        System.out.println("server send content:" + line);

        ctx.writeAndFlush(Unpooled.copiedBuffer(line.getBytes(StandardCharsets.UTF_8)));
    }

    这里使用了一个工具类进行协议的转化

class ProtocolParser{
        public static String parse(String message){
            //
            String[] temp = message.split("HEADBODY");
            temp[0] = temp[0].substring(4);
            temp[1] = temp[1].substring(0,(temp[1].length()-4));
            int length = Integer.parseInt(temp[0].substring(temp[0].indexOf(":")+1));
            if(length != temp[1].length()){
                return null;
            }else{
                return temp[1];
            }

        }

        public static String transferTo(String message){
            message = "HEADcontent-length:" + message.length() + "HEADBODY" + message + "BODY";
            return message;
        }
    }

    2.2 建立客户端代码

      主要修改代码为

public ChannelFuture doRequest(String host, int port, final ChannelHandler ... handlers) throws InterruptedException {
        /**
         * 客户端的bootstrap没有childHandler,只有handler
         *
         * 在客户端必须绑定处理器,也就是必须绑定handler方法。
         * 服务器必须绑定处理器,必须绑定childHandler方法
         */
        this.bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {

                socketChannel.pipeline().addLast(new StringDecoder(StandardCharsets.UTF_8));
                socketChannel.pipeline().addLast(handlers);
            }
        });
        //connect 连接
        return this.bootstrap.connect(host, port).sync();
    }

  

public static void main(String[] args) {

        Client4Protocol client = null;
        ChannelFuture future = null;
        try{
            client = new Client4Protocol();
            future = client.doRequest("localhost",9999,new Client4ProtocolHandler());
            Scanner s = null;
            while (true){
                s = new Scanner(System.in);

                System.out.println("enter the message to server");
                String line = s.nextLine();
                line = Client4ProtocolHandler.ProtocolParser.transferTo(line);
                System.out.println("client send content:" + line);
                future.channel().writeAndFlush(Unpooled.copiedBuffer(line.getBytes(StandardCharsets.UTF_8)));
                TimeUnit.SECONDS.sleep(1);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(null != future){
                try {
                    future.channel().closeFuture().sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if(null != client){
                client.release();
            }
        }
    }

    客户端处理器:

    

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try{
            String message = msg.toString();
            System.out.println("client receive  content:" + message);
            message = ProtocolParser.parse(message);
            if(null == message){
                System.out.println("receive error");
                return;
            }
            System.out.println("from server: " + message);
        }finally {
            ReferenceCountUtil.release(msg);
        }

    }

三:总结

  通过协议进行数据的拆包和分包,可以达到自定义的效果,常见的协议有: http等协议

  

转载于:https://www.cnblogs.com/zhanyifan/p/11130815.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值