TCP协议以流的方式进行数据传输,它无法理解其上层协议的数据意义,而是根据TCP缓冲区的大小对数据进行拆分或组装,即上层一个完整的包可能被拆为几个TCP包来发送,或上层几个包会被组合为一个TCP包发送,这就是TCP的粘包和拆包问题。TCP协议按照自己的工作方式工作,包的拆分和组装问题就需要上层应用来解决,比如每次发送定长的数据包,数据包中包含数据的大小或在包结尾处增加标记等方法。

Netty提供了多种解码器如LineBasedFrameDecoder和StringDecoder等来解决这个问题,只需要在pipeline中加入这些解码器即可,如:

--

new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new TimeClientHandler());
}
}

--

StringDecoder 作用是将接受到的对象转换为字符串,然后继续调用后面的handler。

LineBasedFrameDecoder 以换号符为结束标记的解码器,它依次遍历ByteBuf中的可读字节,如果碰到\n 或\r\n则设置为结束。参数为最大长度,如果读到最大长度还是没有换号则会抛出异常: Unexpected exception from downstream : frame length (16) exceeds the allowed maximum (10)

DelimiterBasedFrameDecoder 是以分隔符作为结束标志的解码器,并且解码时会自动去掉分隔符

FixedLengthFrameDecoder 是用于定长消息的解码器。

下面是一个简单的例子,演示几种decoder的用法:

EchoServer

package com.luangeng.netty.echo;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; /**
* Created by LG on 2017/11/20.
*/
public class EchoServer {
public static void main(String[] args) {
new EchoServer().bind(8080);
} public void bind(int port) {
//配置服务端的线程组,一个用于服务端接收客户端连接,另一个进行SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//ServerBootstrap启动NIO服务端的辅助启动类
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ByteBuf delimiter = Unpooled.copiedBuffer("$".getBytes());
//ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new FixedLengthFrameDecoder(25));
//ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new EchoServerHandler());
}
});
//绑定端口,sync为同步阻塞方法,等待绑定成功,ChannelFuture用于异步操作的通知回调
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("server started");
//等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("server shuting down");
//释放线程资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} }

---

EchoServerHandler

package com.luangeng.netty.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; /**
* Created by LG on 2017/11/20.
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
int count = 0; @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String)msg;
System.out.println("The server received("+count+++"): " + body); body = body + "$";
ByteBuf response = Unpooled.copiedBuffer(body.getBytes());
ctx.writeAndFlush(response);//异步发送
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
} }

---

EchoClient

package com.luangeng.netty.echo;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.FixedLengthFrameDecoder;
import io.netty.handler.codec.string.StringDecoder; /**
* Created by LG on 2017/11/20.
*/
public class EchoClient { public static void main(String[] args) {
new EchoClient().connect("127.0.0.1", 8080);
} public void connect(String host, int port) {
//配置客户端NIO线程组
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ByteBuf delimiter = Unpooled.copiedBuffer("$".getBytes());
//ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new FixedLengthFrameDecoder(25));
//ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new EchoClientHandler());
}
});
//发起异步连接操作,同步等待连接成功
ChannelFuture future = bootstrap.connect(host, port).sync();
System.out.println("client started");
//等待客户端链路关闭
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("client shuting down");
//释放NIO线程组
group.shutdownGracefully();
}
}
}

---

EchoClientHandler

package com.luangeng.netty.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; /**
* Created by LG on 2017/11/20.
*/
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
int count = 0; @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String)msg;
System.out.println("The server received("+count+++"): " + body); body = body + "";
ByteBuf response = Unpooled.copiedBuffer(body.getBytes());
ctx.writeAndFlush(response);//异步发送
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
} }

---

执行结果:

Server:

2017-11-20 22:21:35,508 INFO [io.netty.handler.logging.LoggingHandler] - [id: 0x9b011f21] REGISTERED
2017-11-20 22:21:35,510 INFO [io.netty.handler.logging.LoggingHandler] - [id: 0x9b011f21] BIND: 0.0.0.0/0.0.0.0:8080
server started
2017-11-20 22:21:35,512 INFO [io.netty.handler.logging.LoggingHandler] - [id: 0x9b011f21, L:/0:0:0:0:0:0:0:0:8080] ACTIVE
2017-11-20 22:21:39,919 INFO [io.netty.handler.logging.LoggingHandler] - [id: 0x9b011f21, L:/0:0:0:0:0:0:0:0:8080] READ: [id: 0x09644a50, L:/127.0.0.1:8080 - R:/127.0.0.1:52039]
2017-11-20 22:21:39,920 INFO [io.netty.handler.logging.LoggingHandler] - [id: 0x9b011f21, L:/0:0:0:0:0:0:0:0:8080] READ COMPLETE
The server received(0): Hi,my name is luangeng
The server received(1): Hi,my name is luangeng
The server received(2): Hi,my name is luangeng
The server received(3): Hi,my name is luangeng
The server received(4): Hi,my name is luangeng
The server received(5): Hi,my name is luangeng
The server received(6): Hi,my name is luangeng
The server received(7): Hi,my name is luangeng
The server received(8): Hi,my name is luangeng
The server received(9): Hi,my name is luangeng

Client:

client started
0 client get: Hi,my name is luangeng
1 client get: Hi,my name is luangeng
2 client get: Hi,my name is luangeng
3 client get: Hi,my name is luangeng
4 client get: Hi,my name is luangeng
5 client get: Hi,my name is luangeng
6 client get: Hi,my name is luangeng
7 client get: Hi,my name is luangeng
8 client get: Hi,my name is luangeng
9 client get: Hi,my name is luangeng

end

Netty--TCP粘包和拆包的更多相关文章

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

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

  2. 【Netty】TCP粘包和拆包

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

  3. TCP粘包、拆包

    TCP粘包.拆包 熟悉tcp编程的可能都知道,无论是服务端还是客户端,当我们读取或发送数据的时候,都需要考虑TCP底层的粘包/拆包机制. TCP是一个“流”协议,所谓流就是没有界限的遗传数据.可以想象 ...

  4. 2.Netty的粘包、拆包(一)

    Netty粘包.拆包 1.什么是拆包.粘包 (1)拆包.粘包介绍 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片的,其间并没有分界线.TC ...

  5. Netty入门系列(2) --使用Netty解决粘包和拆包问题

    前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...

  6. Netty中粘包和拆包的解决方案

    粘包和拆包是TCP网络编程中不可避免的,无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包和拆包 TCP是个“流”协议,所谓流,就是没有界限的一串 ...

  7. 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案

    引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...

  8. tcp粘包和拆包的处理方案

    随着智能硬件越来越流行,很多后端开发人员都有可能接触到socket编程.而很多情况下,服务器与端上需要保证数据的有序,稳定到达,自然而然就会选择基于tcp/ip协议的socekt开发.开发过程中,经常 ...

  9. TCP粘包和拆包问题

    问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...

  10. TCP粘包,拆包及解决方法

    在进行Java NIO学习时,发现,如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题.我们都知道TCP属于传输 ...

随机推荐

  1. 十五、dbms_space(分析段增长和空间的需求)

    1.概述 作用:用于分析段增长和空间的需求. 2.包的组成 1).unused_space作用:用于返回对象(表.索引.簇)的未用空间语法:dbms_space.unused_space(segmen ...

  2. ParentNodes、childNodes、children之间的区别

    "parentNode" 常用来获取某个元素的父节点. 把 parentNodes 理解为容器, 在容器中有个子节点 例: <div id="parent" ...

  3. 20165202 预备作业3 Linux安装及学习

    一.虚拟机安装 娄老师的<基于VirtualBox安装Ubuntu图文教程>对于安装过程的介绍很易懂,但在安装过程中还是遇到了一些问题 Q1:安装教程中下载地址的VM提示安装包损坏 解决办 ...

  4. ASP.NET MVC 中使用用户控件——转

    讲讲怎么在 ASP.NET MVC2中使用用户控件.首先我们新建一个用户控件,   我们命名为SelectGroup.ascx,代码如下 <%@ Control Language="C ...

  5. strip()函数和 split()函数

    一:python strip()函数介绍 函数原型:strip可以删除字符串的某些字符 声明:s为字符串,rm为要删除的字符序列 s.strip(rm)        删除s字符串中开头.结尾处,位于 ...

  6. JMter中添加断点和关联

    一.            断点 断言是什么呢,它是用来检查返回结果对不对的. 用来验证结果是否正确,如果正确的话,就代表这个请求的返回是正确的,如果没有的话就代表这个请求的结果和我们预期的不一致,这 ...

  7. NuGet 本地服务器 公司组件库搭建 上传下载操作 模块化灵活设计

    新年第一更,关于如何搭建NuGet的本地服务器,以及部署IIS,以此搭建公司自己的组件仓库,方便所有的组件更新,管理,测试等等操作. 关于如何在NuGet中下载指定的组件,参考这篇文章:http:// ...

  8. CentOS6 mail邮件服务配置

    mail服务配置 环境: [root@m01 ~]# cat /etc/redhat-release CentOS release 6.7 (Final) [root@m01 ~]# uname -m ...

  9. 第23课 #error和#line使用分析

    #error的用法: 示例程序: #include <stdio.h> #ifndef __cplusplus #error This file should be processed w ...

  10. ubuntu 搜狗输入法成功安装

    第一次成功安装这个破玩意,心里万分高兴啊! 几个条件: 1 ubuntu14.04 2 对的教程 参考文献:http://jingyan.baidu.com/article/ad310e80ae6d9 ...