一、什么是粘包/拆包html
通常所谓的TCP粘包是在一次接收数据不能彻底地体现一个完整的消息数据。TCP通信为什么存在粘包呢?主要缘由是TCP是以流的方式来处理数据,再加上网络上MTU的每每小于在应用处理的消息数据,因此就会引起一次接收的数据没法知足消息的须要,致使粘包的存在。处理粘包的惟一方法就是制定应用层的数据通信协议,经过协议来规范现有接收的数据是否知足消息数据的须要。java
二、解决办法编程
2.一、消息定长,报文大小固定长度,不够空格补全,发送和接收方遵循相同的约定,这样即便粘包了经过接收方编程实现获取定长报文也能区分。bootstrap
2.二、包尾添加特殊分隔符,例如每条报文结束都添加回车换行符(例如FTP协议)或者指定特殊字符做为报文分隔符,接收方经过特殊分隔符切分报文区分。数组
2.三、将消息分为消息头和消息体,消息头中包含表示信息的总长度(或者消息体长度)的字段服务器
三、自定义协议,来实现TCP的粘包/拆包问题网络
3.0 自定义协议,开始标记 异步

3.1 自定义协议的介绍socket

3.2 自定义协议的类的封装tcp

3.3 自定义协议的编码器

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 客户端加入协议的编/解码器

五、服务端的实现
- 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();
- }
-
- }
九、参考的博客地址