1.什么是TCP粘包与拆包

首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线。当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包。得不到我们想要的效果。

所谓粘包:当你把A,B两个数据从甲发送到乙,本想A与B单独发送,但是你却把AB一起发送了,此时AB粘在一起,就是粘包了

所谓拆包: 如果发送数据的时候,你把A、B拆成了几份发,就是拆包了。当然数据不是你主动拆的,是TCP流自动拆的

2.TCP粘包与拆包产生原因

1.进行了MSS大小的TCP分段
2.以太网帧的plyload大与MTU进行了IP分片
3.应用程序write写入的字节大小大于套接口发送的缓冲区大小

3.解决方法

1.消息定长,比如把报文消息固定为500字节,不够用空格补位

2.在包尾增加回车换行符进行分割,例如FTP协议

3.将消息分为消息头和消息体,消息头中包含表示消息总长度的字段

4.更复杂的应用层协议

4.netty 普通解决方法

这个是服务端代码

 1 package com.ming.netty.nio;
2
3 import java.net.InetSocketAddress;
4
5 import io.netty.bootstrap.ServerBootstrap;
6 import io.netty.channel.ChannelFuture;
7 import io.netty.channel.ChannelInitializer;
8 import io.netty.channel.ChannelOption;
9 import io.netty.channel.EventLoopGroup;
10 import io.netty.channel.nio.NioEventLoopGroup;
11 import io.netty.channel.socket.SocketChannel;
12 import io.netty.channel.socket.nio.NioServerSocketChannel;
13 import io.netty.handler.codec.LineBasedFrameDecoder;
14 import io.netty.handler.codec.string.StringDecoder;
15
16 public class TimeServer {
17
18
19 public static void main(String[] args) throws Exception{
20 new TimeServer().bind("192.168.1.102", 8400);
21 }
22
23
24 public void bind(String addr,int port) {
25 //配置服务端的nio线程组
26 EventLoopGroup boosGroup=new NioEventLoopGroup();
27 EventLoopGroup workerGroup=new NioEventLoopGroup();
28 try {
29 ServerBootstrap b=new ServerBootstrap();
30 b.group(boosGroup,workerGroup);
31 b.channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG,1024)
32 .childHandler(new ChildChannelHandler());
33 //绑定端口,同步等待成功
34 ChannelFuture f=b.bind(new InetSocketAddress(addr, port)).sync();
35 System.out.println("启动服务器:"+f.channel().localAddress());
36 //等等服务器端监听端口关闭
37 f.channel().closeFuture().sync();
38 } catch (Exception e) {
39 // TODO: handle exception
40 }finally{
41 boosGroup.shutdownGracefully();
42 workerGroup.shutdownGracefully();
43 }
44 }
45
46 private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
47
48 @Override
49 protected void initChannel(SocketChannel ch) throws Exception {
50 System.out.println(ch.remoteAddress());
51 ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
52 ch.pipeline().addLast(new StringDecoder());//增加解码器
53 ch.pipeline().addLast(new TimeServerHandler());
54
55 }
56
57 }
58
59
60 }
61 package com.ming.netty.nio;
62
63 import java.nio.ByteBuffer;
64 import java.text.SimpleDateFormat;
65 import java.util.Date;
66
67 import io.netty.buffer.ByteBuf;
68 import io.netty.buffer.Unpooled;
69 import io.netty.channel.ChannelHandlerAdapter;
70 import io.netty.channel.ChannelHandlerContext;
71
72 public class TimeServerHandler extends ChannelHandlerAdapter {
73
74 private int counter;
75
76 @Override
77 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
78 String body=(String)msg;
79 System.out.println("服务端收到:"+body+",次数:"+ ++counter);
80 SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
81 String time=dateFormat.format(new Date());
82 String res="来自与服务端的回应,时间:"+ time;
83 ByteBuf resp=Unpooled.copiedBuffer(res.getBytes());
84 ctx.writeAndFlush(resp);
85
86 }
87
88
89
90 @Override
91 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
92 ctx.close();
93 }
94
95
96
97
98 }

这个是客服端的代码

 1 package com.ming.netty.nio;
2
3 import io.netty.bootstrap.Bootstrap;
4 import io.netty.channel.ChannelFuture;
5 import io.netty.channel.ChannelInitializer;
6 import io.netty.channel.ChannelOption;
7 import io.netty.channel.EventLoopGroup;
8 import io.netty.channel.nio.NioEventLoopGroup;
9 import io.netty.channel.socket.SocketChannel;
10 import io.netty.channel.socket.nio.NioSocketChannel;
11 import io.netty.handler.codec.LineBasedFrameDecoder;
12 import io.netty.handler.codec.string.StringDecoder;
13
14 /**
15 * netty 客户端模拟
16 * @author mingge
17 *
18 */
19 public class TimeClient {
20
21
22 public static void main(String[] args) throws Exception{
23 new TimeClient().connect("192.168.1.102", 8400);
24 }
25
26 public void connect(String addr,int port) throws Exception{
27 EventLoopGroup group=new NioEventLoopGroup();
28 try {
29 Bootstrap b=new Bootstrap();
30 b.group(group).channel(NioSocketChannel.class)
31 .option(ChannelOption.TCP_NODELAY, true)
32 .handler(new ChannelInitializer<SocketChannel>() {
33 public void initChannel(SocketChannel ch) throws Exception{
34 ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
35 ch.pipeline().addLast(new StringDecoder());
36 ch.pipeline().addLast(new TimeClientHandler());
37 }
38 });
39 ChannelFuture f=b.connect(addr,port).sync();
40 System.out.println("连接服务器:"+f.channel().remoteAddress()+",本地地址:"+f.channel().localAddress());
41 f.channel().closeFuture().sync();//等待客户端关闭连接
42 } catch (Exception e) {
43 e.printStackTrace();
44 }finally{
45
46 group.shutdownGracefully();
47 }
48 }
49 }
50 package com.ming.netty.nio;
51
52 import io.netty.buffer.ByteBuf;
53 import io.netty.buffer.Unpooled;
54 import io.netty.channel.ChannelHandlerAdapter;
55 import io.netty.channel.ChannelHandlerContext;
56
57 public class TimeClientHandler extends ChannelHandlerAdapter {
58
59 private int counter;
60
61 byte[] req;
62
63 public TimeClientHandler() {
64 req=("我是请求数据哦"+System.getProperty("line.separator")).getBytes();
65 }
66
67 @Override
68 public void channelActive(ChannelHandlerContext ctx) throws Exception {
69 ByteBuf message=null;
70 for(int i=0;i<100;i++){
71 message=Unpooled.buffer(req.length);
72 message.writeBytes(req);
73 ctx.writeAndFlush(message);
74 }
75
76 }
77
78 @Override
79 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
80 ByteBuf buf=(ByteBuf)msg;
81 byte[] req=new byte[buf.readableBytes()];
82 buf.readBytes(req);
83 String body=new String(req,"GBK");
84 System.out.println("body:"+body+",响应次数:"+(++counter));
85 }
86
87 @Override
88 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
89 //释放资源
90 ctx.close();
91 }
92
93
94 }

这次代码就是比上次的代码多了:LineBasedFrameDecoder,与StringDecoder的写法.

LineBasedFrameDecoder的原理是它依次遍历ByteBuf中的可读字节,判断是否有"\n"或"\r\n",如果有就以此为结束。它是以换行符为结束标志的解码器

StringDecoder的原理就是将接收到的对象转换为字符串,然后接着调用后面的handler。

LineBasedFrameDecoder+StringDecoder组合就是设计按行切换的文本解码器,被设计来支持TCP的粘包与拆包

netty 解决TCP粘包与拆包问题(一)的更多相关文章

  1. netty 解决TCP粘包与拆包问题(二)

    TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识 ...

  2. netty 解决TCP粘包与拆包问题(三)

    今天使用netty的固定长度进行解码 固定长度解码的原理就是按照指定消息的长度对消息自动解码. 在netty实现中,只需要采用FiexedLengthFrameDecoder解码器即可... 以下是服 ...

  3. 【Netty】TCP粘包和拆包

    一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...

  4. Netty解决TCP粘包/拆包问题 - 按行分隔字符串解码器

    服务端 package org.zln.netty.five.timer; import io.netty.bootstrap.ServerBootstrap; import io.netty.cha ...

  5. 1. Netty解决Tcp粘包拆包

    一. TCP粘包问题 实际发送的消息, 可能会被TCP拆分成很多数据包发送, 也可能把很多消息组合成一个数据包发送 粘包拆包发生的原因 (1) 应用程序一次写的字节大小超过socket发送缓冲区大小 ...

  6. 【Netty】使用解码器Decoder解决TCP粘包和拆包问题

    解码器Decoder和ChannelHandler的关系 netty的解码器通常是继承自ByteToMessageDecoder,而它又是继承自ChannelInboundHandlerAdapter ...

  7. Netty使用LineBasedFrameDecoder解决TCP粘包/拆包

    TCP粘包/拆包 TCP是个”流”协议,所谓流,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TC ...

  8. 深入学习Netty(5)——Netty是如何解决TCP粘包/拆包问题的?

    前言 学习Netty避免不了要去了解TCP粘包/拆包问题,熟悉各个编解码器是如何解决TCP粘包/拆包问题的,同时需要知道TCP粘包/拆包问题是怎么产生的. 在此博文前,可以先学习了解前几篇博文: 深入 ...

  9. 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)

    一.粘包/拆包概念 TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认 ...

随机推荐

  1. C#实现每隔一段时间执行代码(多线程)

    总结以下三种方法,实现c#每隔一段时间执行代码: 方法一:调用线程执行方法,在方法中实现死循环,每个循环Sleep设定时间: 方法二:使用System.Timers.Timer类: 方法三:使用Sys ...

  2. C 语言学习的第 03 课:你的 idea 是怎么变成能够执行的程序的

    在上一篇文章中,我们说到,C 语言系统应该由程序开发环境,C 语言本身和 C 语言的库组成.且同时说了程序开发环境做了“编写”,“预处理”,“编译”和“链接”这几件事情.但是细节并没有一一呈现.不知道 ...

  3. SQL-Server下载地址

    有同学费尽心思的找SQL server数据库各版本的下载地址,看到别人的求助贴就不自觉的想去帮助他们,但是一个一个去帮助又不太现实,毕竟个人精力有限,既然大家有需求,那么艾薇百科今天就本着乐于分享和奉 ...

  4. Ubuntu安装出现左上角光标一直闪解决方式

    Ubuntu安装出现左上角光标一直闪解决方式: 01下载ubunu http://cn.ubuntu.com/download/ 02.软碟通 http://pan.baidu.com/s/1qY8O ...

  5. C++11的default和delete关键字

    C11的新特性实在是太多了,这2个关键字关注的人倒是少了很多,其中有一个原因便是编译器支持得太慢了(VS到VS2013才支持上),不过这2个关键字那真是极为有用的,下面我们来看看. [default关 ...

  6. 概率 高消light oj 1151

    t个样例 n个楼梯或蛇; a b 刚好走到a会到b; 问走到100期望; dp[i]   i到100的期望 这一点没奇怪的东西 dp[i]=1/6(dp[i+1]+dp[i+2]..+6); 有   ...

  7. lucene-查询query->BooleanQuery “与或”搜索

    BooleanQuery也是实际开发过程中经常使用的一种Query.它其实是一个组合的Query,在使用时可以把各种Query对象添加进去并标明它们之间的逻辑关系. BooleanQuery本身来讲是 ...

  8. bootstrap-fileupload-上传文件控件

    官方github:https://github.com/kartik-v/bootstrap-fileinput 官方dome网站:http://plugins.krajee.com/file-bas ...

  9. 【Codeforces 723D】Lakes in Berland (dfs)

    海洋包围的小岛,岛内的有湖,'.'代表水,'*'代表陆地,给出的n*m的地图里至少有k个湖,求填掉面积尽量少的水,使得湖的数量正好为k. dfs找出所有水联通块,判断一下是否是湖(海水区非湖).将湖按 ...

  10. bzoj4429: [Nwerc2015] Elementary Math小学数学

    先把所有可能的答案算出来,每个算式一个点,每个结果一个点,然后如果一个算式能算出一个结果,那么就连一条边 然后跑匈牙利,没有完美匹配就是impossible 每个算式最多有3个结果,所以边数是O(n) ...