前言:
最近因为新项目的需要,需要使用netty这种框架来实现通信,现在将学习的部分心得编写出来。
以下代码均是在Netty4的基础上做的测试,在Netty3上无法正常运行(这也是netty做的不好的地方,这种在代码迁移的时候会非常的痛苦)
下面几个类主要是对底层Netty做的封装,便于新手使用的.
先不说那么多废话了,直接codes ,看起来比较容易懂得
一:传输对象BaseObjBean对象
/**
*
* 基类,在传输的类上,都需要继承之该类 .
*
* @author rocky
*
*/
public class BaseObjBean implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 根据自己的实现业务,选择更合适自己的基类业务去实现 ...
*/
}
二:业务处理BusinessHandler 对象
/**
*
* 业务类的编写.
*
* @author rocky
*
*/
public class BusinessHandler extends ChannelInboundHandlerAdapter {
private BaseObjBean bean;
public BusinessHandler(BaseObjBean bean){
this.bean = bean;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
bean = (BaseObjBean) msg;
msg = "BusinessHandler read msg from client :" + bean;
System.out.println(msg);
msg = "服务端返回的消息是:OK !";
ctx.write(msg);
ctx.channel().write(msg);
ctx.channel().writeAndFlush(msg);
ctx.writeAndFlush(msg);
}
/**
* 消息读取
*
* @param ctx
*
* @param bean
* @throws Exception
*/
protected void messageReceived(ChannelHandlerContext ctx, BaseObjBean bean) throws Exception {
ctx.channel().writeAndFlush("server 读取client发送来的信息成功了...,className = "+bean.getClass().getName());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
三:传输对象ClientInitHandler
/**
*
* 初始化的handler(传输对象)
*
* @author rocky
*
*/
public class ClientInitHandler extends ChannelInboundHandlerAdapter {
private BaseObjBean bean;
public ClientInitHandler(BaseObjBean bean) {
this.bean = bean;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.write(bean); //该对象已经给序列化了
ctx.flush();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ctx.writeAndFlush("client接收到服务器返回的消息:"+msg);
System.out.println("client接收到服务器返回的消息:"+msg);
}
}
四:基础NettyBaseClient对象
目的:主要是来连接服务端,传输数据的
/**
*
* 客户端的编写.
*
* @author rocky
*
*/
public class NettyBaseClient {
private BaseObjBean bean;
public NettyBaseClient(BaseObjBean bean){
this.bean = bean;
}
/**
* 连接的构建.
*
* @param host
* @param port
* @throws Exception
*/
public void connect(String host, int port) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//编码字节设置
ch.pipeline().addLast(new ObjEncoder());
//
ch.pipeline().addLast(new ClientInitHandler(bean));
}
});
ChannelFuture f = b.connect(host, port).sync();
if (f.isSuccess()) {
System.out.println("客户端创建好了...");
}
SocketChannel channel = (SocketChannel) f.channel();
System.out.println("服务端返回的数据是:"+channel.metadata().toString());
//关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
/**
*
* 连接的构建.
*
*@param host
* @param port
* @param handler:自己实现的handler.
* @throws Exception
*/
public void connect(String host, int port,final ChannelHandler handler ) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//编码字节设置
ch.pipeline().addLast(new ObjEncoder());
//
ch.pipeline().addLast(handler);
}
});
ChannelFuture f = b.connect(host, port).sync();
if (f.isSuccess()) {
SocketChannel channel = (SocketChannel) f.channel();
System.out.println("客户端创建好了...");
}
//关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
五:服务基类 NettyBaseServer 对象
目的:主要是用来连接客户端服务
/**
* 服务端的编写
*
* @author rocky
*
*/
public class NettyBaseServer {
private BaseObjBean bean;
public NettyBaseServer(BaseObjBean bean){
this.bean = bean;
}
/**
* 开始方法.
*
* @param port
* @throws Exception
*/
public void start(int port) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//解码处理操作.
ch.pipeline().addLast(new ObjDecoder());
//业务处理的主要实现类.
ch.pipeline().addLast(new BusinessHandler(bean));
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,得到对象
ChannelFuture f = server.bind(port).sync();
if (f.isSuccess()) {
System.out.println(" NettyBaseServer 服务创建完成了...");
}
//关闭连接.
f.channel().closeFuture().sync();
} finally {
//关闭集合
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
/**
* 开始方法.
*
* @param port
* @throws Exception
*/
public void start(int port,final ChannelHandler handler) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap server = new ServerBootstrap();
server.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
//解码处理操作.
ch.pipeline().addLast(new ObjDecoder());
//业务处理的主要实现类.
ch.pipeline().addLast(handler);
}
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,得到对象
ChannelFuture f = server.bind(port).sync();
if (f.isSuccess()) {
//System.out.println("服务创建完成了...");
}
f.channel().writeAndFlush(" NettyBaseServer 服务创建完成了...");
//关闭连接.
f.channel().closeFuture().sync();
} finally {
//关闭集合
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
六:对象转换ObjConverter对象
目的:负责将object对象转换为字节;或者将字节转化为object对象的
/**
*
* 字节和obj对象的转变.
*
* @author rocky
*
*/
public class ObjConverter {
/**
* 字节到obj
*
* @param bytes
* @return
*/
public static Object byteToObject(byte[] bytes) {
Object obj = null;
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
ObjectInputStream oi = null;
try {
oi = new ObjectInputStream(bi);
obj = oi.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bi.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
oi.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return obj;
}
/**
*
* obj到字节的转换.
*
* @param obj
* @return
*/
public static byte[] objectToByte(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = null;
try {
oo = new ObjectOutputStream(bo);
oo.writeObject(obj);
bytes = bo.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bo.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
oo.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
*
* 读取netty中的ByteBuf对象
*
* @param datas
* @return
*/
public static byte[] read(ByteBuf datas) {
byte[] bytes = new byte[datas.readableBytes()];
datas.readBytes(bytes);
return bytes;
}
}
七:对象化
目的:将对象进行转化
/**
*
* 将字节转换为对象
*
* @author rocky
*
*/
public class ObjDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
Object obj = ObjConverter.byteToObject(ObjConverter.read(in));
out.add(obj);
}
}
/**
*
* 将object对象转换成字节传输.
*
* @author rocky
*
*/
public class ObjEncoder extends MessageToByteEncoder<Object> {
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
byte[] datas = ObjConverter.objectToByte(msg);
out.writeBytes(datas);
ctx.flush();
}
}
以上对底层的包装就已经完成,后续会介绍如何使用。
其实这里面涉及的知识还是比较多的,而且他们之间的联系也比较的密切,本质上:
还是socket的通信,只不过是做了很多的封装的工作.... 不难,不难