2.Netty的粘包、拆包(一)
Netty粘包、拆包
1.什麼是拆包、粘包
(1)拆包、粘包介紹
TCP是個“流”協議,所謂流,就是沒有界限的一串資料。大家可以想想河裡的流水,是連成一片的,其間並沒有分界線。TCP底層並不瞭解上層業務資料的具體含義,它會根據TCP緩衝區的實際情況進行包的劃分,所以在業務上認為,一個完整的包可能會被TCP拆分成多個包進行傳送,也有可能把多個小的包封裝成一個大的資料包傳送,這就是所謂的TCP粘包和拆包問題。
(2)圖解
(3)程式碼模擬
服務端Server
package com.xm.netty.demo02; import java.net.InetSocketAddress; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class Server { private final int port; public Server(int port) { this.port = port; } public static void main(String[] args) { int port = 8989; try { new Server(port).start(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void start() throws InterruptedException { EventLoopGroup g1 = new NioEventLoopGroup(); EventLoopGroup g2 = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap .group(g1,g2) .channel(NioServerSocketChannel.class) .localAddress(new InetSocketAddress( port)) .childHandler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new ServerHandler()); } }); ChannelFuture future = bootstrap.bind().sync(); future.channel().closeFuture().sync(); } finally { g1.shutdownGracefully().sync(); g2.shutdownGracefully().sync(); } } }
服務端ServerHandler
package com.xm.netty.demo02; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.util.CharsetUtil; public class ServerHandler extends ChannelHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf in = (ByteBuf) msg; String str = in.toString(CharsetUtil.UTF_8); System.out.println("Server:"+str); str = "伺服器返回--->"+ str; ctx.writeAndFlush(Unpooled.copiedBuffer(str.getBytes())); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())+"一個客戶端連線!"); } }
客戶端Client
package com.xm.netty.demo02; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; public class Client { private final int port; private final String host; public Client(int port, String host) { this.port = port; this.host = host; } public static void main(String[] args) { String host = "127.0.0.1"; int port = 8989; try { new Client(port, host).start(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void start() throws InterruptedException { EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap .group(group) .channel(NioSocketChannel.class) .remoteAddress(host, port) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ClientHandler()); } }); ChannelFuture future = bootstrap.connect().sync(); for(int i=10;i<20;i++) { String str = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now()) + "---- " +i+"<<<"; future.channel().writeAndFlush(Unpooled.copiedBuffer(str.getBytes())); } future.channel().closeFuture().sync(); } finally { group.shutdownGracefully().sync(); } } }
客戶端ClientHandler
package com.xm.netty.demo02; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCountUtil; public class ClientHandler extends ChannelHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try { ByteBuf in = (ByteBuf) msg; String str = in.toString(CharsetUtil.UTF_8); System.out.println("Client:"+str); } finally { ReferenceCountUtil.release(msg); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())+" 已連線上伺服器!"); } }
新增依賴
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.xm</groupId> <artifactId>netty</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>5.0.0.Alpha2</version> </dependency> </dependencies> </project>
預期結果
1)伺服器
2018-10-11T18:37:19.857一個客戶端連線!
Server:2018-10-11T18:37:19.855---- 10<<<
Server:2018-10-11T18:37:20.377---- 11<<<
Server:2018-10-11T18:37:20.877---- 12<<<
Server:2018-10-11T18:37:21.378---- 13<<<
Server:2018-10-11T18:37:21.879---- 14<<<
Server:2018-10-11T18:37:22.379---- 15<<<
Server:2018-10-11T18:37:22.879---- 16<<<
Server:2018-10-11T18:37:23.38---- 17<<<
Server:2018-10-11T18:37:23.881---- 18<<<
Server:2018-10-11T18:37:24.382---- 19<<<
(2)客戶端
2018-10-11T18:37:19.855 已連線上伺服器!
Client:伺服器返回--->2018-10-11T18:37:19.855---- 10<<<
Client:伺服器返回--->2018-10-11T18:37:20.377---- 11<<<
Client:伺服器返回--->2018-10-11T18:37:20.877---- 12<<<
Client:伺服器返回--->2018-10-11T18:37:21.378---- 13<<<
Client:伺服器返回--->2018-10-11T18:37:21.879---- 14<<<
Client:伺服器返回--->2018-10-11T18:37:22.379---- 15<<<
Client:伺服器返回--->2018-10-11T18:37:22.879---- 16<<<
Client:伺服器返回--->2018-10-11T18:37:23.38---- 17<<<
Client:伺服器返回--->2018-10-11T18:37:23.881---- 18<<<
Client:伺服器返回--->2018-10-11T18:37:24.382---- 19<<<
實際結果
(1)伺服器
2018-10-11T18:35:40.988一個客戶端連線! Server:2018-10-11T18:35:40.986---- 10<<<2018-10-11T18:35:41.01---- 11<<<2018-10-11T18:35:41.01---- 12<<<2018-10-11T18:35:41.01---- 13<<<2018-10-11T18:35:41.01---- 14<<<2018-10-11T18:35:41.01---- 15<<<2018-10-11T18:35:41.01---- 16<<<2018-10-11T18:35:41.01---- 17<<<2018-10-11T18:35:41.01---- 18<<<2018-10-11T18:35:41.01---- 19<<<
(2)客戶端
2018-10-11T18:35:40.986 已連線上伺服器! Client:伺服器返回--->2018-10-11T18:35:40.986---- 10<<<2018-10-11T18:35:41.01---- 11<<<2018-10-11T18:35:41.01---- 12<<<2018-10-11T18:35:41.01---- 13<<<2018-10-11T18:35:41.01---- 14<<<2018-10-11T18:35:41.01---- 15<<<2018-10-11T18:35:41.01---- 16<<<2018-10-11T18:35:41.01---- 17<<<2018-10-11T18:35:41.01---- 18<<<2018-10-11T18:35:41.01---- 19<<<