目录

  • 一、 ByteBuffer消息粘包、消息半包的概述
  • 二、示例需求
  • 三、示例代码

一、 ByteBuffer消息粘包、消息半包的概述

  • NIO是面向缓冲区进行通信的,不是面向流的。既然是缓冲区,那它一定存在一个固定大小。这样一来通常会遇到两个问题:
  • 消息粘包:当缓冲区足够大,由于网络不稳定种种原因,可能会有多条消息从通道读入缓冲区,此时如果无法分清数据包之间的界限,就会导致粘包问题;
  • 消息半包:若消息没有接收完,缓冲区就被填满了,会导致从缓冲区取出的消息不完整,即半包的现象。

二、示例需求

网络上有多条数据发送给服务端,数据之间使用 \n 进行分隔,但由于某种原因这些数据在接收时,被进行了重新组合,例如原始数据有3条:

  • Hello,world\n
  • I’m zhangsan\n
  • How are you?\n

变成了下面的两个 byteBuffer (黏包,半包)

  • Hello,world\nI’m zhangsan\nHo
  • w are you?\n

现在要求你编写程序,将错乱的数据恢复成原始的按 \n 分隔的数据

三、示例代码

  • 需求代码

    package com.example.nettytest.nio.day1;import java.nio.ByteBuffer;
    import static com.example.nettytest.nio.day1.ByteBufferUtil.debugAll;
    /*** @description:* @author: xz* @create: 2022-07-26 21:10*/
    public class TestByteBufferExam {public static void main(String[] args) {//分配一个新的字节缓冲区,容量为50ByteBuffer sourceByteBuffer = ByteBuffer.allocate(50);//写入数据sourceByteBuffer.put("Hello,world\nI'm zhangsan\nHo".getBytes());split(sourceByteBuffer);//再次写入数据sourceByteBuffer.put("w are you?\n".getBytes());split(sourceByteBuffer);}/*** 将错乱的数据恢复成原始的按 \n 分隔的数据方法* */public static void split(ByteBuffer sourceByteBuffer){//flip 切换到读模式sourceByteBuffer.flip();for(int i = 0; i < sourceByteBuffer.limit(); i++){if(sourceByteBuffer.get(i) =='\n'){//找到一条完整消息//换行符索引+1-起始位置int length =i + 1- sourceByteBuffer.position();// 把此条完整消息存入新的 ByteBufferByteBuffer targetByteBuffer = ByteBuffer.allocate(length);for(int j=0;j<length;j++){targetByteBuffer.put(sourceByteBuffer.get());}//打印byteBuffer中所有内容debugAll(targetByteBuffer);}}//compact 把未读完的部分向前压缩,然后切换至写模式sourceByteBuffer.compact();}
    }
    
  • 输出ByteBuffer结构的工具类

    package com.example.nettytest.nio.day1;import io.netty.util.internal.StringUtil;import java.nio.ByteBuffer;import static io.netty.util.internal.MathUtil.isOutOfBounds;
    import static io.netty.util.internal.StringUtil.NEWLINE;public class ByteBufferUtil {private static final char[] BYTE2CHAR = new char[256];private static final char[] HEXDUMP_TABLE = new char[256 * 4];private static final String[] HEXPADDING = new String[16];private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];private static final String[] BYTE2HEX = new String[256];private static final String[] BYTEPADDING = new String[16];static {final char[] DIGITS = "0123456789abcdef".toCharArray();for (int i = 0; i < 256; i++) {HEXDUMP_TABLE[i << 1] = DIGITS[i >>> 4 & 0x0F];HEXDUMP_TABLE[(i << 1) + 1] = DIGITS[i & 0x0F];}int i;// Generate the lookup table for hex dump paddingsfor (i = 0; i < HEXPADDING.length; i++) {int padding = HEXPADDING.length - i;StringBuilder buf = new StringBuilder(padding * 3);for (int j = 0; j < padding; j++) {buf.append("   ");}HEXPADDING[i] = buf.toString();}// Generate the lookup table for the start-offset header in each row (up to 64KiB).for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i++) {StringBuilder buf = new StringBuilder(12);buf.append(NEWLINE);buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));buf.setCharAt(buf.length() - 9, '|');buf.append('|');HEXDUMP_ROWPREFIXES[i] = buf.toString();}// Generate the lookup table for byte-to-hex-dump conversionfor (i = 0; i < BYTE2HEX.length; i++) {BYTE2HEX[i] = ' ' + StringUtil.byteToHexStringPadded(i);}// Generate the lookup table for byte dump paddingsfor (i = 0; i < BYTEPADDING.length; i++) {int padding = BYTEPADDING.length - i;StringBuilder buf = new StringBuilder(padding);for (int j = 0; j < padding; j++) {buf.append(' ');}BYTEPADDING[i] = buf.toString();}// Generate the lookup table for byte-to-char conversionfor (i = 0; i < BYTE2CHAR.length; i++) {if (i <= 0x1f || i >= 0x7f) {BYTE2CHAR[i] = '.';} else {BYTE2CHAR[i] = (char) i;}}}/*** 打印所有内容* @param buffer*/public static void debugAll(ByteBuffer buffer) {int oldlimit = buffer.limit();buffer.limit(buffer.capacity());StringBuilder origin = new StringBuilder(256);appendPrettyHexDump(origin, buffer, 0, buffer.capacity());System.out.println("+--------+-------------------- all ------------------------+----------------+");System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), oldlimit);System.out.println(origin);buffer.limit(oldlimit);}/*** 打印可读取内容* @param buffer*/public static void debugRead(ByteBuffer buffer) {StringBuilder builder = new StringBuilder(256);appendPrettyHexDump(builder, buffer, buffer.position(), buffer.limit() - buffer.position());System.out.println("+--------+-------------------- read -----------------------+----------------+");System.out.printf("position: [%d], limit: [%d]\n", buffer.position(), buffer.limit());System.out.println(builder);}public static void main(String[] args) {ByteBuffer buffer = ByteBuffer.allocate(10);buffer.put(new byte[]{97, 98, 99, 100});debugAll(buffer);}private static void appendPrettyHexDump(StringBuilder dump, ByteBuffer buf, int offset, int length) {if (isOutOfBounds(offset, length, buf.capacity())) {throw new IndexOutOfBoundsException("expected: " + "0 <= offset(" + offset + ") <= offset + length(" + length+ ") <= " + "buf.capacity(" + buf.capacity() + ')');}if (length == 0) {return;}dump.append("         +-------------------------------------------------+" +NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +NEWLINE + "+--------+-------------------------------------------------+----------------+");final int startIndex = offset;final int fullRows = length >>> 4;final int remainder = length & 0xF;// Dump the rows which have 16 bytes.for (int row = 0; row < fullRows; row++) {int rowStartIndex = (row << 4) + startIndex;// Per-row prefix.appendHexDumpRowPrefix(dump, row, rowStartIndex);// Hex dumpint rowEndIndex = rowStartIndex + 16;for (int j = rowStartIndex; j < rowEndIndex; j++) {dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);}dump.append(" |");// ASCII dumpfor (int j = rowStartIndex; j < rowEndIndex; j++) {dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]);}dump.append('|');}// Dump the last row which has less than 16 bytes.if (remainder != 0) {int rowStartIndex = (fullRows << 4) + startIndex;appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);// Hex dumpint rowEndIndex = rowStartIndex + remainder;for (int j = rowStartIndex; j < rowEndIndex; j++) {dump.append(BYTE2HEX[getUnsignedByte(buf, j)]);}dump.append(HEXPADDING[remainder]);dump.append(" |");// Ascii dumpfor (int j = rowStartIndex; j < rowEndIndex; j++) {dump.append(BYTE2CHAR[getUnsignedByte(buf, j)]);}dump.append(BYTEPADDING[remainder]);dump.append('|');}dump.append(NEWLINE +"+--------+-------------------------------------------------+----------------+");}private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {if (row < HEXDUMP_ROWPREFIXES.length) {dump.append(HEXDUMP_ROWPREFIXES[row]);} else {dump.append(NEWLINE);dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));dump.setCharAt(dump.length() - 9, '|');dump.append('|');}}public static short getUnsignedByte(ByteBuffer buffer, int index) {return (short) (buffer.get(index) & 0xFF);}
    }
    
  • 输出结果

Netty——ByteBuffer(ByteBuffer消息粘包、半包示例)相关推荐

  1. 三、Netty的粘包半包问题解决

    一.定义 TCP 传输中,客户端发送数据,实际是把数据写入到了 TCP 的缓存中,粘包和半包也就会在此时产生.客户端给服务端发送了两条消息ABC和DEF,服务端这边的接收会有多少种情况呢?有可能是一次 ...

  2. TCP 粘包半包 netty 编解码 三者关系

    1 何为粘包 / 半包? 对方一次性接收了多条消息这种现象,我们就称之为 粘包现象. 对方多次接收了不完整消息这种现象,我们就称之为 半包现象. 粘包的原因: 发送方发送的消息 < 缓冲区大小 ...

  3. Netty框架之TCP粘包/半包解决方案

    Netty框架之TCP粘包/半包解决方案 一.TCP粘包 二.TCP半包 三.TCP粘包/半包解决方案 1.FixedLengthFrameDecoder定长解析器 2.LineBasedFrameD ...

  4. Netty粘包/半包问题解析

    目录 一.什么是粘包/半包问题 二.TCP粘包/半包发生的原因 三.粘包/半包解决办法 四.Netty中粘包/半包解决示例 1. 采用固定长度数据包编解码方式 2. 采用特殊字符作为边界字符编解码方式 ...

  5. Netty如何解决粘包半包问题

    何为粘包 / 半包? 比如,我们发送两条消息:ABC 和 DEF,那么对方收到的就一定是 ABC 和 DEF 吗? 不一定,对方可能一次就把两条消息接收完了,即 ABCDEF:也可能分成了好多次,比如 ...

  6. netty——黏包半包的解决方案、滑动窗口的概念

    黏包半包 滑动窗口 在深入理解黏包半包问题之前,先了解TCP的一个知识点--滑动窗口 我们都指定tcp是一种可靠的传输协议,这主要是因为在tcp中客户端给服务器端发送一条消息,要等待服务器端的应答,如 ...

  7. 网络:什么是TCP粘包/半包?怎么解决这个问题

    在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接.在TCP的socket编程中,发送端和接收端都有成对的socke ...

  8. websocket是否需要处理粘包半包问题分析

    结论: ​ 不需要. 背景: ​ 公司通信涉及到websocket相关,我们都知道websocket是基于tcp的,而tcp是面向字节流的,是需要处理粘包半包问题的.那么websocket是否需要处理 ...

  9. Http 调用netty 服务,服务调用客户端,伪同步响应.ProtoBuf 解决粘包,半包问题.

    实际情况是: 公司需要开发一个接口给新产品使用,需求如下 1.有一款硬件设备,客户用usb接上电脑就可以,但是此设备功能比较单一,所以开发一个服务器程序,辅助此设备业务功能 2.解决方案,使用Sock ...

  10. Netty(一)基础socketchannel,Buffer,selector黏包 半包解决 实战

    NIO 基础 non-blockiong io:非阻塞 阻塞vs非阻塞 三大组件 1.channel & Buffer channel : 双向流 Buffer:暂存数据 缓冲区 常见chan ...

最新文章

  1. linux下 x86、i386、i486、i586、i686、x86_64区别
  2. 解决 org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does no
  3. spring boot Redis使用
  4. 修改Bugzilla的主页图片
  5. SAP Spartacus pagefold学习笔记
  6. elasticsearch版本_折腾下Windows下的Elasticsearch安装与使用
  7. 力扣回文字串的动态规划解法
  8. 为何电脑html无法删除,电脑文件无法删除该怎么办?几种解决方法介绍
  9. Python yield generator
  10. 近年来,学习图像去雾不得不看的论文和源代码
  11. ArcView Image Analyst v1.0.rar
  12. java jsp乱码怎么解决_Java/JSP中文乱码问题解决心得
  13. 水星逆行对股市涨跌的实证检验
  14. 博士申请 | 新加坡南洋理工大学尹旬元老师招收机器学习方向全奖博士/博后
  15. OO包设计原则遵循度自动分析检查工具JDM简介(原创)
  16. wordpress 调整段落间距和字体大小
  17. Google Earth Engine——MODIS Combined 16-Day NDVI逐年合成影像循环下载
  18. 简单理解数字签名和验签
  19. SAP中采购订单交货己完成相关逻辑和控制原理分析
  20. android底层之什么是Zram?

热门文章

  1. 写给Krpano小白们的最最最入门级教程(一)
  2. python生成的excel无法打开-Python 解决中文写入Excel时抛异常的问题
  3. Qt常见make编译错误:/usr/bin/ld:cannot find -lxxx
  4. 2020年中国无人船艇行业发展政策分析,竞争格局相对分散「图」
  5. FFmpeg的HEVC解码器源码简单分析:概述
  6. FFmpeg的HEVC解码器源代码简单分析 解析器(Parser)部分
  7. 关于微信公众号文章抓取
  8. 攻防世界 MISC 新手练习区 writeup 001-006
  9. java计算机毕业设计医院病历管理系统源程序+mysql+系统+lw文档+远程调试
  10. php开发俄罗斯方块,HTML5+JS实现俄罗斯方块原理及具体步骤_html5教程技巧