最简单的 NIO 粘包解析与分析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 最简单的 NIO 粘包解析与分析

前言

黏包 是指网络上有多条数据发送给服务端,  但是由于某种原因这些数据在被接受的时候进行了重新组合, 这就是黏包, 本篇文章用来演示一种最简单的黏包解析方法, 适用于初初初级选手

正常来讲客户端发送给服务端的消息, 都是存在专门的通讯协议的, 为了避免黏包现象, 我们通常有几种方式去制定相应的规则: 消息长度固定, 特定分隔符, 消息长度固定+特定分隔符

本文是采用了 特定分隔符 的方式, 每条数据包都以 \n 结尾

例如以下三条原始数据数据:
hell\n
ningxuan\n
thanks\n
变成了以下两个:
hello\nningxuan\nth
anks\n
这就是黏包
复制代码

黏包产生的原因

socket 网络编程中, TCPUDP 分别是面向连接和非面相连接的. 但是他们都存在产生黏包问题吗?

本文不会对 tcp 和 udp 进行详细的讲解, 感兴趣的可以自行百度或者掘金

tcp

先说结论: tcp 会产生黏包问题

由于 tcp 协议本身的机制(面向连接的可靠性协议-三次握手机制) 客户端与服务端会维持一个连接(Channel), 数据在连接不断开的情况下, 可以将多个数据包持续不断的发送到服务器上.

但是如果发送的网络数据包太小, tcp就会启用Nagle算法对多个数据包进行合并再发送到服务器上. 这种情况下服务器在接收到消息的时候无法区分哪些数据包是分开的, 所以产生了黏包

还有一种可能是: 服务器在接收到数据之后, 将数据放入到缓冲区中, 如果消息没有被及时的从缓冲区取走, 下次在取数据的时候就会出现一次取到多个数据包的情况, 造成黏包现象

tcp三次握手:

  • 客户端服务端发送建立通道请求
  • 服务端客户端发送允许客户端建立一个单向的数据通道; 服务端向客户端发送建立通道请求
  • 客户端服务端发送允许服务端建立一个单向的数据通道
    此时数据通道是双向的, 允许客户端、服务端互相发送消息

Nagle算法:

  • 如果包长度达到 MSS, 则允许发送
  • 如果该包中含有 FIN, 则允许发送
  • 设置了 TCP_NODELAY 选项, 若所有发出去的小数据包(长度小于 MSS )均被确认, 则允许发送
  • 若上述条件均未满足, 但发送了超时(一般为 200ms ), 则立即发送

udp

upd 不存在黏包问题

udp本身是无连接的不可靠传输协议, 不会对数据包进行合并发送, 也就没有Nagle算法, 不会存在数据合并的情况, 每一个数据包都是完整的, 所以不存在黏包现象

最简单的黏包解析

黏包解析也很简单:

  • 遍历当前的 ByteBuffer 缓冲区
  • 判断元素为 '\n' 的下标
  • 生成新的 ByteBuffer 缓冲区
  • 将起始下标到标记下标的字符写到新的缓冲区

具体代码如下所示:

public class ByteBufferTest {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(32);
        buffer.put("hello\nningxuan\nth".getBytes());
        split(buffer);
        buffer.put("anks\n".getBytes());
        split(buffer);
    }
    private static void split(ByteBuffer buffer){
        // 将 buffer 切换为 读模式
        buffer.flip();
        // 根据 buffer 当前的长度进行遍历
        for (int i = 0; i < buffer.limit(); i++) {
            // 判断当前下标元素是不是数据包切割符 \n
            if (buffer.get(i) == '\n'){ // 注意这个时候 buffer 的 position 属性一直为 0
                // 计算当前数据包长度
                int length = i + 1 - buffer.position();
                // 根据当前数据包长度, 动态生成新的 缓冲区
                ByteBuffer target = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    target.put(buffer.get());   // 注意这个时候 buffer 的 position 属性在 ++
                }
                // 打印 target 当前的元素和属性
                ByteBufferUtils.selectAll(target);
            }
        }
        buffer.compact();
    }
}
复制代码




目录
相关文章
|
3月前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
458 2
|
2月前
|
数据采集 自然语言处理 搜索推荐
基于qwen2.5的长文本解析、数据预测与趋势分析、代码生成能力赋能esg报告分析
Qwen2.5是一款强大的生成式预训练语言模型,擅长自然语言理解和生成,支持长文本解析、数据预测、代码生成等复杂任务。Qwen-Long作为其变体,专为长上下文场景优化,适用于大型文档处理、知识图谱构建等。Qwen2.5在ESG报告解析、多Agent协作、数学模型生成等方面表现出色,提供灵活且高效的解决方案。
213 49
|
2月前
|
测试技术 开发者 Python
使用Python解析和分析源代码
本文介绍了如何使用Python的`ast`模块解析和分析Python源代码,包括安装准备、解析源代码、分析抽象语法树(AST)等步骤,展示了通过自定义`NodeVisitor`类遍历AST并提取信息的方法,为代码质量提升和自动化工具开发提供基础。
82 8
|
1月前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
62 4
|
2月前
|
数据采集 存储 自然语言处理
基于Qwen2.5的大规模ESG数据解析与趋势分析多Agent系统设计
2022年中国上市企业ESG报告数据集,涵盖制造、能源、金融、科技等行业,通过Qwen2.5大模型实现报告自动收集、解析、清洗及可视化生成,支持单/多Agent场景,大幅提升ESG数据分析效率与自动化水平。
131 0
|
3月前
|
存储 SQL 分布式计算
湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
212 1
|
4月前
|
存储 缓存 自然语言处理
深度解析ElasticSearch:构建高效搜索与分析的基石
【9月更文挑战第8天】在数据爆炸的时代,如何快速、准确地从海量数据中检索出有价值的信息成为了企业面临的重要挑战。ElasticSearch,作为一款基于Lucene的开源分布式搜索和分析引擎,凭借其强大的实时搜索、分析和扩展能力,成为了众多企业的首选。本文将深入解析ElasticSearch的核心原理、架构设计及优化实践,帮助读者全面理解这一强大的工具。
339 7
|
6月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
121 11
|
4月前
|
监控 安全 网络安全
恶意软件分析:解析与实践指南
【8月更文挑战第31天】
263 0
|
5月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
171 0

推荐镜像

更多