Java网络编程(二)经典案例[粘包拆包]
粘包拆包
概述
TCP是面向流的协议,TCP在网络上传输的数据就是一连串的数据,完全没有分界线。
TCP协议的底层并不了解上层业务的具体定义,它会根据TCP缓冲区的实际情况进行包的划分。
在业务层面认为一个完整的包可能会被TCP拆分成多个小包进行发送,也可能把多个小的包封装成一个大的数据包进行发送,这就是所谓的TCP粘包拆包问题,如上图会存在多种情况
原因分析
TCP数据流最终发到目的地,必须通过以太网协议封装成一个个的以太网帧发送出去,以太网数据帧大小最小64字节,最大1518字节,除去header部分,其数据payload为46到1500字节。所以如果以太网帧的payload大于MTU(默认1500字节)就需要进行拆包
解决方案
由于TCP协议底层无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,所以该问题只能通过上层的应用层协议设计来解决,常见方案如下:
- 消息定长,发送方和接收方规定固定大小的消息长度,例如每个报文大小固定为200字节,如果不够,空位补空格
- 增加特殊字符进行分割,例如FTP协议
- 自定义协议,将消息分为消息头和消息体,消息头中包含消息总长度,这样服务端就可以知道每个数据包的具体长度了,知道了发送数据包的具体边界后,就可以解决粘包和拆包问题了
Netty解决方案
基于JDK原生的socket或者nio解决粘包拆包问题比较麻烦;作为一款非常强大的网络通信框架,Netty提供了多种编码器用于解决粘包拆包问题,只要掌握这些类库的使用即可解决粘包拆包问题,常见的类库包括:
- LineBasedFrameDecoder:基于行的解码器,遇到 “\n”、"\r\n"会被作为行分隔符
- FixedLengthFrameDecoder:基于固定长度的解码器
- DelimiterBasedFrameDecoder:基于分隔符的帧解码器
- ByteToMessageDecoder:处理自定义消息协议的解码器
- ReplayingDecoder:继承ByteToMessageDecoder,网络缓慢并且消息格式复杂时可能慢于ByteToMessageDecoder
自定义消息协议+编码/解码器解决粘包拆包问题实战
Netty中使用自定义协议(本质就是解决服务器端每次读取数据长度的问题) + 编码解码器来解决
客户端
package com.bierce.io.netty.stickingAndunpacking;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class MyStickingUnpackingClientTest {private String host;private int port;public MyStickingUnpackingClientTest(String host, int port){this.host = host;this.port = port;}public void run(){NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new MyMessageEncoder()); //自定义编码器pipeline.addLast(new MyMessageDecoder()); //自定义解码器pipeline.addLast(new MyStickingUnpackingClientHandler()); //自定义业务处理器}});ChannelFuture cf = bootstrap.connect(host, port).sync();Channel channel = cf.channel();System.out.println("北京时间-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")) + ": " + "Client"+ channel.localAddress()+ " start Successful!!!");cf.channel().closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {eventLoopGroup.shutdownGracefully();}}public static void main(String[] args) {new MyStickingUnpackingClientTest("127.0.0.1", 9999).run();}
}
class MyStickingUnpackingClientHandler extends SimpleChannelInboundHandler<MessageProtocal> {private int count;@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {for (int i = 0; i < 5; i++) {String msg = "Hello Bierce";byte[] data = msg.getBytes(Charset.forName("UTF-8"));int length = msg.getBytes(Charset.forName("UTF-8")).length;MessageProtocal msgProtocal = new MessageProtocal();msgProtocal.setData(data);msgProtocal.setLength(length);ctx.writeAndFlush(msgProtocal);}}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, MessageProtocal msg) throws Exception {byte[] data = msg.getData();int length = msg.getLength();System.out.println("客户端接收到服务端响应信息包括:");System.out.println("长度=" + length + ";内容=" + new String(data, Charset.forName("UTF-8")) + ";消息包数量=" + (++this.count));}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {System.out.println("异常消息: " + cause.getMessage());ctx.close();}
}
服务端
package com.bierce.io.netty.stickingAndunpacking;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.nio.charset.Charset;
import java.util.UUID;
public class MyStickingUnpackingServerTest {private int port;public MyStickingUnpackingServerTest(int port){this.port = port;}public void run(){EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap serverBootstrap = new ServerBootstrap();serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new MyMessageDecoder()); //自定义解码器pipeline.addLast(new MyMessageEncoder()); //自定义编码器pipeline.addLast(new MyStickingUnpackingServerHandler());}});System.out.println("服务器启动成功!");serverBootstrap.bind(port).sync().channel().closeFuture().sync();} catch (InterruptedException e) {throw new RuntimeException(e);} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String[] args) {new MyStickingUnpackingServerTest(9999).run();}
}
class MyStickingUnpackingServerHandler extends SimpleChannelInboundHandler<MessageProtocal> {private int count;@Overrideprotected void channelRead0(ChannelHandlerContext ctx, MessageProtocal msg) throws Exception {int length = msg.getLength();byte[] data = msg.getData();System.out.println();System.out.println("服务器接收到的信息包括:");System.out.println("长度=" + length + ";内容=" + new String(data, Charset.forName("UTF-8")) + ";消息包数量=" + (++this.count));System.out.println("------------服务端读取成功------------");//回复给客户端信息String responseContent = UUID.randomUUID().toString();int responseLen = responseContent.getBytes("UTF-8").length;MessageProtocal msgProtocal = new MessageProtocal();msgProtocal.setLength(responseLen);msgProtocal.setData(responseContent.getBytes("UTF-8"));ctx.writeAndFlush(msgProtocal);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {System.out.println("异常消息:" + cause.getMessage());ctx.close();}
}
自定义消息协议类
package com.bierce.io.netty.stickingAndunpacking;
/*** 自定义消息服协议*/
public class MessageProtocal {private int length;private byte[] data;public int getLength() {return length;}public void setLength(int length) {this.length = length;}public byte[] getData() {return data;}public void setData(byte[] data) {this.data = data;}
}
自定义消息解码编码器类
package com.bierce.io.netty.stickingAndunpacking;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.ReplayingDecoder;
import java.util.List;
public class MyMessageDecoder extends ReplayingDecoder<Void> {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {System.out.println("MyMessageDecoder method called");int length = in.readInt();byte[] data = new byte[length];in.readBytes(data);MessageProtocal messageProtocal = new MessageProtocal();messageProtocal.setLength(length);messageProtocal.setData(data);out.add(messageProtocal);}
}
class MyMessageEncoder extends MessageToByteEncoder<MessageProtocal> {@Overrideprotected void encode(ChannelHandlerContext ctx, MessageProtocal msg, ByteBuf out) throws Exception {System.out.println("MyMessageEncoder method called");out.writeInt(msg.getLength());out.writeBytes(msg.getData());}
}
效果图
相关文章:
Java网络编程(二)经典案例[粘包拆包]
粘包拆包 概述 TCP是面向流的协议,TCP在网络上传输的数据就是一连串的数据,完全没有分界线。 TCP协议的底层并不了解上层业务的具体定义,它会根据TCP缓冲区的实际情况进行包的划分。 在业务层面认为一个完整的包可能会被TCP拆分成多个小包进行发送,也可能把多个小的包封装成一…...
无分布式锁的ID生成
起因 TEAM GARDEN 本来ID是自增的,后面发现自增ID比较麻烦,有问题: 不可控的间隔: 如果你在插入数据时,中途删除了一些行,导致自增的ID出现间隔,那么新插入的行会填充这些间隔,可能…...
X2000 Linux UVC
参考文档:\doc\开发使用说明\USB使用说明文档\设备\USB_UVC\xburst2\USB_UVC.pdf 一、内核添加USB UVC功能 1、确定所用dts文件 进入到/tools/iconfigtool/IConfigToolApp/路径下,执行./IConfigTool 选择config文件,查看kernel默认配置 配…...
HCIP-OpenStack组件之neutron
neutron(ovs、ovn) OVS OVS(Open vSwitch)是虚拟交换机,遵循SDN(Software Defined Network,软件定义网络)架构来管理的。 OVS介绍参考:https://mp.weixin.qq.com/s?__bizMzAwMDQyOTcwOA&mid2247485088&idx1…...
数学建模-常见算法(3)
KMP算法(Knuth-Morris-Pratt算法) KMP算法是一种用于字符串匹配的算法,它的时间复杂度为O(mn)。该算法的核心思想是在匹配失败时,利用已经匹配的信息,减少下一次匹配的起始位置。 def kmp(text, pattern): n len(…...
缓存的设计方式
问题情况: 当有大量的请求到内部系统时,若每一个请求都需要我们操作数据库,例如查询操作,那么对于那种数据基本不怎么变动的数据来说,每一次都去数据库里面查询,是很消耗我们的性能 尤其是对于在海量数据…...
CH02_重构的原则(什么是重构、为什么重构、何时重构)
什么是重构 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。 重构(动词):使用一系列重构手法࿰…...
26. 删除有序数组中的重复项(简单系列)
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k ,你需要做…...
【linux】基本指令(二)【man、echo、cat、cp】
目录 一、man指令二、echo指令三、cat指令二、cp指令一些常见快捷键 一、man指令 Linux的命令有很多参数,我们不可能全记住,可以通过查看联机手册获取帮助。访问Linux手册页的命令是 man 语法: man [选项] 命令 常用选项 1.-k 根据关键字搜索联机帮助 2…...
【视频】Python用LSTM长短期记忆神经网络对不稳定降雨量时间序列进行预测分析|数据分享...
全文下载链接:http://tecdat.cn/?p23544 在本文中,长短期记忆网络——通常称为“LSTM”——是一种特殊的RNN递归神经网络,能够学习长期依赖关系(点击文末“阅读原文”获取完整代码数据)。 本文使用降雨量数据…...
2023年7月京东空气净化器行业品牌销售排行榜(京东运营数据分析)
随着科技发展,智能家具在日常生活中出现的频率越来越高,许多曾经不被关注的家电也出现在其中,包括近年来逐渐兴起的空气净化器。伴随人们对自身健康的重视度越来越高,作为能够杀灭空气污染物、有效提高空气清洁度的产品࿰…...
原生小案例:如何使用HTML5 Canvas构建画板应用程序
使用HTML5 Canvas构建绘图应用是在Web浏览器中创建交互式和动态绘图体验的绝佳方式。HTML5 Canvas元素提供了一个绘图表面,允许您操作像素并以编程方式创建各种形状和图形。本文将为您提供使用HTML5 Canvas创建绘图应用的概述和指导。此外,它还将通过解释…...
Electron 报gpu_process_host.cc(951)] GPU process launch faile错误
解决方法,在入口js文件中,添加如下代码: app.commandLine.appendSwitch(no-sandbox)...
每天一分享#读up有感#
不知道开头怎么写,想了一下,要不,就这样吧,开头也就写完 今日分享 分享一博主的分享——https://blog.csdn.net/zhangay1998/article/details/121736687 全程高能,大佬就diao,一鸣惊人、才能卓越、名扬四…...
threejs贴图系列(一)canvas贴图
threejs不仅支持各种texture的导入生成贴图,还可以利用canvas绘制图片作为贴图。这就用到了CanvasTexture,它接受一个canas对象。只要我们绘制好canvas,就可以作为贴图了。这里我们利用一张图片来实现这个效果。 基础代码: impo…...
taro react/vue h5 中的上传input onchange 值得区别
<inputclassNamebase-input-file-h5typefileacceptimage/*capturecameraonChange{onChangeInput} />1、taro3react 2、taro3vue3...
(AcWing) 任务安排(I,II,III)
任务安排I: 有 N 个任务排成一个序列在一台机器上等待执行,它们的顺序不得改变。 机器会把这 N 个任务分成若干批,每一批包含连续的若干个任务。 从时刻 0 开始,任务被分批加工,执行第 i 个任务所需的时间是 Ti。 另外&#x…...
Excel筛选后复制粘贴不连续问题的解决
一直以来都没好好正视这个问题认真寻求解决办法 终于还是被需求逼出来了,懒人拯救世界[doge] 一共找到两个方法,个人比较喜欢第二种,用起来很方便 Way1:CtrlG定位可见单元格后使用vlookup解决(感觉不定位直接公式向下…...
【SCSS变量】$ | | var | @for | @include | @function | @each 等常用方法使用
SCSS优点:编写清晰、无冗余、语义化的CSS,减少不必要的重复工作 1、变量声明($)和使用2、使用 & 代替父元素3、在HTML中使用 :style{--name: 动态值}自定义属性,在SCSS中用var(--name)函数绑定动态变量值ÿ…...
iOS 17 及 Xcode 15.0 Beta7 问题记录
1、iOS 17 真机调试问题 iOS 17之后,真机调试Beta版本必须使用Beta版本的Xcode来调试,用以前复制DeviceSupport 方式无法调试,新的Beta版本Xcode中,已经不包含 iOS 17目录。如下图: 解决方案: 1&#x…...
docker-maven-plugin直接把镜像推到私有仓库
接着上篇 推送到本地docker 我们已经把服务做成镜像推到docker,也可以通过docker login 私有地址,去push。麻烦 直接上代码 1、pom改动 <properties><docker.registry>eco-registry.XXX.com</docker.repostory><docker.registry…...
2023年机器学习项目—布匹缺陷检测
2023年机器学习项目———布匹缺陷检测 测试环境: CPU : 12th Gen Intel Core™ i7-12700H 2.70 GHz GPU : NVIDIA RTX3070Ti RAM : 32GB Matlab R2020a (Deep Learning Tools) 注 :Data文件过大 未上传 一.神经网络概述 1. 卷积神经网络概念 人工神经网络(Artific…...
RabbitMQ---订阅模型分类
订阅模型分类 在之前的模式中,我们创建了一个工作队列。 工作队列背后的假设是:每个任务只被传递给一个工作人员。 在这一部分,我们将做一些完全不同的事情 - 我们将会传递一个信息给多个消费者。 这种模式被称为“发布/订阅”。 订阅模型示意…...
pycharm添加虚拟环境以及虚拟环境安装pytorch
file、settings、interpreter、add interpreter、add local interpreter 记住不要勾选inherit,不然会把主环境的东西继承到虚拟环境。 创建前可以先点existing看看有没有已经建好的虚拟环境 有的时候pycharm有问题,创建了虚拟环境没有显示。找一个.py文…...
Git企业开发控制理论和实操-从入门到深入(三)|分支管理
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…...
【VsCode】SSH远程连接Linux服务器开发,搭配cpolar内网穿透实现公网访问(1)
文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…...
LC-1267. 统计参与通信的服务器(枚举 + 计数)
1267. 统计参与通信的服务器 中等 这里有一幅服务器分布图,服务器的位置标识在 m * n 的整数矩阵网格 grid 中,1 表示单元格上有服务器,0 表示没有。 如果两台服务器位于同一行或者同一列,我们就认为它们之间可以进行通信。 请…...
Linux TCP协议——三次握手,四次挥手
一、TCP协议介绍 TCP协议是可靠的、面向连接的、基于字节流的传输层通信协议。 TCP的头部结构: 源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去;(tcp是传输层的协议,端与端之间的数据传输,在TCP和UDP协议当中不会体现出I…...
人机对抗智能-部分可观测异步智能体协同(POAC)
环境链接:数据中心-人机对抗智能 (ia.ac.cn)http://turingai.ia.ac.cn/data_center/show/10 1.环境配置 Ubuntu 20.04 Anaconda python版本3.6 1.1 安装torch0.4.1失败 参考文章: 安装torch0.4.1的神坑_torch0.4.1_DEMO_Tian的博客-CSDN博客 co…...
数学——七桥问题——图论
当涉及数学,有很多不同的话题可以讨论。你是否有特定的数学领域、概念或问题想要了解更多?以下是一些常见的数学领域和主题,你可以选择一个或者告诉我你感兴趣的具体内容,我将很乐意为你提供更多信息: 代数学ÿ…...
宁阳网站开发/如何建立企业网站
一、题目 二、题目分析 题目要求做一个电压测量监控设备,各个模块的使用都比较简单,但是当要利用各个模块完成一个简单的小项目,就要理清楚整个程序的大致框架。这里将整个功能分为三大部分显示界面,设置界面,串口接发…...
成都企业模版网站建设/软件培训班学费多少
嗨,未来的研究生们,早上好呀!在选学校这个问题上,所有同学都达成了共识,分数够清华北大就一定要报清华北大相对的,选专业这件事就相对复杂了如何用有限的分数选择前途无限的专业?赵本山曾在春晚极力推荐这个专业然而现…...
不登陆不收费的网站链接/网站内部seo
一. 场景 在测试过程中, 可能需要调用第三方jar包来生成测试数据或者使用java工具类来实现业务场景, 普遍的做法是手动调用jar包, 再把这些值赋给jmeter中的某个参数, 以满足业务测试需求目的, jmete有调用jar包的功能, 可以利用这个实现对jar包的嵌入调用. 二. 自定义jar包 …...
大庆市建设局网站上不去/重庆seo教程
1.根据营改增的相关规定,下列各项中,属于在中国境内提供应税服务的是()。 A.境外单位向境内单位提供完全在境外消费的应税服务 B.境外个人向境内个人出租完全在境外使用的有形动产 C.境内单位向境内个人提供完全在境外消费的应税…...
wordpress图片无法加载/推广软件排行榜前十名
本章的目的是编写一个完整字符设备驱动。例子代码来自于LDD3的scull范例。scull字符设备是对内存的操作,不涉及具体的硬件,所以理论上在任何一台机器上都可以测试,这很方便。4.1 scull的设计scull字符设备的驱动将默认对应4个scull设备(文件)…...
山西网站的公司/广州百度竞价外包
2019独角兽企业重金招聘Python工程师标准>>> 当前db4.6-util版本已经不好使,安装总提示失败,可直接换使用5.3版本: $sudo apt-get install db5.3-utils db5.3-utils主要用于针对Vsftpd虚拟用户的存储 sudo db4.6_load -T -t hash …...