netty的简单例子学习记录(若有发现错误的地方希望可以留言指出互相学习哈哈)
传输编码解码参考了https://blog.csdn.net/qq_22200097/article/details/83042424 , 稳 , 稳在哪里 , 稳在可以是传入class的 , 感觉比较舒胡.哈哈
例子主要是实现server和client的通讯 , 传输pojo对象
涉及的类先说下
NormalServer | netty服务端 |
NormalServerHandler extends ChannelInboundHandlerAdapter | |
NormalClient | netty客户端 |
NormalClientHandler extends ChannelInboundHandlerAdapter | |
RpcDecoder extends ByteToMessageDecoder | 解码(为何是类名是Rpc,因为我主要是想用netty实现rpc异步调用哈哈) |
RpcEncoder extends MessageToByteEncoder | 编码 |
RpcDecoder
import java.util.List;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;public class RpcDecoder extends ByteToMessageDecoder {
//目标对象类型进行解码
private Class<?> target;
public RpcDecoder(Class target) {
this.target = target;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 4) { //不够长度丢弃
return;
}
in.markReaderIndex(); //标记一下当前的readIndex的位置
int dataLength = in.readInt(); // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4
if (in.readableBytes() < dataLength) { //读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
in.resetReaderIndex();
return;
}
byte[] data = new byte[dataLength];
in.readBytes(data);
Object obj = JSON.parseObject(data, target); //将byte数据转化为我们需要的对象
out.add(obj);
}
}
RpcEncoder
import java.util.List;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;public class RpcDecoder extends ByteToMessageDecoder {
//目标对象类型进行解码
private Class<?> target;
public RpcDecoder(Class target) {
this.target = target;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 4) { //不够长度丢弃
return;
}
in.markReaderIndex(); //标记一下当前的readIndex的位置
int dataLength = in.readInt(); // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4
if (in.readableBytes() < dataLength) { //读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
in.resetReaderIndex();
return;
}
byte[] data = new byte[dataLength];
in.readBytes(data);
Object obj = JSON.parseObject(data, target); //将byte数据转化为我们需要的对象
out.add(obj);
}
}
NormalServerHandler
channelRead方法是接收到客户端发过来的消息 , 我直接转成User是因为在客户端编码User后发过来 , 并且在服务端解码User转成User
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import xuyingwx.netty.example.pojo.ResponseData;
import xuyingwx.netty.example.pojo.User;public class NormalServerHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
String userStr = ((User)msg).toString();
System.out.println("server收到:"+userStr);
ResponseData res = new ResponseData();
res.setSuccess(true);
res.setData(userStr);
ctx.writeAndFlush(res);
}@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
NormalServer
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.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import xuyingwx.netty.example.normal.decoder.RpcDecoder;
import xuyingwx.netty.example.normal.encoder.RpcEncoder;
import xuyingwx.netty.example.pojo.ResponseData;
import xuyingwx.netty.example.pojo.User;public class NormalServer {
public static void main(String[] args) {
int port = 8080;
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
// //如果不使用ObjectDecoder时https://blog.csdn.net/qq_22200097/article/details/83042424
// //添加对象解码器 负责对序列化POJO对象进行解码 设置对象序列化最大长度为1M 防止内存溢出
// //设置线程安全的WeakReferenceMap对类加载器进行缓存 支持多线程并发访问 防止内存溢出
// new ObjectDecoder(
// 1024*1024,
// ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())
// ),
// new ObjectEncoder(),
new RpcDecoder(User.class), // 解码客户端发送来的User
new RpcEncoder(ResponseData.class), // 编码返回对象再发送会客户端
new NormalServerHandler()
);
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7)
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
}catch (Exception e) {
}finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
NormalClientHandler
channelActive连上server后会进入这个方法 , 这里是连上server后会直接发送一个User给服务端
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import xuyingwx.netty.example.pojo.ResponseData;
import xuyingwx.netty.example.pojo.User;public class NormalClientHandler extends ChannelInboundHandlerAdapter{
private ChannelHandlerContext ctx;
@Override
public void channelActive(final ChannelHandlerContext ctx) { // (1)
System.out.println("进入channelActive");
User user = new User();
user.setId("1");
user.setName("nameasd");
ChannelFuture f = ctx.writeAndFlush(user);
System.out.println("准备编码");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
ResponseData responseData = (ResponseData)msg;
System.out.println("client收到:"+responseData);
ctx.close();
}@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
public void send() {
User user = new User();
user.setId("1");
user.setName("nameasd");
ChannelFuture f = ctx.writeAndFlush(user);
}
}
NormalClient
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import xuyingwx.netty.example.normal.decoder.RpcDecoder;
import xuyingwx.netty.example.normal.encoder.RpcEncoder;
import xuyingwx.netty.example.pojo.ResponseData;
import xuyingwx.netty.example.pojo.User;public class NormalClient {
public static void main(String[] args) throws Exception {
String host = "localhost";
int port = 8080;
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
// //添加对象解码器 负责对序列化POJO对象进行解码 设置对象序列化最大长度为1M 防止内存溢出
// //设置线程安全的WeakReferenceMap对类加载器进行缓存 支持多线程并发访问 防止内存溢出
// new ObjectDecoder(
// 1024*1024,
// ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())
// ),
// new ObjectEncoder(),
new RpcDecoder(ResponseData.class),
new RpcEncoder(User.class),
new NormalClientHandler()
);
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync(); // (5)
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
System.out.println("clientmain");
}