Netty学习2(netty整合protobuf进行数据传输)

1.目标:

使用netty整合protobuf进行高效数据传输,包括粘包、半包问题解决。

2.为什么使用protobuf

Protobuf是Google开发的一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储。使用protobuf编译器能自动生成代码,但需要编写proto文件。Netty学习2(netty整合protobuf进行数据传输)_第1张图片

3.生成protobuf消息:

项目结构如下:
Netty学习2(netty整合protobuf进行数据传输)_第2张图片

1)首先要下载protobuf工具,下载完毕,解压到项目的protoTool包
下载地址https://github.com/protocolbuffers/protobuf/releases

2)导入pom

        
        
            com.google.protobuf
            protobuf-java
            3.5.1
        

3)编写MessageProto.proto

syntax = "proto3"; //proto协议版本
 option java_package = "Netty.protobuf.protoClass";//这里是java类包名
 option java_outer_classname = "Message";//java类名

 message MessageProto  {  //这里的消息名不能跟java_outer_classname相同
   int32 id = 1;
   string content = 2;
 }

4)ProtobufGenarator

package Netty.protobuf;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * protoc生成java文件
 */
public class ProtobufGenarator {
    public static void main(String[] args) throws IOException {
        String protoc = System.getProperty("user.dir") + "\\src\\test\\java\\Netty\\protobuf\\protoTool\\bin\\protoc.exe";//protoc.exe目录
        String protoPath = System.getProperty("user.dir") + "\\src\\test\\java\\Netty\\protobuf\\protofile";//proto源文件目录
        String javaPath = System.getProperty("user.dir") + "\\src\\test\\java";//生成的java文件存放目录
        List protoFileList = new ArrayList();
        File f = new File(protoPath);
        File fa[] = f.listFiles();
        for (File fs : fa) {
            if (fs.isFile()) {
                protoFileList.add(fs.getName());
            }
        }
        for (String protoFile : protoFileList) {
            String strCmd = protoc + " -I=" + protoPath + " --java_out=" + javaPath + " " + protoPath + "\\" + protoFile;
            Runtime.getRuntime().exec(strCmd);
            System.out.println("proc生成proto文件:" + protoFile);
        }
    }
}

到这里,直接执行ProtobufGenarator类,即可生成对应 消息结构 的java类。

4.netty实现(直接上代码)

  • NettyServer
package Netty.protobuf;

import Netty.protobuf.protoClass.Message;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyServer {

    public void start(int port){
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);  //boss线程组,接收传入的连接,并将连接分配给worker线程组
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup)
                     .channel(NioServerSocketChannel.class)      //在这里,我们指定使用NioServerSocketChannel类来实例化一个新的Channel(通道)来接受传入的连接。
                     .childHandler(new ChannelInitializer() {  //设置服务器处理程序
                        protected void initChannel(SocketChannel ch) throws Exception {
                             ch.pipeline().addLast(new ProtobufVarint32FrameDecoder()); //解决粘包半包编码器
                            ch.pipeline().addLast("decoder", new ProtobufDecoder(Message.MessageProto.getDefaultInstance()));//protobuf编码器
                            ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());//解决粘包半包编码器
                            ch.pipeline().addLast("encoder", new ProtobufEncoder());//protobuf解码器
                            ch.pipeline().addLast(new NettyServerHandler()); //自定义处理器
                        };
                     })
                     .option(ChannelOption.SO_BACKLOG, 128)
                     .childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture future = bootstrap.bind(port).sync();    //启动服务器
            System.out.println("netty Server port : " + port );
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

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

        new NettyServer().start(8081);
    }
}
  • NettyServerHandler
package Netty.protobuf;

import Netty.protobuf.protoClass.Message;
import com.alibaba.fastjson.JSON;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;

public class NettyServerHandler extends SimpleChannelInboundHandler {

   @Override
   public void channelActive(ChannelHandlerContext ctx) throws Exception {
       System.out.println("Netty server 激活...");
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message.MessageProto msg) throws Exception {
        System.out.println("Netty server 接收消息:"+ msg.toString());
        msg = msg.toBuilder().setContent("hello netty client").build();
        ctx.write(msg);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}
  • NettyClient
package Netty.protobuf;

import Netty.protobuf.protoClass.Message;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class NettyClient {


    public void run(String host,int port) {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline p = ch.pipeline();
                            ch.pipeline().addLast(new ProtobufVarint32FrameDecoder()); //解决粘包半包编码器
                            ch.pipeline().addLast("decoder", new ProtobufDecoder(Message.MessageProto.getDefaultInstance()));//protobuf编码器
                            ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());//解决粘包半包编码器
                            ch.pipeline().addLast("encoder", new ProtobufEncoder());//protobuf解码器
                            p.addLast(new NettyClientHandler());//自定义处理器
                        }
                    });

            ChannelFuture future = null;
            try {
                future = b.connect(host, port).sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                future.channel().closeFuture().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new NettyClient().run("127.0.0.1",8081);

    }

}
  • NettyClientHandler
package Netty.protobuf;

import Netty.protobuf.protoClass.Message;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;

public class NettyClientHandler extends SimpleChannelInboundHandler {


    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("netty client 启动...");
        Message.MessageProto messageProto = Message.MessageProto.newBuilder().setId(1).setContent("hello netty server").build();
        ctx.channel().writeAndFlush(messageProto);
    }


    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Message.MessageProto msg) throws Exception {
        System.out.println("netty 服务器响应信息:" +  msg.toString());
    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }

}

项目源码:https://gitee.com/warrior666/demo


你可能感兴趣的:(Netty学习)