在通讯协议中,粘包,拆包是常发生的事情,而netty则很好的给出了三种解决方式,下面分别介绍:
一;利用LineBasedFrameDecoder解决TCP粘包问题
直接上代码,
Client类:
package stickorApartPackageResolveOne.one;
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.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
* @ProjectName: nettyTes
* @Package: stickorApartPackageResolveOne.one
* @ClassName: Client
* @Author: Administrator
* @Description: ${description}
* @Date: 2019/10/12 16:48
* @Version: 1.0
* *LineBasedFrameDecoder以换行符为结束标志的解码器,支持携带结束符或者不携带结束符两种解码方式,
* * 同时支持配置单行的最大长度。如果连续读取到最大长度后仍然没有发现换行符,
* * 就会抛出异常,同时忽略掉之前读到的异常码流。
* * StringDecoder的功能非常简单,就是将接收到的对象转换成字符串,
* * 然后继续调用后面的handler。LineBasedFrameDecoder+StringDecoder组合就是按行切换的文本解码器,
* * 它被设计用来支持TCP的粘包和拆包。
*/
public class Client {
public static void main(String[] args) {
int port = 8080;
String host = "127.0.0.1";
new Client().connect(port, host);
}
private void connect(int port, String host) {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bst = new Bootstrap();
bst.group(eventLoopGroup);
bst.channel(NioSocketChannel.class);
bst.option(ChannelOption.TCP_NODELAY, true);
bst.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture f = bst.connect(host, port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
}
ClientHandler类
package stickorApartPackageResolveOne.one;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import java.util.logging.Logger;
/**
* @ProjectName: nettyTes
* @Package: stickorApartPackageResolveOne.one
* @ClassName: ClientHandler
* @Author: Administrator
* @Description: ${description}
* @Date: 2019/10/12 17:03
* @Version: 1.0
*/
public class ClientHandler extends ChannelHandlerAdapter {
private static final Logger logger = Logger
.getLogger(ClientHandler.class.getName());
private int counter;
private byte[] req;
public ClientHandler() {
req = ("QUERY TIME ORDER" + System.getProperty("line.separator") ).getBytes();
System.out.print(req.length);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ByteBuf message = null;
for (int i = 0; i < 30; i++) {
message = Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
String body = (String) msg;
System.out.println("Now is : " + body + " ; the counter is : " + ++counter);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 释放资源
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}
}
Server类
package stickorApartPackageResolveOne.one;
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;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/**
* @ProjectName: nettyTes
* @Package: stickorApartPackageResolveOne
* @ClassName: Timeserver
* @Author: Administrator
* @Description: ${description}
* @Date: 2019/10/12 16:03
* @Version: 1.0
*/
public class Server {
public static void main(String args[]) {
int port = 8080;
new Server().bind(port);
}
private void bind(int port) {
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
EventLoopGroup eventLoopGroup1 = new NioEventLoopGroup();
try {
ServerBootstrap sbt = new ServerBootstrap();
sbt.group(eventLoopGroup, eventLoopGroup1);
sbt.channel(NioServerSocketChannel.class);
sbt.option(ChannelOption.SO_BACKLOG, 1024);
sbt.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//利用LineBasedFrameDecoder解决TCP粘包问题
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture cf = sbt.bind(port).sync();
cf.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully();
eventLoopGroup1.shutdownGracefully();
}
}
}
ServerHandler类
package stickorApartPackageResolveOne.one;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
/**
* @ProjectName: nettyTes
* @Package: stickorApartPackageResolveOne
* @ClassName: ServerHandler
* @Author: Administrator
* @Description: ${description}
* @Date: 2019/10/12 16:20
* @Version: 1.0
*/
public class ServerHandler extends ChannelHandlerAdapter {
int counter;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String)msg;
System.out.println("The time server receive order : " + body + " ; the counter is : " + ++counter);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(System.currentTimeMillis()).toString() : "BAD ORDER";
currentTime = currentTime + System.getProperty("line.separator");
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
ctx.writeAndFlush(resp);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
先直接上结果:
客户端:
服务端:
LineBasedFrameDecoder和StringDecoder的原理分析
LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf中的可读字节,判断看是否有\n或者以\r\n气如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。它是以换行符为结束标志的解码器,支持携带结束符或者不携带结束符两种解码方式,同时支持配置单行的最大长度。如果连续读取到最大长度后仍然没有发现换行符,就会抛出异常,同时忽略掉之前读到的异常码流。
StringDecoder的功能非常简单,就是将接收到的对象转换成字符串,然后继续调用后面的handler。LineBasedFrameDecoder+StringDecoder组合就是按行切换的文本解码器,它被设计用来支持TCP的粘包和拆包。