TCP粘包与拆包问题
2017-10-08 16:42
260 查看
0 概述
熟悉TCP编程的读者可能都知道,无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP粘包与拆包问题机制。1 TCP粘包与拆包问题
TCP是个”流协议”,所谓流,就是没有界限的一串数据。TCP底层并不了解上层业务的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在上层业务认为,一个完整的包可能被TCP拆分成多个包进行发送,也可能把多个小的包封装成一个大包发送,这就是TCP拆包与粘包问题。下图中客户端向服务端先后发送了Packet1和Packet2两个包。
实例分析:
服务端code:
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; /** * Created by apple on 17/9/4. */ public class NettyServer { public static void main(String[] args) { /** * parentGroup 用于accept */ EventLoopGroup parentGroup = new NioEventLoopGroup(1); /** * 默认线程数 cup*2 ,用于处理 */ EventLoopGroup childGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(parentGroup, childGroup); serverBootstrap.option(ChannelOption.SO_BACKLOG, 128) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new SimpleHandler()); } }); ChannelFuture future = serverBootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); } catch (Exception ex) { ex.printStackTrace(); } finally { parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } } } import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Created by apple on 17/9/29. */ public class SimpleHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf=(ByteBuf)msg; byte [] data=new byte[buf.readableBytes()]; buf.readBytes(data); System.out.println(new String(data)); String responseMsg="收到"+System.getProperty("line.separator"); ByteBuf sendMsg= Unpooled.copiedBuffer(responseMsg.getBytes()); ctx.writeAndFlush(sendMsg); } }
客户端
import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; /** * Created by apple on 17/10/1. */ public class NettyClient { public static void main(String[] args) { startClient(); } private static void startClient() { EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); try { bootstrap.channel(NioSocketChannel.class) .group(eventLoopGroup) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelInitializer<NioSocketChannel>() { protected void initChannel(NioSocketChannel ch) throws Exception { ch.pipeline().addLast(new ClientHandler()); } }); //发起异步连接操作 ChannelFuture future= bootstrap.connect("127.0.0.1", 8080).sync(); //等待客户端链路关闭 future.channel().closeFuture().sync(); } catch (Exception ex) { ex.printStackTrace(); } finally { eventLoopGroup.shutdownGracefully(); } } } import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Created by apple on 17/10/1. */ public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("客户端开始读数据"); ByteBuf buf = (ByteBuf) msg; byte[] data = new byte[buf.readableBytes()]; buf.readBytes(data); System.out.println(new String(data)); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //向服务端发送100 个hello world for (int i = 0; i < 100; i++) { ByteBuf buf = Unpooled.copiedBuffer("hello world".getBytes()); ctx.writeAndFlush(buf); } } }
运行结果:
希望达到效果是服务端收到100条消息同时恢复100条消息,结果只收到了两条消息,同时也只回复了两条消息。
解决办法:
既然业务层知道需要用什么分割,那么就可以指定相应的解码器。
LineBasedFrameDecoder 是以换行符为结束标志的解码器,当然也可以自定义解码器实现私有协议。
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; /** * Created by apple on 17/9/4. */ public class NettyServer { public static void main(String[] args) { /** * parentGroup 用于accept */ EventLoopGroup parentGroup = new NioEventLoopGroup(1); /** * 默认线程数 cup*2 ,用于处理 */ EventLoopGroup childGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(parentGroup, childGroup); serverBootstrap.option(ChannelOption.SO_BACKLOG, 128) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) .childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); ch.pipeline().addLast(new SimpleHandler()); } }); ChannelFuture future = serverBootstrap.bind(8080).sync(); future.channel().closeFuture().sync(); } catch (Exception ex) { ex.printStackTrace(); } finally { parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } } }
客户端发送时候需要带上换行符:
import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Created by apple on 17/10/1. */ public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("客户端开始读数据"); ByteBuf buf = (ByteBuf) msg; byte[] data = new byte[buf.readableBytes()]; buf.readBytes(data); System.out.println(new String(data)); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { //向服务端发送100 个hello world String message="hello world"+System.getProperty("line.separator"); for (int i = 0; i < 100; i++) { ByteBuf buf = Unpooled.copiedBuffer(message.getBytes()); ctx.writeAndFlush(buf); } } }
参考文献
[1] Netty 权威指南(第二版),李林峰著
相关文章推荐
- TCP粘包问题
- 关于TCP的粘包问题
- TCP的粘包问题
- Netty精粹之TCP粘包拆包问题
- TCP粘包问题分析
- TCP的粘包问题
- tcp粘包问题
- 解决TCP网络传输“粘包”问题
- Netty通信框架提供解决TCP粘包拆包问题方案
- TCP 有粘包 UDP 没有粘包问题
- Netty中TCP粘包问题代码示例与分析
- TCP面试题(三)之粘包问题
- TCP粘包/拆包--利用LineBasedFrameDecoder解决TCP粘包问题
- Netty实践(二):TCP拆包、粘包问题
- 解决TCP网络传输“粘包”问题
- netty开发之tcp粘包拆包问题
- [转]TCP粘包问题的解决
- Netty学习——TCP粘包/拆包的问题
- 网络编程与并发-TCP/UDP套接字、粘包问题、Socket编程、并发编程、FTP作业
- Linux网络编程学习笔记(6)---TCP中的粘包问题及解决方案