首先maven需要增加依赖
io.netty
netty-all
4.1.30.Final
org.msgpack
msgpack
0.6.12
实现编码器类 MsgpackEncoder,注意这里是需要继承MessageToByteEncoder类,重写它的encode方法, 这的Object表示对Object类型进行编码,这里实际直接拿来用就行了
package io.netty.handler.codec.msgpack;
import org.msgpack.MessagePack;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class MsgpackEncoder extends MessageToByteEncoder
实现解码器类 MsgpackDecoder
package io.netty.handler.codec.msgpack;
import java.util.List;
import org.msgpack.MessagePack;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
public class MsgpackDecoder extends MessageToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg,
List
编写客户端的EchoClientHandler
package io.netty.handler.codec.msgpack;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
// sendNumber为写入发送缓冲区的对象数量
private int sendNumber;
public EchoClientHandler(int sendNumber) {
this.sendNumber = sendNumber;
}
/**
* 构建长度为UserInfoNum的UserInfo对象数组
* @param UserInfoNum
* @return
*/
private UserInfo[] getUserInfoArray(int UserInfoNum) {
UserInfo[] userInfos = new UserInfo[UserInfoNum];
UserInfo userInfo = null;
for(int i = 0; i < UserInfoNum; i++) {
userInfo = new UserInfo();
userInfo.setName("ABCDEFG --->" + i);
userInfo.setAge(i);
userInfos[i] = userInfo;
}
return userInfos;
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
UserInfo[] userInfos = getUserInfoArray(sendNumber);
for (UserInfo UserInfo : userInfos) {
ctx.write(UserInfo);
}
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Client receive the msgpack message : " + msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
编写客户端的EchoClient
package io.netty.handler.codec.msgpack;
import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class EchoClient {
public void connect(String host, int port, final int sendNumber) throws Exception {
// 配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
// 设置TCP连接超时时间
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("frameDecoder", new
LengthFieldBasedFrameDecoder(65525, 0, 2, 0, 2));
// 添加MesspagePack解码器
ch.pipeline().addLast("msgpack decoder", new MsgpackDecoder());
ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(2));
// 添加MessagePack编码器
ch.pipeline().addLast("msgpack encoder", new MsgpackEncoder());
// 添加业务处理handler
ch.pipeline().addLast(new EchoClientHandler(sendNumber));
}
});
// 发起异步连接操作
ChannelFuture f = b.connect(host, port).sync();
// 等待客户端链路关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args != null && args.length > 0) {
try {
port = Integer.valueOf(port);
} catch (NumberFormatException e) {
// 采用默认值
}
}
int sendNumber = 10;
new EchoClient().connect("localhost", port, sendNumber);
}
}
编写服务端EchoServerHandler
package io.netty.handler.codec.msgpack;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("Server receive the msgpack message : " + msg);
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 发生异常,关闭链路
ctx.close();
}
}
编写服务端的EchoServer
package io.netty.handler.codec.msgpack;
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.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
public class EchoServer {
public void bind(int port) throws Exception {
// 配置服务端的NIO线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("frameDecoder", new
LengthFieldBasedFrameDecoder(65525, 0, 2, 0, 2));
// 添加MesspagePack解码器
ch.pipeline().addLast("msgpack decoder", new MsgpackDecoder());
ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(2));
// 添加MessagePack编码器
ch.pipeline().addLast("msgpack encoder", new MsgpackEncoder());
// 添加业务处理handler
ch.pipeline().addLast(new EchoServerHandler());
}
});
// 绑定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
// 等待服务端监听端口关闭
f.channel().closeFuture().sync();
} finally {
// 优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args != null && args.length > 0) {
try {
port = Integer.valueOf(port);
} catch (NumberFormatException e) {
// TODO: handle exception
}
}
new EchoServer().bind(port);
}
}
编写pojoUserInfo
package io.netty.handler.codec.msgpack;
import org.msgpack.annotation.Message;
@Message
public class UserInfo {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
}
以上代码可以直接用来参考运行, 在client和server里面增加的编解码器顺序不要变
ch.pipeline().addLast("frameDecoder", new
LengthFieldBasedFrameDecoder(65525, 0, 2, 0, 2));
// 添加MesspagePack解码器
ch.pipeline().addLast("msgpack decoder", new MsgpackDecoder());
//
ch.pipeline().addLast("frameEncoder", new LengthFieldPrepender(2));
// 添加MessagePack编码器
ch.pipeline().addLast("msgpack encoder", new MsgpackEncoder());
// 添加业务处理handler
ch.pipeline().addLast(new EchoServerHandler());
上面一段表示在解码器之前增加一个LengthFieldBasedFrameDecoder表示用来处理半包消息,会在消息前增加两个字节,后续解析的时候根据消息头来的字节信息来解析,保证每次拿到的数据都是整包消息, 可以认为跟DelimiterBasedFrameDecoder类似的感觉,本人是这么认为的通过特定的消息字节来保证报文完整性
在编码器之前增加LengthFieldPrepender表示在字节缓冲区的报文之前增加两个字节长度