一、什麼是粘包/拆包html
通常所謂的TCP粘包是在一次接收數據不能徹底地體現一個完整的消息數據。TCP通信爲什麼存在粘包呢?主要緣由是TCP是以流的方式來處理數據,再加上網絡上MTU的每每小於在應用處理的消息數據,因此就會引起一次接收的數據沒法知足消息的須要,致使粘包的存在。處理粘包的惟一方法就是制定應用層的數據通信協議,經過協議來規範現有接收的數據是否知足消息數據的須要。java
二、解決辦法編程
2.一、消息定長,報文大小固定長度,不夠空格補全,發送和接收方遵循相同的約定,這樣即便粘包了經過接收方編程實現獲取定長報文也能區分。bootstrap
2.二、包尾添加特殊分隔符,例如每條報文結束都添加回車換行符(例如FTP協議)或者指定特殊字符做爲報文分隔符,接收方經過特殊分隔符切分報文區分。數組
2.三、將消息分爲消息頭和消息體,消息頭中包含表示信息的總長度(或者消息體長度)的字段服務器
三、自定義協議,來實現TCP的粘包/拆包問題網絡
3.0 自定義協議,開始標記 異步
![](http://static.javashuo.com/static/loading.gif)
3.1 自定義協議的介紹socket
![](http://static.javashuo.com/static/loading.gif)
3.2 自定義協議的類的封裝tcp
![](http://static.javashuo.com/static/loading.gif)
3.3 自定義協議的編碼器
![](http://static.javashuo.com/static/loading.gif)
3.4 自定義協議的解碼器
四、協議相關的實現
4.1 協議的封裝
- import java.util.Arrays;
-
- public class SmartCarProtocol {
-
- private int head_data = ConstantValue.HEAD_DATA;
-
- private int contentLength;
-
- private byte[] content;
-
-
- public SmartCarProtocol(int contentLength, byte[] content) {
- this.contentLength = contentLength;
- this.content = content;
- }
-
- public int getHead_data() {
- return head_data;
- }
-
- public int getContentLength() {
- return contentLength;
- }
-
- public void setContentLength(int contentLength) {
- this.contentLength = contentLength;
- }
-
- public byte[] getContent() {
- return content;
- }
-
- public void setContent(byte[] content) {
- this.content = content;
- }
-
- @Override
- public String toString() {
- return "SmartCarProtocol [head_data=" + head_data + ", contentLength="
- + contentLength + ", content=" + Arrays.toString(content) + "]";
- }
-
- }
4.2 協議的編碼器
- public class SmartCarEncoder extends MessageToByteEncoder<SmartCarProtocol> {
-
- @Override
- protected void encode(ChannelHandlerContext tcx, SmartCarProtocol msg,
- ByteBuf out) throws Exception {
-
-
- out.writeInt(msg.getHead_data());
-
- out.writeInt(msg.getContentLength());
-
- out.writeBytes(msg.getContent());
- }
- }
4.3 協議的解碼器
- import java.util.List;
- import io.netty.buffer.ByteBuf;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.handler.codec.ByteToMessageDecoder;
-
- public class SmartCarDecoder extends ByteToMessageDecoder {
-
-
- public final int BASE_LENGTH = 4 + 4;
-
- @Override
- protected void decode(ChannelHandlerContext ctx, ByteBuf buffer,
- List<Object> out) throws Exception {
-
- if (buffer.readableBytes() >= BASE_LENGTH) {
-
-
-
- if (buffer.readableBytes() > 2048) {
- buffer.skipBytes(buffer.readableBytes());
- }
-
-
- int beginReader;
-
- while (true) {
-
- beginReader = buffer.readerIndex();
-
- buffer.markReaderIndex();
-
- if (buffer.readInt() == ConstantValue.HEAD_DATA) {
- break;
- }
-
-
-
- buffer.resetReaderIndex();
- buffer.readByte();
-
-
-
-
- if (buffer.readableBytes() < BASE_LENGTH) {
- return;
- }
- }
-
-
-
- int length = buffer.readInt();
-
- if (buffer.readableBytes() < length) {
-
- buffer.readerIndex(beginReader);
- return;
- }
-
-
- byte[] data = new byte[length];
- buffer.readBytes(data);
-
- SmartCarProtocol protocol = new SmartCarProtocol(data.length, data);
- out.add(protocol);
- }
- }
-
- }
4.4 服務端加入協議的編/解碼器
4.5 客戶端加入協議的編/解碼器
![](http://static.javashuo.com/static/loading.gif)
五、服務端的實現
- 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.logging.LogLevel;
- import io.netty.handler.logging.LoggingHandler;
-
- public class Server {
-
- public Server() {
- }
-
- public void bind(int port) throws Exception {
-
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
-
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, workerGroup)
- .channel(NioServerSocketChannel.class)
- .handler(new LoggingHandler(LogLevel.INFO))
- .childHandler(new ChildChannelHandler())
- .option(ChannelOption.SO_BACKLOG, 1024)
- .childOption(ChannelOption.SO_KEEPALIVE, true);
-
- ChannelFuture f = b.bind(port).sync();
-
- f.channel().closeFuture().sync();
- } finally {
-
- workerGroup.shutdownGracefully();
- bossGroup.shutdownGracefully();
- }
- }
-
-
- private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
-
- ch.pipeline().addLast(new SmartCarEncoder());
- ch.pipeline().addLast(new SmartCarDecoder());
-
- ch.pipeline().addLast(new ServerHandler());
- }
- }
-
- public static void main(String[] args) throws Exception {
- new Server().bind(9999);
- }
- }
六、服務端Handler的實現
- import io.netty.channel.ChannelHandlerAdapter;
- import io.netty.channel.ChannelHandlerContext;
-
- public class ServerHandler extends ChannelHandlerAdapter {
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg)
- throws Exception {
-
- SmartCarProtocol body = (SmartCarProtocol) msg;
- System.out.println("Server接受的客戶端的信息 :" + body.toString());
-
-
- String str = "Hi I am Server ...";
- SmartCarProtocol response = new SmartCarProtocol(str.getBytes().length,
- str.getBytes());
-
- ctx.writeAndFlush(response);
-
-
-
-
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
- throws Exception {
-
- ctx.close();
- }
- }
七、客戶端的實現
- 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;
-
- public class Client {
-
-
- public void connect(int port, String host) throws Exception {
-
- EventLoopGroup group = new NioEventLoopGroup();
- try {
-
- Bootstrap b = new Bootstrap();
- b.group(group)
- .channel(NioSocketChannel.class)
- .option(ChannelOption.TCP_NODELAY, true)
- .handler(new MyChannelHandler());
-
- ChannelFuture f = b.connect(host, port).sync();
-
-
- f.channel().closeFuture().sync();
-
- } finally {
- group.shutdownGracefully();
- System.out.println("客戶端優雅的釋放了線程資源...");
- }
-
- }
-
-
- private class MyChannelHandler extends ChannelInitializer<SocketChannel> {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
-
- ch.pipeline().addLast(new SmartCarEncoder());
- ch.pipeline().addLast(new SmartCarDecoder());
-
- ch.pipeline().addLast(new ClientHandler());
- }
-
- }
-
- public static void main(String[] args) throws Exception {
- new Client().connect(9999, "127.0.0.1");
-
- }
-
- }
八、客戶端Handler的實現
- import io.netty.channel.ChannelHandlerAdapter;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.util.ReferenceCountUtil;
-
- public class ClientHandler extends ChannelHandlerAdapter {
-
-
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
-
-
- String data = "I am client ...";
-
- byte[] content = data.getBytes();
-
- int contentLength = content.length;
-
- SmartCarProtocol protocol = new SmartCarProtocol(contentLength, content);
-
- ctx.writeAndFlush(protocol);
- }
-
-
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg)
- throws Exception {
- try {
-
- SmartCarProtocol body = (SmartCarProtocol) msg;
- System.out.println("Client接受的客戶端的信息 :" + body.toString());
-
- } finally {
- ReferenceCountUtil.release(msg);
- }
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
- throws Exception {
- ctx.close();
- }
-
- }
九、參考的博客地址