您的位置:首页 > 理论基础 > 计算机网络

netty(一) 利用 LineBasedFrameDecoder,StringDecoder解决TCP粘包/拆包问题

2018-08-16 10:34 323 查看

.一,TCP粘包/拆包问题图示

产生TCP粘包/拆包问题如下几点:

TCP粘包/拆包问题解决方案:

 

二,利用 LineBasedFrameDecoder,StringDecoder解决TCP粘包/拆包问题 

server端代码示例:

[code]package nettyDemo3;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
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;

public class TimeServerHanler extends ChannelHandlerAdapter {

private int counter; // 计数器

/**
* 抛异常会调用此方法
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
// super.exceptionCaught(ctx, cause);
cause.printStackTrace();
ctx.close();
}

/**
* 读取客户端回应的消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
// super.channelRead(ctx, msg);
// ByteBuf buf = (ByteBuf) msg;
// byte[] bytes = new byte[buf.readableBytes()];
// buf.readBytes(bytes);
// String body = new String(bytes, "UTF-8");
// System.getProperty("line.separator") 换行符
String body = (String) msg;
System.out.println(body + "【第" + (++counter) + "次收到客户端的消息】");
String result = "i am server" + System.getProperty("line.separator");
ByteBuf buf2 = Unpooled.copiedBuffer(result.getBytes());
ctx.write(buf2);
}

/**
* 读取完成之后会调用此方法
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
// super.channelReadComplete(ctx);
ctx.flush();
System.out.println("读取完成");
}

public static void main(String[] args) {

// 配置服务端线程组,一个负责连接 ,一个负责读写
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
try {

// 创建服务端辅助启动类并设置参数
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {

@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
// 添加回车换行符解码器
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new TimeServerHanler());
}
});
int inetPort = 8080;
// 绑定端口,并开始阻塞等待客户端的连接
ChannelFuture f = bootstrap.bind(inetPort).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 优雅退出,释放线程池资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}

 

client端代码示例:

[code]package nettyDemo3;

import org.omg.PortableServer.POA;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
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;

public class TimeClientHandler extends ChannelHandlerAdapter {

private int counter;

/**
* 抛异常会调用此方法
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// TODO Auto-generated method stub
// super.exceptionCaught(ctx, cause);
ctx.close();
cause.printStackTrace();

}

/**
* 当客户端成功连接上服务端之后会调用此方法
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
// super.channelActive(ctx);
byte[] msg = ("i am clien" + System.getProperty("line.separator")).getBytes();
ByteBuf buf = null;
for (int i = 0; i < 50; i++) {
buf = Unpooled.buffer(msg.length);
buf.writeBytes(msg);
ctx.writeAndFlush(buf);
}
}

/**
* 读取服务端回应的消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// TODO Auto-generated method stub
// super.channelRead(ctx, msg);
// ByteBuf buf = (ByteBuf) msg;
// byte[] bytes = new byte[buf.readableBytes()];
// buf.readBytes(bytes);
// String body = new String(bytes, "UTF-8");
String body = (String) msg;
System.out.println(body + "【第" + (++counter) + "次收到服务端的消息】");
}

public static void main(String[] args) {
// 配置客户端端线程组负责读写
EventLoopGroup work = new NioEventLoopGroup();
try {
// 创建客户端的辅助启动类并设置参数
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(work).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
// 添加解码器
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new TimeClientHandler());
}
});
final String host = "127.0.0.1";
final int port = 8080;
// 连接服务端
ChannelFuture future = bootstrap.connect(host, port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
} finally {
// 优雅退出,释放线程池资源
work.shutdownGracefully();
}
}
}

 

分别运行客户端和服务端实例,看到控制台输出以下图片中的内容,说明成功解决tcp粘包/拆包问题。

 

附: netty利用DelimiterBasedFrameDecoder 和 FixedLengthFrameDecoder  也可以按需自定义解码实现粘包/拆包问题

[code]//添加自定义以$_为分隔符的解码器
ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
//添加固定长度解码器
ch.pipeline().addLast(new FixedLengthFrameDecoder(13));

 

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: