Netty的強大之處在於它的高度抽象和封裝,對於使用者來說不必過多關心內部實現。當需要有新的需求時,只需簡單的添加或者修改相關的Handler類即可。
本章將使用Netty 實現TCP協議,以下爲具體實現。
1、服務端實現
TcpServer.java
package emulator.netty5;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
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;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
public class TcpServer {
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 ChildChannelHandler());
//綁定端口,同步等待成功
ChannelFuture f = b.bind(port).sync();
//等待服務端監聽端口關閉
f.channel().closeFuture().sync();
}finally{
//退出時釋放資源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new TcpServerHandler());
}
}
public static void main(String[] args) throws Exception{
int port = 8083;
new TcpServer().bind(port);
}
}
TcpServerHandler.java
package emulator.netty5;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import emulator.Constants;
import emulator.util.Dom4JUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.CharsetUtil;
public class TcpServerHandler extends ChannelHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
String body = (String)msg;
System.out.println("request content:\n"+ body);
//響應
resp(ctx, body);
}
/**
*
* @param xml
*/
private void resp(ChannelHandlerContext ctx, String xml){
String transCode = Dom4JUtil.header(xml, "TransCode");
String retUrl = "D:\\workspaces\\eclipse-huifu\\emulator\\xml\\error.xml";
String retCtt = null;
System.out.println("交易代碼:"+transCode);
if(equal(transCode, Constants.TC_DZZH)){//電子賬戶
retUrl = "D:\\workspaces\\eclipse-huifu\\emulator\\xml\\account\\manage\\resp.xml";
}
try {
retCtt = FileUtils.readFileToString(new File(retUrl));
} catch (IOException e) {
e.printStackTrace();
}
ByteBuf resp = Unpooled.copiedBuffer(retCtt.getBytes());
ctx.writeAndFlush(resp).addListener(ChannelFutureListener.CLOSE);
}
public static boolean equal(String var, String cons){
return isNotEmpty(var) && cons.equals(var);
}
private static boolean isNotEmpty(String s){
return (null != s && !"".equals(s));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
//ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
//super.exceptionCaught(ctx, cause);
ctx.close();
}
}
2、客戶端實現
TcpClient.java
package emulator.netty5;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
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;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
public class TcpClient {
public void connect(int port,String host)throws Exception{
//配置客戶端NIO線程組
EventLoopGroup group = new NioEventLoopGroup();
try{
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast("handler", new TcpClientHandler());
};
});
//發起異步連接操作
ChannelFuture f = b.connect(host,port).sync();
//等待客戶端鏈路關閉
f.channel().closeFuture().sync();
}finally{
//退出,釋放資源
group.shutdownGracefully();
}
}
public static void main(String[] args)throws Exception {
int port = 8083;
new TcpClient().connect(port, "127.0.0.1");
}
}
TcpClientHandler.java
package emulator.netty5;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.CharsetUtil;
/**
*
* @author lh
*
*/
public class TcpClientHandler extends ChannelHandlerAdapter {
private static final Logger logger = Logger.getLogger(TcpClientHandler.class.getName());
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String retUrl = "D:\\workspaces\\eclipse-huifu\\emulator\\xml\\account\\manage\\req.xml";
String ret = null;
try {
ret = FileUtils.readFileToString(new File(retUrl));
} catch (IOException e) {
e.printStackTrace();
}
ctx.writeAndFlush(ret);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
String body = (String)msg;
System.out.println("server response :\n"+body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
logger.warning("unexpected exception from downstream:"+ cause.getMessage());
ctx.close();
}
}
OVER!