Netty实践(二):TCP拆包、粘包问题-学海无涯 心境无限-51CTO博客 http://blog.51cto.com/zhangfengzhe/1890577

2017-01-09 21:56:06

什么是TCP拆包、粘包?

在网络通信中,数据在底层都是以字节流形式在流动,那么发送方和接受方理应有一个约定(协议),只有这样接受方才知道需要接受多少数据,哪些数据需要在一起处理;如果没有这个约定,就会出现本应该一起处理的数据,被TCP划分为多个包发给接收方进行处理,如下图:

看一个TCP拆包、粘包的实例

客户端Handler:

服务端Handler:

运行结果:

上面的程序本意是CLIENT发送3次消息给SERVER,SERVER端理应处理3次,可是结果SERVER却将3条消息一次处理了。

那么如何解决TCP拆包、粘包问题呢?其实思路不外乎有3种:

第一种:发定长数据

接收方拿固定长度的数据,发送方发送固定长度的数据即可。但是这样的缺点也是显而易见的:如果发送方的数据长度不足,需要补位,浪费空间。

第二种:在包尾部增加特殊字符进行分割

发送方发送数据时,增加特殊字符;在接收方以特殊字符为准进行分割

第三种:自定义协议

类似于HTTP协议中的HEAD信息,比如我们也可以在HEAD中,告诉接收方数据的元信息(数据类型、数据长度等)

Netty如何解决TCP拆包、粘包问题?

《Java通信实战:编写自定义通信协议实现FTP服务》中,涉及到了JAVA SOCKET这方面的处理,大家可以参考。接下来,我们来看Netty这个框架是如何帮助我们解决这个问题的。本篇博客的代码在《Netty实践(一):轻松入门》基础上进行。

方式一:定长消息

Server启动类:

Client Handler:

运行结果:

利用FixedLengthFrameDecoder,加入到管道流处理中,长度够了接收方才能收到。

方式二:自定义分隔符

Server启动类:

Client Handler:

运行结果:

方式三:自定义协议

下面我们将简单实现一个自定义协议:

HEAD信息中包含:数据长度、数据版本

数据内容

MyHead

public class MyHead {

    //数据长度
private int length; //数据版本
private int version; public MyHead(int length, int version) {
this.length = length;
this.version = version;
} public int getLength() {
return length;
} public void setLength(int length) {
this.length = length;
} public int getVersion() {
return version;
} public void setVersion(int version) {
this.version = version;
} } MyMessage public class MyMessage {
//消息head
private MyHead head;
//消息body
private String content; public MyMessage(MyHead head, String content) {
this.head = head;
this.content = content;
} public MyHead getHead() {
return head;
} public void setHead(MyHead head) {
this.head = head;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
} @Override
public String toString() {
return String.format("[length=%d,version=%d,content=%s]",head.getLength(),head.getVersion(),content);
}
} 编码器 /**
* Created by Administrator on 17-1-9.
* 编码器 将自定义消息转化成ByteBuff
*/
public class MyEncoder extends MessageToByteEncoder { @Override
protected void encode(ChannelHandlerContext channelHandlerContext, MyMessage myMessage, ByteBuf byteBuf) throws Exception { int length = myMessage.getHead().getLength();
int version = myMessage.getHead().getVersion();
String content = myMessage.getContent(); byteBuf.writeInt(length);
byteBuf.writeInt(version);
byteBuf.writeBytes(content.getBytes(Charset.forName("UTF-8"))); }
} 解码器 /**
* Created by Administrator on 17-1-9.
* 解码器 将ByteBuf数据转化成自定义消息
*/
public class MyDecoder extends ByteToMessageDecoder { @Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List
 

运行结果

到这里,你会发现Netty处理TCP拆包、粘包问题很简单,通过编解码技术支持,让我们编写自定义协议也很方便,在后续的Netty博客中,我将继续为大家介绍Netty在实际中的一些应用(比如实现心跳检测),See You~

Netty处理TCP拆包、粘包的更多相关文章

  1. 架构师养成记--20.netty的tcp拆包粘包问题

    问题描述 比如要发ABC DEFG HIJK 这一串数据,其中ABC是一个包,DEFG是一个包,HIJK是一个包.由于TCP是基于流发送的,所以有可能出现ABCD EFGH 这种情况,那么ABC和D就 ...

  2. TCP拆包粘包之分隔符解码器

    TCP以流的方式进行数据传输,上层的应用协议为了对消息进行区分,往往采用如下4种方式. (1)消息长度固定,累计读取到长度总和为定长LEN的报文后,就认为读取到了一个完整的消息:将计数器置位,重新开始 ...

  3. 使用Netty如何解决拆包粘包的问题

    首先,我们通过一个DEMO来模拟TCP的拆包粘包的情况:客户端连续向服务端发送100个相同消息.服务端的代码如下: AtomicLong count = new AtomicLong(0); NioE ...

  4. tomcat Http11NioProtocol如何解析http请求及如何解决TCP拆包粘包

    前言 tomcat是常用的Web 应用服务器,目前国内有很多文章讲解了tomcat架构,请求流程等,但是没有如何解析http请求及如何解决TCP粘包拆包,所以这篇文章的目的就是介绍这块内容,一下内容完 ...

  5. Netty—TCP的粘包和拆包问题

    一.前言 虽然TCP协议是可靠性传输协议,但是对于TCP长连接而言,对于消息发送仍然可能会发生粘贴的情形.主要是因为TCP是一种二进制流的传输协议,它会根据TCP缓冲对包进行划分.有可能将一个大数据包 ...

  6. 《精通并发与Netty》学习笔记(14 - 解决TCP粘包拆包(二)Netty自定义协议解决粘包拆包)

    一.Netty粘包和拆包解决方案 Netty提供了多个解码器,可以进行分包的操作,分别是: * LineBasedFrameDecoder (换行)   LineBasedFrameDecoder是回 ...

  7. Netty 拆包粘包和服务启动流程分析

    Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你能掌握EventLoopGroup的工作流程,ServerBootstrap的启动流程,ChannelPipeline是如何操作管理Ch ...

  8. netty的解码器和粘包拆包

    Tcp是一个流的协议,一个完整的包可能会被Tcp拆成多个包进行发送,也可能把一个小的包封装成一个大的数据包发送,这就是所谓的粘包和拆包问题 粘包.拆包出现的原因: 在流传输中出现,UDP不会出现粘包, ...

  9. 【转】Netty 拆包粘包和服务启动流程分析

    原文:https://www.cnblogs.com/itdragon/archive/2018/01/29/8365694.html Netty 拆包粘包和服务启动流程分析 通过本章学习,笔者希望你 ...

随机推荐

  1. 安装CentOS7后,无法联网,用yum安装软件提示 cannot find a valid baseurl for repo:base/7/x86_64 的解决方法

    无法联网的明显表现会有: 1.yum install出现 Error: cannot find a valid baseurl or repo:base 2.ping host会提示unknown h ...

  2. prompt() 方法

    定义和用法 prompt() 方法用于显示可提示用户进行输入的对话框. 语法 prompt(text,defaultText) 参数 描述 text 可选.要在对话框中显示的纯文本(而不是 HTML ...

  3. 代码审计之Finecms任意文件下载漏洞

    PS:该漏洞已被公布,只是学习.故自己跟着大佬的步伐审计. 文件地址:\controllers\ApiController.php Line 57 public function downAction ...

  4. 本系列love2d示例代码错误集中整理

    3.输入和音乐 音乐不是循环播放的,可以在love.audio.play(music) 之前添加music:setLooping(true)

  5. PHP——0128练习相关2——js点击button按钮跳转到另一个新页面

    js点击button按钮跳转到另一个新页面 投稿:whsnow 字体:[增加 减小] 类型:转载 时间:2014-10-10我要评论 点击按钮怎么跳转到另外一个页面呢?点击图片要跳转到新的页面时,怎么 ...

  6. 使用wc统计代码行数

    最近写了一些代码,想统计一下代码的行数,在eclipse中好像没这功能,网上搜了一下才发现原来Linux有一个统计文件行数的命令wc.使用wc可以打印出每个文件和总文件的行数.字数和字节数,如果没有指 ...

  7. yum whatprovides host 根据命令查找包

    [root@Cobbler ~]# yum whatprovides host Loaded plugins: fastestmirror Loading mirror speeds from cac ...

  8. IDEA 中 右键新建时,没有新建class的解决方案

    今天在用IDEA新建包后,因为我新建的包名是userprivman-data这样的包,然后导致右键后没法新建类,并且在新建包名的时候就提示:不合法的包名,可能不会新建类 然后右键单击包名,发现没有新建 ...

  9. 002Maven_第一个Maven演示

    第一步. 首先建立Hello项目同时建立Maven约定的目录结构 Hello     --src     -----main     ----------java     ----------reso ...

  10. 文件 I/O 问题

    文件 I/O 问题:(1)对不存在的或者错误的文件进行操作吗?(2)文件以不正确的方式打开吗?(3)文件结束判断不正确吗?(4)没有正确地关闭文件吗? #include <iostream> ...