使用netty整合protobuf进行高效数据传输,包括粘包、半包问题解决。
Protobuf是Google开发的一种平台无关、语言无关、可扩展且轻便高效的序列化数据结构的协议,可以用于网络通信和数据存储。使用protobuf编译器能自动生成代码,但需要编写proto文件。
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);
}
}
}
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);
}
}
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();
}
}
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);
}
}
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