Netty解决TCP粘包/拆包导致的半包读写问题
一.TCP粘包/拆包问题说明
TCP是个“流”协议,就是没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包拆分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。
二.利用LinedBasedFrameDecoder解决TCP粘包问题
为了解决TCP粘包/拆包导致的半包读写问题,netty默认提供了多种解码器用于处理半包,只要能熟练掌握这些类库使用,TCP粘包问题就会非常容易。
2.1 支持TCP粘包的服务端开发
2.1.1 TimeServer实现:
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;
importio.netty.channel.nio.NioEventLoopGroup;
importio.netty.channel.socket.SocketChannel;
importio.netty.channel.socket.nio.NioServerSocketChannel;
importio.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/*
* 利用LineBasedFrameDecoder解决TCP粘包问题
*/
public class TimeServerNoStick {
publicvoid bind(int port) throws Exception{
//配置服务端的NIO线程组
EventLoopGroupbossGroup =new NioEventLoopGroup();
EventLoopGroupworkerGroup =new NioEventLoopGroup();
try{
//用于启动NIO服务器的辅助启动类,降低服务端的开发复杂度
ServerBootstrapb=new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)
.childHandler(newChildChannelHandler());
//绑定端口,同步等待成功
ChannelFuturef=b.bind(port).sync();
//等待服务端监听端口关闭
f.channel().closeFuture().sync();
}catch (Exception e) {
//TODO: handle exception
}finally{
//优雅退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
privateclass ChildChannelHandler extends ChannelInitializer <SocketChannel> {
@Override
protectedvoid initChannel(SocketChannel arg0) throws Exception{
// 增加2个解码器:LineBasedFrameDecoder和StringDecoder
arg0.pipeline().addLast(newLineBasedFrameDecoder(1024));
arg0.pipeline().addLast(newStringDecoder());
arg0.pipeline().addLast(newTimeServerHandlerNoStick());
}
}
publicstatic void main(String[] args) throws Exception{
intport=8080;
if(args!=null&&args.length>0){
try{
port=Integer.valueOf(args[0]);
}catch (NumberFormatException e) {
//TODO: handle exception
}
}
newTimeServerNoStick().bind(port);
}
}
2.1.2 TimeServerHandler实现
/*
* 利用LineBasedFrameDecoder解决TCP粘包问题
*/
public class TimeServerHandlerNoStickextends ChannelHandlerAdapter{
privateint counter;
@Override
publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
// 可以发现接收到的msg就是删除回车换行符后的请求消息,
//不需要额外考虑处理读半包问题,也不需要对请求消息进行编码
Stringbody=(String) msg;
System.out.println("The time server receiveorder:"+body+";the counter is"+ ++counter);
StringcurrentTime="QUERY TIME ORDER".equalsIgnoreCase(body)?
newjava.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";
ByteBufresp=Unpooled.copiedBuffer(currentTime.getBytes());
ctx.writeAndFlush(resp);
}
@Override
publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
ctx.close();
}
}
2.2 支持TCP粘包的客户端开发
2.2.1 TimeClient实现
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;
importio.netty.channel.socket.SocketChannel;
importio.netty.channel.socket.nio.NioSocketChannel;
importio.netty.handler.codec.LineBasedFrameDecoder;
importio.netty.handler.codec.string.StringDecoder;
/*
* 利用LineBasedFrameDecoder解决TCP粘包问题
*/
public class TimeClientNoStick {
publicvoid connect(int port,String host) throws Exception{
//配置客户端NIO线程组
EventLoopGroupgroup=new NioEventLoopGroup();
try{
Bootstrapb=new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(newChannelInitializer<SocketChannel>() {
@Override
publicvoid initChannel(SocketChannel ch) throws Exception{
ch.pipeline().addLast(newLineBasedFrameDecoder(1024));
ch.pipeline().addLast(newStringDecoder());
ch.pipeline().addLast(newTimeClientHandlerNoStick(1024));
}
});
//发起异步连接操作
ChannelFuturef=b.connect(host,port).sync();
//等待客户端链路关闭
f.channel().closeFuture().sync();
}catch (Exception e) {
//TODO: handle exception
}finally{
//优雅退出,释放NIO线程组
group.shutdownGracefully();
}
}
publicstatic void main(String[] args) throws Exception{
intport=8080;
if(args!=null&&args.length>0){
try{
port=Integer.valueOf(args[0]);
}catch (NumberFormatException e) {
//TODO: handle exception
}
newTimeClient().connect(port,"127.0.0.1");
}
}
}
2.2.2 TimeClientHandler的实现
import java.util.logging.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
importio.netty.channel.ChannelHandlerAdapter;
importio.netty.channel.ChannelHandlerContext;
/*
* 利用LineBasedFrameDecoder和StringDecoder解决TCP粘包问题
*/
public class TimeClientHandlerNoStickextends ChannelHandlerAdapter{
privatestatic final Logger logger=Logger.getLogger(TimeClientHandler.class.getName());
privateint counter;
privatebyte[] req;
publicTimeClientHandlerNoStick(){
req=("QUERYTIME ORDER"+System.getProperty("line.separator")).getBytes();
}
@Override
publicvoid channelActive(ChannelHandlerContext ctx){
ByteBufmessage=null;
for(inti=0;i<100;i++){
message=Unpooled.buffer(req.length);
message.writeBytes(req);
ctx.writeAndFlush(message);
}
}
@Override
publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{
//此时msg已经是解码成字符串之后的应答消息
Stringbody=(String) msg;
System.out.println("Nowis:"+body+";the counter is"+ ++counter);
}
@Override
publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
//释放资源
logger.warning("Unexpectedexception from downstream:"+cause.getMessage());
ctx.close();
}
}
三.运行支持TCP粘包的时间服务器程序
为了尽量模拟TCP粘包的半包场景,采用简单的压力测试,链路建立成功之后,客户端连续发送100条消息给服务端,然后查看服务端和客户端运行结果,分别运行TimeServer和TimeClient,服务端执行结果为:
。。。
客户端执行结果为:
。。。
运行结果完全符合预期,说明通过使用LinedBasedFrameDecoder和StringDecoder成功解决了TCP粘包导致的读半包问题。
Netty解决TCP粘包/拆包导致的半包读写问题相关推荐
- netty解决TCP粘包/拆包导致的半包读写问题的三种方案
解决方案一:LineBasedFrameDecoder+StringDecoder来解决TCP的粘包/拆包问题 只需要在客户端和服务端加上45.46两行代码并且在发送消息的时候加上换行符即可解决TCP ...
- Netty学习总结(5)——Netty之TCP粘包/拆包问题的解决之道
无论是服务端还是客户端,读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议. 流:没有界限的一串数据.如同河里的流水,它们是连成 ...
- 【Netty】TCP粘包和拆包
一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...
- Netty解决TCP的粘包和分包(二)
2019独角兽企业重金招聘Python工程师标准>>> Netty解决TCP的粘包和分包(二) 使用LengthFieldBasedFrameDecoder解码器分包 先看一下这个类 ...
- golang解决TCP粘包问题
6行代码解决golang TCP粘包 转自:https://studygolang.com/articles/12483 什么是TCP粘包问题以及为什么会产生TCP粘包,本文不加讨论.本文使用gola ...
- c#解决TCP“粘包”问题
c#解决TCP"粘包"问题 参考文章: (1)c#解决TCP"粘包"问题 (2)https://www.cnblogs.com/wangjun8868/p/71 ...
- Linux socket编程(一):客户端服务端通信、解决TCP粘包
一.服务端程序 服务端程序工作流程: 创建socket →\rightarrow→ 绑定监听的IP地址和端口 →\rightarrow→ 监听客户端连接 →\rightarrow→ 接受/发送数据.对 ...
- 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)
一.粘包/拆包概念 TCP是一个"流"协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的 ...
- Netty 之 TCP粘包拆包场景
转自:http://blog.csdn.net/z69183787/article/details/52595980 TCP编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输 ...
最新文章
- Android初级开发笔记-- activity启动模式的学习(1)
- python实现gif动画(matplotlib、imageio、pillow))
- Oracle学习:序列
- (转载)python多行注释
- 快速理解编程结构_为什么系统编程全都用英文而不用中文?听完专家的解释恍然大悟!...
- 何必言精通——十年杂感
- 双重检查锁实现单例模式的线程安全问题
- 数据库事务(Database Transaction)
- X5档案-参加业务架构平台研讨会后记
- 两平面平行但不重合的条件是_____2012江苏省数学竞赛《提优教程》教案:第77讲_组合几何...
- mysql,oracle,sql server数据库默认的端口号,端口号可以为负数吗?
- 雷电html查看程序编辑程序,NC程序编辑器(nEditor)
- 计算机护眼模式怎么设置的,为你解答电脑护眼设置怎么开
- 解决jdk证书问题-生成jssecacerts PKIX path building failed
- 程序开发类本科论文结构【2022年修改】
- 基于PHP的聚合数据车辆违章查询接口调用代码示例
- OpenGL初探:二维卡通人物交互设计
- selenium下Edge()配置错误问题
- android工程师培训价格,徐州android工程师培训基地
- win_server_2008x64升级powershell到4.0
热门文章
- 库 01_EOS 普元 EOS Platform 7.6 开发版安装时没装coframe,后续再部署coframe源码的操作步骤
- spring boot新建报错,多处显示导包失败,显示犹如The import org.junit cannot be resolved报错
- Error creating bean with name ‘servletEndpointRegistrar‘ defined in class path resource
- 关于安装双系统时多重引导的说明
- bzoj2754【SCOI2012】喵星球上的点名
- 【工作感悟】路自在前,冷暖自知
- Linux中防火墙firewalld
- java计算机毕业设计网上购物商城源代码+数据库+系统+lw文档
- 挑战UnityShader学习之八——用最简单的方法实现下雨天积水效果
- 飞行的小鸟论文python_80+行代码实现简单的“飞行的小鸟”游戏