Netty 自定义协议实战
依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.50.Final</version>
</dependency>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.63</version>
</dependency>
协议
/**
* 常量
*/
public interface RpcProtocolConst {
/**
* 协议开始标识
*/
int HEAD_START = 0x177;
}
@Getter
@Setter
public class RpcProtocol implements Serializable {
private static final long serialVersionUID = 42L;
/**
* 协议开始标记
*/
public final int start = RpcProtocolConst.HEAD_START;
/**
* header 长度
*/
private int headerLength;
/**
* header
*/
private RpcProtocolHeader header;
/**
* body 长度
*/
private int bodyLength;
/**
* body
*/
private byte[] body;
}
/**
* 协议头
*/
@Getter
@Setter
public class RpcProtocolHeader implements Serializable {
private static final long serialVersionUID = 42L;
/**
* 请求id
*/
private String id;
/**
* 版本号
*/
private String version;
}
序列化工具类
public class HessianUtils {
public static <T> T read(byte[] data, Class<T> clazz) {
ByteArrayInputStream os = new ByteArrayInputStream(data);
Hessian2Input input = new Hessian2Input(os);
try {
return (T) input.readObject(clazz);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public static byte[] write(Object obj) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(os);
try {
output.writeObject(obj);
output.close();
return os.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return new byte[0];
}
}
编码器
public class RpcProtocolToByteEncoder extends MessageToByteEncoder<RpcProtocol> {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, RpcProtocol rpcRequest, ByteBuf byteBuf) throws Exception {
RpcProtocolHeader header = rpcRequest.getHeader();
// 将 header 转 byte 数组
byte[] headerData = HessianUtils.write(header);
byte[] body = rpcRequest.getBody();
// 写入开始标志
byteBuf.writeInt(RpcProtocolConst.HEAD_START);
// 写入头信息
byteBuf.writeInt(headerData.length);
byteBuf.writeBytes(headerData);
// 写入 body 信息
if (body == null) {
byteBuf.writeInt(0);
} else {
byteBuf.writeInt(body.length);
byteBuf.writeBytes(body);
}
}
}
解码器
public class ByteToRpcProtocolDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
try {
int begin;
// 在循环中找到协议开始的位置
while (true) {
// 本次协议包开始的位置
begin = byteBuf.readerIndex();
// 标记本次协议包开始的位置
byteBuf.markReaderIndex();
if (byteBuf.readInt() == RpcProtocolConst.HEAD_START) {
break;
}
// 没有读到 HEAD_START,那么就读取下一个字节
byteBuf.resetReaderIndex();
byteBuf.readByte();
}
// 协议包头长度
int headerLength = byteBuf.readInt();
// 协议包头数据还未到齐,回到协议开始的位置,等待数据到齐
if (byteBuf.readableBytes() < headerLength) {
byteBuf.readerIndex(begin);
return;
}
// 读取协议包头数据
byte[] header = new byte[headerLength];
byteBuf.readBytes(header);
// 协议内容长度
int bodyLength = byteBuf.readInt();
// 协议包内容数据还未到齐,回到协议开始的位置,等待数据到齐
if (byteBuf.readableBytes() < bodyLength) {
byteBuf.readerIndex(begin);
return;
}
// 读取协议包内容数据
byte[] body = new byte[bodyLength];
byteBuf.readBytes(body);
// 封装协议
RpcProtocol rpcProtocol = new RpcProtocol();
rpcProtocol.setHeaderLength(headerLength);
// 将 byte 数组转成 header
RpcProtocolHeader rpcProtocolHeader = HessianUtils.read(header, RpcProtocolHeader.class);
rpcProtocol.setHeader(rpcProtocolHeader);
rpcProtocol.setBodyLength(bodyLength);
rpcProtocol.setBody(body);
out.add(rpcProtocol);
} catch (Exception e) {
e.printStackTrace();
}
}
}
协议处理器
public class MessageHandler extends SimpleChannelInboundHandler<RpcProtocol> {
private final String endpoint;
public MessageHandler(String endpoint) {
this.endpoint = endpoint;
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, RpcProtocol msg) throws Exception {
System.out.printf("%s收到消息:----------\n", endpoint);
System.out.printf("头信息:%s\n", msg.getHeader());
System.out.printf("内容:%s\n", new String(msg.getBody()));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}
验证
服务端
public class ServerExample {
public static void main(String[] args) {
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();
serverBootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ByteToRpcProtocolDecoder());
ch.pipeline().addLast(new RpcProtocolToByteEncoder());
ch.pipeline().addLast(new MessageHandler("服务端"));
}
});
try {
ChannelFuture channelFuture = serverBootstrap.bind(9999).sync();
System.out.println("服务端启动,端口9999");
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
客户端
public class ClientExample {
public static void main(String[] args) {
Bootstrap serverBootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup();
serverBootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ByteToRpcProtocolDecoder());
ch.pipeline().addLast(new RpcProtocolToByteEncoder());
ch.pipeline().addLast(new MessageHandler("客户端"));
}
});
try {
ChannelFuture channelFuture = serverBootstrap.connect("localhost", 9999).sync();
Channel channel = channelFuture.channel();
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
int finalI = i;
executorService.execute(() -> {
RpcProtocol rpcProtocol = new RpcProtocol();
rpcProtocol.setBody(String.format("hello server %d", finalI).getBytes());
RpcProtocolHeader header = new RpcProtocolHeader();
header.setId(UUID.randomUUID().toString());
header.setVersion("V1.0");
rpcProtocol.setHeader(header);
channel.writeAndFlush(rpcProtocol);
});
}
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
先启动服务端,然后启动客户端。可以在服务端打印的消息。
…………省略部分消息…………
服务端收到消息:----------
头信息:RpcProtocolHeader{id='76d41fd9-01df-4fc9-afde-4153251076e1', version='V1.0'}
内容:hello server 90
服务端收到消息:----------
头信息:RpcProtocolHeader{id='bcc4c242-9ec0-4ff5-a601-c28eeef23115', version='V1.0'}
内容:hello server 89
服务端收到消息:----------
头信息:RpcProtocolHeader{id='8643a5c3-388c-4ee9-8add-50914b4dca07', version='V1.0'}
内容:hello server 87
服务端收到消息:----------
头信息:RpcProtocolHeader{id='7b216f49-1c76-4073-822b-2af308b790b4', version='V1.0'}
内容:hello server 98
服务端收到消息:----------
头信息:RpcProtocolHeader{id='5a127ec6-4d77-46c7-9d29-332b429926a7', version='V1.0'}
内容:hello server 99
服务端收到消息:----------
头信息:RpcProtocolHeader{id='7fee4bd1-ea55-450c-b181-8afb9abc1c06', version='V1.0'}
内容:hello server 97