Netty通信中的粘包半包问题(一)

前言

我们在日常开发过程中,客户端和服务端的连接大多使用的是TCP协议,因为我们要保证数据的可靠传输,
当网络中出现丢包时要求,要求数据包的发送端重传给接收端。而TCP是一种面向连接的传输层协议,
当使用TCP进行传输时,客户端和服务端会各自维护两个缓冲区,它们分别是发送缓冲区、接收缓冲区,如图所示

我们在日常开发过程中,客户端和服务端的连接大多使用的是TCP协议,因为我们要保证数据的可靠传输,当网络中出现丢包时要求,要求数据包的发送端重传给接收端。而TCP是面向
在网络传输过程中,虽然对要发送的数据包大小没有要求,但是TCP又不可能一次性的把数据全部加载到
发送缓冲区中,这样会有可能撑爆TCP的发送缓冲区,比如说你要发送个1G的数据给服务端,TCP本身是不会
每次根据你要发送的数据包大小划定缓冲区大小的。但是它会收数据链路层的协议限制,
因为数据链路层是服务于传输层协议的,我们需要了解数据链路层当中传输的最大单元
在这里插入图片描述
MTU(Maximum Transmission Unit)最大传输单元,用来通知对方所能接受数据服务单元的最大尺寸,说明发送方能能够接受的有效载荷大小,另外传输层还会进行一个MSS大小的TCP分段。MSS是最大报文长度的缩写,MSS是TCP报文段中的数据字段的最大长度。数据字段加上TCP首部才等于整个的TCP报文段,所以MSS并不是TCP报文的最大长度,而是MSS=TCP报文长度 - TCP首部长度,在以太网中MTU是1500个字节,其中TCP报头占20个字节,IP包头占20个字节,剩下的大小才是我们能在发送的有效数据包大小,也就是1500-20-20=1460个字节,
也就是说,如果没有进行过调整,当你发送的数据包小于1460个字节的时候,客户端在发送缓冲区里放下你的报文时还会有空余,但是不会立马发送,而是会等待缓冲区达到一个阈值时再发送,这个时候就会出现粘包现象,因为TCP在发送缓冲区会存在其他报文的数据,看着就像粘在一起一样。反过来讲,当你要发送的数据包大于这个发送缓冲区的大小时,将会留到下一次发送缓冲区填充,相当于一个报文被拆分了多次进行发送.这两种现象会导致服务端必须要等待接收到完整数据报文才可以进行处理,不然就得现将数据暂存到内存中,影响处理效率,会影响服务端整体的吞吐量,因为服务端需要维持报文的中间状态,维护报文之间的顺序关系,增加了服务端的工作量

下面我以代码的形式复现一下:

1.Client

package splicing.demo;import constant.Constant;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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 java.net.InetSocketAddress;public class EchoCliStickyHalf {private final int port;private final String host;public EchoCliStickyHalf(int port, String host) {this.port = port;this.host = host;}public void start() throws InterruptedException {// 线程组EventLoopGroup group = new NioEventLoopGroup();try {// 客户端启动必备, 和服务器的不同点Bootstrap b = new Bootstrap();b.group(group)// 指定使用NIO的通信模式.channel(NioSocketChannel.class)// 指定服务器的IP地址和端口,和服务器的不同点.remoteAddress(new InetSocketAddress(host, port))// 和服务器的不同点.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new EchoCliStickyHalfHandler());}});// 异步连接到服务器,sync()会阻塞到完成,和服务器的不同点ChannelFuture f = b.connect().sync();// 阻塞当前进程,直到客户端的channel被关闭f.channel().closeFuture().sync();} finally {group.shutdownGracefully().sync();}}public static void main(String[] args) throws InterruptedException {new EchoCliStickyHalf(Constant.DEFAULT_PORT, Constant.DEFAULT_SERVER_IP).start();}
}
package splicing.demo;import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;import java.util.concurrent.atomic.AtomicInteger;public class EchoCliStickyHalfHandler extends SimpleChannelInboundHandler<ByteBuf> {private AtomicInteger counter = new AtomicInteger(0);/*** 客户端读取到网络数据后的处理*/@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {System.out.println("client Accept[" + byteBuf.toString(CharsetUtil.UTF_8) +"] and the counter is :" + counter.incrementAndGet()); }@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {String request = "Mark,Zhuge,Fox,Zhouyu,Loulan" + System.getProperty("line.separator");ByteBufAllocator alloc = ctx.alloc();ByteBuf msg = null;// 我们希望服务器接收到100个这样的报文for (int i = 0; i < 100; i++) {ByteBuf byteBuf = alloc.buffer();msg = alloc.buffer(request.length());msg.writeBytes(request.getBytes());ctx.writeAndFlush(msg);}
//        super.channelActive(ctx);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();
//        super.exceptionCaught(ctx, cause);}
}
import java.util.Date;/*** 常量*/
public class Constant {public static final Integer DEFAULT_PORT = 7777;public static final String DEFAULT_SERVER_IP= "127.0.0.1";// 根据输入信息拼接出一个应答信息public static String response(String msg) {return "Hello, " + msg + ", Now is" + new Date(System.currentTimeMillis()).toString(); }
}

2.Server

package splicing.demo;import constant.Constant;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.net.InetSocketAddress;public class EchoSvrStickyHalf {private static final Logger LOG = LoggerFactory.getLogger(EchoSvrStickyHalf.class);private final int port;public EchoSvrStickyHalf(int port) {this.port = port;}public static void main(String[] args) throws InterruptedException {EchoSvrStickyHalf echoSvrStickyHalf =new EchoSvrStickyHalf(Constant.DEFAULT_PORT);LOG.info("服务器即将启动");echoSvrStickyHalf.start();LOG.info("服务器关闭");}public void start() throws InterruptedException {// 线程组EventLoopGroup group = new NioEventLoopGroup();try {// 服务端启动必备ServerBootstrap b = new ServerBootstrap();b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {socketChannel.pipeline().addLast(new EchoSvrStickyHalfHandler());}});// 异步绑定到服务器,sync()会阻塞到完成ChannelFuture f = b.bind().sync();LOG.info("服务器启动完成。");// 阻塞当前线程,直到服务器的ServerChannel被关闭f.channel().closeFuture().sync();} finally {group.shutdownGracefully().sync();}}}
package splicing.demo;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;import java.util.concurrent.atomic.AtomicInteger;public class EchoSvrStickyHalfHandler extends ChannelInboundHandlerAdapter {private AtomicInteger counter = new AtomicInteger(0);@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf in = (ByteBuf)msg;String request = in.toString(CharsetUtil.UTF_8);System.out.println("Server Accept[" + request + "] and the counter is :" + counter.incrementAndGet() );String resp = "Hello, " +request + ". Welcome to Netty World" + System.getProperty("line.separator");ctx.writeAndFlush(Unpooled.copiedBuffer(resp.getBytes()));
//        super.channelRead(ctx, msg);}/*** 发生异常后的处理* @param ctx* @param cause* @throws Exception*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);cause.printStackTrace();ctx.close();}
}

3.分析

代码中明明发送了100个报文,结果服务端收到的报文个数,却是两个报文,中间的某一个报文还发生了半包,
下期我们再来讨论如何解决这种现象
在这里插入图片描述

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/235850.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【软件测试】学习笔记-设计GUI自动化测试策略

这篇文章从“实战”这个角度展开&#xff0c;探讨实际的大型全球化电商网站的GUI自动化测试如何开展。这场实战&#xff0c;从以下两个方面展开&#xff1a; 测试策略如何设计&#xff1f;这一点&#xff0c;我会根据亲身经历的实际项目&#xff0c;和你探讨GUI测试的分层测试…

使用CLIP和LLM构建多模态RAG系统

在本文中我们将探讨使用开源大型语言多模态模型(Large Language Multi-Modal)构建检索增强生成(RAG)系统。本文的重点是在不依赖LangChain或LLlama index的情况下实现这一目标&#xff0c;这样可以避免更多的框架依赖。 什么是RAG 在人工智能领域&#xff0c;检索增强生成(re…

每日一题——LeetCode1103.分糖果 ||

方法一 个人方法&#xff1a; 有多少人就创建多大的数组并把数组的所有元素初始化为0&#xff0c;只要还有糖果&#xff0c;就循环给数组从头到尾添加糖果&#xff0c;每次分的糖果数递增1&#xff0c;最后可能刚好分完也可能不够&#xff0c;不够就还剩多少给多少。 var dis…

作业--day45

定时播放 #include "mywidget.h" #include "ui_mywidget.h"MyWidget::MyWidget(QWidget *parent) :QWidget(parent),ui(new Ui::MyWidget) {ui->setupUi(this);ui->bg_lab->setPixmap(QPixmap(":/pictrue/shanChuan.jpg"));ui->bg_…

Leetcode2981. 找出出现至少三次的最长特殊子字符串 I

Every day a Leetcode 题目来源&#xff1a;2981. 找出出现至少三次的最长特殊子字符串 I 解法1&#xff1a;滑动窗口 暴力枚举 滑动窗口枚举窗口内字符相同的字符串&#xff0c;再暴力枚举长度相等的字符串。 代码&#xff1a; /** lc appleetcode.cn id2981 langcpp**…

国标28181平台的手机视频监控客户端的电子地图功能对比

目 录 一、手机客户端 1、概述 2、具体功能简述 二、电子地图功能 1、经纬度定位 2、附近设备 3、实时浏览功能 4、录像回放 5、缩放功能 三、手机web客户端和CS客户端上的电子地图功能对比 1、对比表 2、测距&#xff08;PC客户端功能&#xff09; 3…

精品公式——“V型反转”,精准把握V型反转行情,主副图分享

► 日线表现 代码评估 技术指标代码评估&#xff1a; M5, M14, M25 - 指数移动平均线&#xff08;EMA&#xff09;: M5:EMA(C,5),COLORLIBLUE;&#xff1a;5日指数移动平均线&#xff0c;用浅蓝色表示。 M14:EMA(C,13),COLORF00FF0;&#xff1a;13日指数移动平均线&#xff…

OpenHarmony—开发环境搭建

背景 因为没有实体的开发硬件&#xff0c;且不想破坏原有的Linux环境&#xff0c;所以这里基于 Docker QEMU 搭建开发环境 宿主机Linux系统命令行方式DockerQEMU 6.2 Docker环境准备 安装Docker 在Ubuntu中&#xff0c;可以使用下面的命令来安装Docker&#xff1a; sudo …

【软件测试】学习笔记-从0到1:API测试怎么做

这篇文章是API测试的基础&#xff0c;先从0到1设计一个API测试用例&#xff0c;通过这个测试用例&#xff0c;体会到最基本的API测试是如何进行的&#xff0c;并介绍几款常用的API测试工具。 API测试的基本步骤 通常来讲&#xff0c;无论采用什么API测试工具&#xff0c;API测…

用于生成信息提取的大型语言模型综述

论文地址&#xff1a;https://arxiv.org/pdf/2312.17617.pdf 代码仓库&#xff1a;https://github.com/quqxui/Awesome-LLM4IE-Papers 信息抽取&#xff08;IE&#xff09;旨在从纯自然语言文本中提取结构化知识&#xff08;如实体、关系和事件&#xff09;。最近&#xff0c…

第十一章 Cookie

第十一章 Cookie 1.什么是Cookie2.Cookie的创建3.Cookie的获取4.Cookie值的修改5.谷歌浏览器和火狐浏览器如何查看Cookie6.Cookie的存活设置7.Cookie的path属性8.Cookie练习之免用户名登入 1.什么是Cookie 2.Cookie的创建 下面我看看如何创建Cookie&#xff0c;如何让客户端保…

视频监控设备通过onvif协议接入到视频监控平台

目 录 一、什么是onvif规范 1、onvif的定义 2、onvif的优势 二、AS-V1000监控平台对onvif的支持程度 二、通过onvif接入视频监控设备 1、onvif维护主页面 2、设备发现 3、设备验证 4、设备录入系统 5、通道配置 6、权限分配 三、对onvif设备进行…

AD软件与其他EDA软件工程的问题汇总

1:如何在AD中使用eagle工程 在ad中打不开原理图&#xff0c;要使用导入功能,转化为ad的文件后&#xff0c;就可以打开了 2:打开旧版本的Protel文件 有时候新版本的AD打不开以前Protel的PCB文件&#xff0c;可以在DXP菜单下的Extension下进行配置&#xff08;Configure&…

回归预测 | Matlab基于SMA+WOA+SFO-LSSVM多输入单输出回归预测

回归预测 | Matlab基于SMAWOASFO-LSSVM多输入单输出回归预测 目录 回归预测 | Matlab基于SMAWOASFO-LSSVM多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 SMAWOASFO-LSSVM回归预测 基于黏菌算法鲸鱼算法向日葵算法优化LSSVM回归预测 其中包含三种改进…

国产系统-银河麒麟桌面版安装wps

0安装版本 系统版本 版本名称:银河麒麟桌面版操作系统V10(SP1) 软件版本 wps个人版2019 1双击安装 1.1卸载自带wps 为什么要卸载没有序列号,授权过期,不是免费的,通过先安装/在升级个人版跳过输入序列号问题等等原因 1.1.1当前自带的wps版本 1.1.2卸载 不卸载无法安装在…

[Kubernetes]9. K8s ingress讲解借助ingress配置http,https访问k8s集群应用

前面讲解了使用Helm部署mysql集群,这里来看看使用Ingress搭建负载均衡功能 1.介绍 功能类似 Nginx ,可以根据域名、路径把请求转发到不同的 Service , Ingress 为外部访问集群提供了一个 统一 入口, 避免 了 对外暴露集群端口 ,可以配置 https,http访问集群应用,接下来看看如…

React.Children.map 和 js 的 map 有什么区别?

JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行处理&#xff0c;而 React.Children.map 中的 map 可以处理 React.Children 为 null 或者 undefined 的情况。 React 空节点&#xff1a;可以由null、undefined、false、true创建 import React from reactexport …

参数小,性能强!开源多模态模型—TinyGPT-V

安徽工程大学、南洋理工大学和理海大学的研究人员开源了多模态大模型——TinyGPT-V。 TinyGPT-V以微软开源的Phi-2作为基础大语言模型&#xff0c;同时使用了视觉模型EVA实现多模态能力。尽管TinyGPT-V只有28亿参数&#xff0c;但其性能可以媲美上百亿参数的模型。 此外&…

【Docker基础二】Docker安装Mysql8

下载镜像 安装mysql&#xff08;版本&#xff1a;8.0.35&#xff09; # 拉取镜像 docker pull mysql:8.0.35 # 查看镜像是否已经下载 docker images 创建挂载目录 # 宿主机上创建挂载目录 (可以不创建&#xff0c;docker run -v配置了挂载目录&#xff0c;docker会自动…

Nginx配置反向代理实例二

Mac 安装Nginx教程 Nginx配置反向代理实例一 提醒一下&#xff1a;下面实例讲解是在Mac系统演示的&#xff1b; 反向代理实例二实现的效果 使用nginx 反向代理&#xff0c;根据访问的地址跳转到不同端口的服务中 nginx 监听端口为81&#xff1b; 访问地址1&#xff1a;http:/…