講解一下內容:
- 什麼是pojo;
- 爲什麼使用pojo代替ByteBuf;
- 使用pojo代替ByteBuf案例;
1、什麼是POJO
POJO(Plain Ordinary Java Object)即普通Java類,具有一部分getter/setter方法的那種類就可以稱作POJO。
實際意義就是普通的JavaBeans(簡單的實體類),特點就是支持業務邏輯的協助類。
POJO類的作用是方便程序員使用數據庫中的數據表,對於程序員來說,可以很方便的將POJO類當作對象來進行使用,也可以方便的調用其get,set方法。
但不允許有業務方法,即不包含業務邏輯或持久邏輯等。
2、爲什麼使用pojo代替ByteBuf
在 ChannelHandler 使用 POIO 的好處很明顯:通過從ChannelHandler 中提取出 ByteBuf 的代碼,將會使 ChannelHandler的實現變得更加可維護和可重用
3、使用pojo代替ByteBuf案例
在 TIME 客戶端和服務器的例子中,我們讀取的僅僅是一個32位的整形數據爲本次案例講解。
首先,讓我們定義一個新的類型叫做 UnixTime
UnixTime.java
package com.moreday.netty.pojo;
import java.util.Date;
/**
* @ClassName UnixTime
* @Description TODO(這裏用一句話描述這個類的作用)
* @author 尋找手藝人
* @Date 2020年4月11日 上午9:47:22
* @version 1.0.0
*/
public class UnixTime {
private final long value;
public UnixTime() {
this(System.currentTimeMillis() / 1000L + 2208988800L);
}
/**
* @Description TODO(這裏用一句話描述這個方法的作用)
* @param l
*/
public UnixTime(long value) {
this.value = value;
}
@Override
public String toString() {
return new Date((getValue() - 2208988800L) * 1000L).toString();
}
/**
* @return the value
*/
public long getValue() {
return value;
}
}
現在我們定義 TimeDecoder 類,返回一個 UnixTime,以替代ByteBuf
package com.moreday.netty.pojo;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
* @ClassName TimeDecoder
* @Description TODO(這裏用一句話描述這個類的作用)
* @author 尋找手藝人
* @Date 2020年4月11日 上午9:53:07
* @version 1.0.0
*/
public class TimeDecoder extends ByteToMessageDecoder{
/* (非 Javadoc)
* Description:
* @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel.ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List)
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
// TODO Auto-generated method stub
if(in.readableBytes()<4) {
return;
}
// out.add(in.readBytes(4));
out.add(new UnixTime(in.readUnsignedInt()));
}
}
下面TimeClientHandler 不再任何的 ByteBuf 代碼了
package com.moreday.netty.pojo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
* @ClassName TimeClientHandler
* @Description TODO(客戶端處理器)
* @author 尋找手藝人
* @Date 2020年4月9日 下午12:52:23
* @version 1.0.0
*/
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
/* (非 Javadoc)
* Description:
* @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
// ByteBuf m = (ByteBuf) msg;
// try {
// long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
// System.out.println(new Date(currentTimeMillis));
// ctx.close();
// } finally {
// // TODO: handle finally clause
// m.release();
// }
UnixTime m = (UnixTime) msg;
System.out.println(m);
ctx.close();
}
/* (非 Javadoc)
* Description:
* @see io.netty.channel.ChannelInboundHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext, java.lang.Throwable)
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
cause.printStackTrace();
ctx.close();
}
}
是不是變得更加簡單和優雅了?相同的技術可以被運用到服務端。讓我們修改一下 TimeServerHandler 的代碼
package com.moreday.netty.pojo;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
*
* @ClassName TimeServerHandler
* @Description TODO(這裏用一句話描述這個類的作用)
* @author 尋找手藝人
* @Date 2020年4月9日 下午1:16:57
* @version 1.0.0
*/
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
/*
* (非 Javadoc) Description:
* channelActive() 方法將會在連接被建立並且準備進行通信時被調用。
* 因此讓我們在這個方法裏完成一個代表當前時間的32位整數消息的構建工作。4個字節byte,一個字節8位,共計32bit
* @see
* io.netty.channel.ChannelInboundHandlerAdapter#channelActive(io.netty.channel.
* ChannelHandlerContext)
*/
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
final ChannelFuture f = ctx.writeAndFlush(new UnixTime());
//寫操作已經完成他會通知他的監聽
f.addListener(new ChannelFutureListener() {
//這裏我們構建了一個匿名的 ChannelFutureListener 類用來在操作完成時關閉 Channel。
//或者你可以使用簡單的預定義監聽器代碼: f.addListener(ChannelFutureListener.CLOSE);
@Override
public void operationComplete(ChannelFuture future) throws Exception {
// TODO Auto-generated method stub
assert f == future;
ctx.close();
}
});
}
/*
* (非 Javadoc) Description:
*
* @see io.netty.channel.ChannelInboundHandlerAdapter#exceptionCaught(io.netty.
* channel.ChannelHandlerContext, java.lang.Throwable)
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
cause.printStackTrace();
ctx.close();
}
}
現在,唯一缺少的功能是一個編碼器,是ChannelOutboundHandler的實現,用來將 UnixTime 對象重新轉化爲一個 ByteBuf。這是比編寫一個解碼器簡單得多,因爲沒有需要處理的數據包編碼消息時拆分和組裝。
package com.moreday.netty.pojo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
/**
* @ClassName TimeEncoder
* @Description TODO(這裏用一句話描述這個類的作用)
* @author xiayutian
* @Date 2020年4月11日 上午10:15:09
* @version 1.0.0
*/
public class TimeEncoder extends ChannelOutboundHandlerAdapter {
/* (非 Javadoc)
* Description:
* @see io.netty.channel.ChannelOutboundHandlerAdapter#write(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.channel.ChannelPromise)
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// TODO Auto-generated method stub
UnixTime m = (UnixTime) msg;
ByteBuf encode = ctx.alloc().buffer(4);
encode.writeInt((int)m.getValue());
//通過 ChannelPromise,當編碼後的數據被寫到了通道上 Netty 可以通過這個對象標記是成功還是失敗。
ctx.write(encode,promise);
}
}
最後的任務就是
在TimeClientHandler之前把TimeDecoder插入到ChannelPipeline;
package com.moreday.netty.pojo;
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.nio.NioSocketChannel;
/**
* @ClassName TimeClient
* @Description TODO(時間客戶端)
* @author 尋找手藝人
* @Date 2020年4月9日 下午1:35:18
* @version 1.0.0
*/
public class TimeClient {
public void connect(int port,String host) throws Exception {
//配置客戶端NIO線程
EventLoopGroup group = new NioEventLoopGroup();
try {
//BootStrap 和 ServerBootstrap 類似,不過他是對非服務端的 channel 而言
//,比如客戶端或者無連接傳輸模式的 channel。
Bootstrap b = new Bootstrap();
//NioSocketChannel這個類在客戶端channel 被創建時使用
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ch.pipeline().addLast(new TimeDecoder(),new TimeClientHandler());
}
});
//發起異步鏈接操作,啓動客戶端
//我們用 connect() 方法代替了 bind() 方法
ChannelFuture f = b.connect(host, port).sync();
//等待客戶端鏈路關閉
f.channel().closeFuture().sync();
} finally {
//優雅退出
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args!=null && args.length>0) {
try {
port = Integer.valueOf(args[0]);
} catch (Exception e) {
// TODO: handle exception
}
}
new TimeClient().connect(port, "127.0.0.1");
}
}
在 TimeServerHandler 之前把 TimeEncoder 插入到ChannelPipeline;
package com.moreday.netty.pojo;
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;
/**
* @ClassName TimeServer
* @Description TODO(時間服務器服務端)
* @author 尋找手藝人
* @Date 2020年4月9日 下午1:36:04
* @version 1.0.0
*/
public class TimeServer {
public void bind(int port) throws Exception{
//配置服務端線程組
EventLoopGroup parentGroup = new NioEventLoopGroup();
EventLoopGroup childGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(parentGroup, childGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(new ChildChannelHandler());
//綁定端口,同步等待成功
ChannelFuture future = b.bind(port).sync();
//等待服務端監聽端口關閉
future.channel().closeFuture().sync();
} finally {
// TODO: handle finally clause
//優雅退出,釋放線程資源
parentGroup.shutdownGracefully();
childGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ch.pipeline().addLast(new TimeEncoder(),new TimeServerHandler());
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if(args!=null&&args.length>0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
// TODO: handle exception
}
}
new TimeServer().bind(port);
}
}
到此,用POJO代替ByteBuf案例以講解完成。
下篇博文我們介紹用Netty與Google protocol buffer整合案例(Protocol入門到開發),大家加油!