本节只介绍如何传输String类型的简单的Client-Server模式的Netty代码 和几种处理TCP粘包/拆包的方式
1、NettyServer主逻辑
package com.back.server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class SimpleNettyServer {
public static void main(String[] args) {
int port = 8888;
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024).childHandler(new RouteHandler());
ChannelFuture future = serverBootstrap.bind(port).sync();
System.out.println("server start!");
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
}finally{
System.out.println("释放线程池资源!");
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2、上面看到的RouteHandler代码
package com.back.server;
import com.back.constant.NettyConstant;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
* 设置请求 要经过哪些处理的handler
* 公共的handler逻辑等 可以在此处添加 如:记录日志、消息编解码等
* LineBasedFrameDecoder+StringDecoder 来解决TCP粘包拆包问题原理
* 前者识别换行符,同时设置单行最大字节 所以这两个组合就是按行切换的文本解码器
* DelimiterBasedFrameDecoder+StringDecoder 则是通过制定分隔符(同时设置最大字节)来区分 每次消息末尾都要加指定分隔符
* FixedLengthFrameDecoder+StringDecoder 则代表不管怎样 每次读取指定长度 字节的包 不在演示
* @author back
*
*/
public class RouteHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
//arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));
ByteBuf delimiter = Unpooled.copiedBuffer(NettyConstant.DELIMITER.getBytes());
arg0.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
arg0.pipeline().addLast(new StringDecoder());
arg0.pipeline().addLast(new MessageHandler());
}
}
3、业务处理的逻辑类MessageHandler
package com.back.server;
import com.back.constant.NettyConstant;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* 真实处理信息的handler
* @author Administrator
*
*/
public class MessageHandler extends ChannelHandlerAdapter {
private RequestHandler handler = RequestHandler.getRequestHandler();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String message = (String)msg;
System.out.println("请求报文 : " + message);
//String dilimiter = System.getProperty("line.separator");
String resp = handler.simpleHandlerRequest(message)+NettyConstant.DELIMITER;
ByteBuf copiedBuffer = Unpooled.copiedBuffer(resp.getBytes());
ctx.writeAndFlush(copiedBuffer);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
4、自定义的业务逻辑类
package com.back.server;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 随便写了个处理请求 和返回值得逻辑
* @author back
*
*/
public class RequestHandler {
private RequestHandler(){};
private static volatile RequestHandler handler;
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
public static RequestHandler getRequestHandler(){
if(handler == null){
synchronized (RequestHandler.class) {
if(null == handler){
handler = new RequestHandler();
}
}
}
return handler;
}
public String simpleHandlerRequest(String request){
String response = null;
switch (request) {
case "time":
response = sdf.format(new Date());
break;
default:
break;
}
return response == null ? "badReq" :response;
}
}
5、NettyClient
package com.back.client;
import com.back.constant.NettyConstant;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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 io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
public class SimpleNettyClient {
public static void main(String[] args) {
NioEventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ClientRouteHandler());
ChannelFuture future = bootstrap.connect("127.0.0.1", 8888).sync();
System.out.println("client start!");
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
}finally{
group.shutdownGracefully();
}
}
}
class ClientRouteHandler extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ByteBuf delimiter = Unpooled.copiedBuffer(NettyConstant.DELIMITER.getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new ClientMessageHandler());
}
}
6、ClientMessageHandler 发送请求和拿到返回报文
package com.back.client;
import com.back.constant.NettyConstant;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
public class ClientMessageHandler extends ChannelHandlerAdapter {
//byte[] req = ("time"+System.getProperty("line.separator")).getBytes();
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 100; i++) {
byte[] req = ("time"+NettyConstant.DELIMITER).getBytes();
ByteBuf buf = Unpooled.buffer(req.length);
buf.writeBytes(req);
ctx.writeAndFlush(buf);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String response = (String)msg;
System.out.println("接收到报文:"+response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}