Netty--TCP粘包和拆包
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粘包和拆包的更多相关文章
- netty 解决TCP粘包与拆包问题(一)
1.什么是TCP粘包与拆包 首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线.当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包.得不到我 ...
- 【Netty】TCP粘包和拆包
一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...
- TCP粘包、拆包
TCP粘包.拆包 熟悉tcp编程的可能都知道,无论是服务端还是客户端,当我们读取或发送数据的时候,都需要考虑TCP底层的粘包/拆包机制. TCP是一个“流”协议,所谓流就是没有界限的遗传数据.可以想象 ...
- 2.Netty的粘包、拆包(一)
Netty粘包.拆包 1.什么是拆包.粘包 (1)拆包.粘包介绍 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片的,其间并没有分界线.TC ...
- Netty入门系列(2) --使用Netty解决粘包和拆包问题
前言 上一篇我们介绍了如果使用Netty来开发一个简单的服务端和客户端,接下来我们来讨论如何使用解码器来解决TCP的粘包和拆包问题 TCP为什么会粘包/拆包 我们知道,TCP是以一种流的方式来进行网络 ...
- Netty中粘包和拆包的解决方案
粘包和拆包是TCP网络编程中不可避免的,无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包和拆包 TCP是个“流”协议,所谓流,就是没有界限的一串 ...
- 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案
引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...
- tcp粘包和拆包的处理方案
随着智能硬件越来越流行,很多后端开发人员都有可能接触到socket编程.而很多情况下,服务器与端上需要保证数据的有序,稳定到达,自然而然就会选择基于tcp/ip协议的socekt开发.开发过程中,经常 ...
- TCP粘包和拆包问题
问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...
- TCP粘包,拆包及解决方法
在进行Java NIO学习时,发现,如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题.我们都知道TCP属于传输 ...
随机推荐
- log4j打印不同颜色
1.首先在eclipse中安装一个插件: ANSI COLOR 在Eclipse Marketplace 中直接搜索 ANSI COLOR 然后安装 2.在log4j 中加入红色字体部分: < ...
- bzoj1269
题解: splay维护 只不过变成了字符串 代码: #include<bits/stdc++.h> using namespace std; +,BS= + ,BN= + ; ,head, ...
- String.Remove
String.Remove方法注意事项: 1.该方法不改变元字符串: 2.str = ‘’: str.Remove(str.Length-3);明显超限,但是不报错,返回值为''; str = '1 ...
- 谷歌模拟手机和真机上显示的各个机型的 dpi--和高度
**以下数据尚未经过严密测试.待日后工作中再试** var dpi = window.devicePixelRatio;//获取屏幕分辨率 alert("dpi为:"+dpi); ...
- 【第三方类库】underscore.js源码---each forEach 每次迭代跟{}比较的疑惑
var each = _.each = _.forEach = function(obj, iterator, context) { if (obj == null) return; //首先判断是否 ...
- redis的Hash类型以及其操作
hashes类型 hashes类型及操作Redis hash是一个string类型的field和value的映射表.它的添加.删除操作都是0(1)(平均).hash特别适合用于存储对象.相较于将对象的 ...
- 201621123005《Java程序设计》第四周学习总结
201621123005<Java程序设计>第四周学习总结 标签(空格分隔): 1.本章学习总结 1. 面向对象设计 1.1 写出你认为本周学习中比较重要的知识点关键词 继承.多态.覆盖. ...
- 2017.10.23 Arduino Atmel EFM32低功耗监测
mark:因为最近一直调试(维护)客户的EFM32,和其他系列的Atmel芯片.对atmel不熟悉,做点功课. 1. 自建Arduino 最小系统(原来树莓派都是Atmel的芯片) http://ww ...
- LINUX系统下的shell命令---diff、cut、sort、uniq等
1)diff:比较两个文件或目录的不同 -b 不检查空格字符的不同 -B 不检查空白行 -c 显示全部内容,并标出不同之处 -i 不检查 ...
- Unity 异步网络方案 IOCP Socket + ThreadSafe Queue
Unity IOCP Socket + ThreadSafe Queue 1.Socket.BeginReceive系列接口在unityweb下是不正常的,页面刷新会导致问题 2.自己维护线程,会带来 ...