一、TCP的粘包拆包问题
Tcp底层协议保证了发数据包的顺序,并没有保证每一次会发一个完整的包。
下面用图来表示粘包,拆包的一种情况:
假设客户端向服务端发送了两个请求包(data1,data2),服务端收到这两个包的情况是无 法预测的。
二、Netty模拟粘包
Server:
/********************************************************************
* Netty 学习
********************************************************************
*/
package com.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
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;
/**
*
* @author Jack Lei
* @email 895896736@qq.com
* @date 2016年3月13日 下午3:07:12
*/
public class Server {
final static short packetId = 1001;
public Server(int port) {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
ServerBootstrap boot = new ServerBootstrap();
boot.group(boss, work)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx,
Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
short packetId = buf.readShort();
if (packetId == Server.packetId) {
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String str = new String(bytes);
System.out.println("PacketId = " + packetId + ", body = " + str);
}
}
});
}
});
try {
ChannelFuture future = boot.bind(port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
public static void main(String[] args) {
new Server(8090);
}
}
Client
/********************************************************************
*
* Netty 学习
********************************************************************
*/
package com.netty;
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.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
/**
*
* @author Jack Lei
* @email 895896736@qq.com
* @date 2016年3月13日 下午3:19:48
*/
public class ClientTest {
final short packetId = 1001;
public ClientTest(String host, int port) {
EventLoopGroup work = new NioEventLoopGroup();
Bootstrap boot = new Bootstrap();
boot.group(work)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 100; i++) {
ByteBuf buf = Unpooled.buffer();
buf.writeShort(packetId);
String msg = "返回信息。";
buf.writeBytes(msg.getBytes());
ctx.writeAndFlush(buf);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx,
Object msg) throws Exception {
super.channelRead(ctx, msg);
}
});
};
});
try {
ChannelFuture future = boot.connect(host, port).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
work.shutdownGracefully();
}
}
public static void main(String[] args) {
new ClientTest("127.0.0.1", 8090);
}
}
三、测试结果:
出现粘包的情况后,服务端如果不做处理粘包拆包处理,就无法正确的把包解码,导致了客户端收不到相应的返回包。关于粘包,拆包的解决办法,将会在下一篇写出。