在上一篇文章对Netty做了一个简要的介绍,并通过Netty实现了server-client端的字符串传输。在实际开发中不可避免的会遇到传输对象的情况,本篇通过一个简单的例子展示如何通过Netty来传输对象。
1.首先我们定义一个User对象:
package com.ricky.codelab.netty.model;
import java.io.Serializable;
public class User implements Serializable {
/** * */
private static final long serialVersionUID = 1L;
private long id;
private String name;
private int age;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
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 [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
注意:User类一定要实现 java.io.Serializable 接口,表示我们使用JDK内置的序列化机制来对User进行序列化和反序列化。
POJOTranferServer.java
package com.ricky.codelab.netty.ch2;
import com.ricky.codelab.netty.util.Constant;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
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;
public class POJOTranferServer {
private final int port;
public POJOTranferServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder(),
new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)),
new POJOTransferServerHandler());
}
});
// Bind and start to accept incoming connections.
b.bind(port).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new POJOTranferServer(Constant.PORT).run();
}
}
Netty4内置了ObjectEncoder和ObjectDecoder类来帮助我们将Object转换为byte数组。
package com.ricky.codelab.netty.ch2;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class POJOTransferServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("server receive:"+msg);
ctx.writeAndFlush(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
POJOTransferClient.java
package com.ricky.codelab.netty.ch2;
import java.util.ArrayList;
import java.util.List;
import com.ricky.codelab.netty.model.User;
import com.ricky.codelab.netty.util.Constant;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
public class POJOTransferClient {
private String host;
private int port;
private List<User> message;
public POJOTransferClient(String host, int port, List<User> message) {
this.host = host;
this.port = port;
this.message = message;
}
public void send() throws InterruptedException {
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder(),
new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)),
new POJOTransferClientHandler(message));
}
});
ChannelFuture future = bootstrap.connect(host, port).sync();
future.channel().closeFuture().sync();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
final List<User> message = new ArrayList<>();
for(int i=0;i<5;i++){
User user = new User();
user.setId(i+1);
user.setName("Ricky P"+i);
user.setAge(20+i);
message.add(user);
}
new POJOTransferClient(Constant.HOST, Constant.PORT, message).send();
}
}
package com.ricky.codelab.netty.ch2;
import java.util.List;
import com.ricky.codelab.netty.model.User;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class POJOTransferClientHandler extends ChannelInboundHandlerAdapter {
private final List<User> message;
/** * Creates a client-side handler. */
public POJOTransferClientHandler(List<User> message) {
this.message = message;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// Send the message to Server
super.channelActive(ctx);
System.out.println("client send message");
ctx.writeAndFlush(message);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
// you can use the Object from Server here
System.out.println("client receive:"+msg);
ctx.close();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
本例中从客户端发送消息到服务端 属于单向发消息,所以需要在客户端配置编码,服务端解码。如果双向收发,则需要客户端&服务器 都需要配置Encoder和Decoder。
另外, 需要注意的是 注册到Server ChannelPipeline中的handler是有顺序之分的,如果颠倒一下注册顺序导致的结果就是:先进入我们自己的handler,再进行解码,这自然是不行的,会强转失败。
最后,本例中使用Java内置的序列化机制对对象进行序列化和反序列化后再进行传输,当然,我们也可以使用其他序列化机制,例如:kryo、hessian、protobuf等。
点此下载源代码
参考文档:user-guide-for-4.x