解决java socket在传输汉字时出现截断导致乱码的问题

当使用socket进行TCP数据传输时,传输的字符串会编码成字节数组,当采用utf8编码时,数字与字母长度为1个字节,而汉字一般为3个字节。这里参考

字符集之在UTF-8中,一个汉字为什么需要三个字节? - 苦涩的茶 - 博客园 (cnblogs.com)

UTF-8 往事 (taoshu.in)

如果传输的字符串是数字,字符和汉字混杂。在数据的接收端,每次调用read方法接收的byte数组的长度是一定的,由于数字,字母和汉字对应的utf8编码长度不同,可能会导致末尾的汉字被截断。举个例子:假定接收端每次调用read方法读取的byte数组长度为20。而发送短发送的字符串为"Hello World! 你好中国!",转换为byte数组为

[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x20, 0xE4, 0xBD, 0xA0, 0xE5, 0xA5, 0xBD, 0xE4, 0xB8, 0xAD, 0xE5, 0x9B, 0xBD, 0x21]

在接收端第一次读取的byte数组长度为20,对应的byte数组为

[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x20, 0xE4, 0xBD, 0xA0,0xE5, 0xA5, 0xBD, 0xE4]

转换为字符变成了"Hello World! 你好�",最后一个字符出现了乱码。这是因为前19个字节(英文空格加标点共13个字节,前两个汉字占六个字节)转换成了字符串"Hello World! 你好",最后一个字节对应着字符"中"的三个字节[0xE4, 0xB8, 0xAD]中的第一个字节0xE4。但是这个一个字节(首位为1没有对应的单字节字符)是无法转换为可见的字符的。这里就是出现了隔断。"中"的剩余两个字节为下一个read读取得到的字节数组的前两两个字节。

为了能够解决截断问题,我们需要判断接收到的字节数组的最后两个字节是否为被截断的汉字字符的对应的字节。

为了解决这个问题,首先了解一个汉字字符在UTF-8编码中的格式。基本上常见的汉字字符在UTF-8编码中都是三个字节。在UTF-8编码方案中,对三字节编码的每个字节都做了规定:

如果是三个字节编码的话,那么第一个字节的前三位为111,第四位为0,剩余的两个字节的前两位都为10

比如汉字"中",对应的三个字节用二进制可以表示为:[1110 0100, 1011 1000, 1010 1101]

满足UTF-8编码的要求。

如何根据编码格式来判断得到的byte数组的最后一个或者两个字节是被截断的呢。

我们将byte数组的最后两个字节定义为firstByte和secondByte,分别对应着导数第二个字节和倒数第一个字节。

情况1:

如果倒数第一个字节符合1110 xxxx格式,说明这个字节对应着汉字字符的第一个字节,剩余的两个字节在下个被接收的byte数组中,发生了截断。我们需要暂存最后一个字节secondByte,与下面一个byte数组的前两个字节组合在一起解析出汉字。

情况2:

如果倒数第二个字节的二进制符合1110 xxxx格式,那就说明这个字节对应着汉字字符的第一个字节,最后一个字节对应汉字字符的第二个字节,第三个字节为下个被接收的byte数组第一个字节,发生了截断。这种情况需要将byte数组的最后两个字符保存起来,与下面一个byte数组的第一个字节结合起来才能解析出对应的汉字。

对于如何判断一个字节是否符合1110 xxxx格式,这里我们采用掩码的方式,保留firstByte的前四位,屏蔽后四位(将后四位置零)。

判断 firstByte & 11100000 == 11100000是否成立,如果成立对应第一种情况,否则

判断secondByte & 11100000 == 11100000是否成立,成立则对应第二种情况

解决java socket在传输汉字时出现截断导致乱码的问题的更多相关文章

  1. java socket通信-传输文件图片--传输图片

    ClientTcpSend.java   client发送类 package com.yjf.test; import java.io.DataOutputStream; import java.io ...

  2. 解决java web中safari浏览器下载后文件中文乱码问题

    解决java web中safari浏览器下载后文件中文乱码问题 String fileName = "测试文件.doc"; String userAgent = request.g ...

  3. java socket 网络通信 指定端口的监听 多线程 乱码

    Java Socket编程 对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首 ...

  4. java socket之传输实体类对象

    一.TCP编程     TCP协议是面向连接的.可靠地.有序的,以字节流的方式发送数据.java实现TCP通信依靠2个类:客户端的Socket类和服务器端的ServerSocket类. 基于TCP通信 ...

  5. Java socket字节流传输的示例

    服务端server端: package com.yuan.socket; import java.io.*; import java.net.ServerSocket; import java.net ...

  6. 解决 java循环中使用 Map时 在put值时value值被覆盖的问题

    其实很简单,只需要把容器换成list 然后在循环中,每次循环末尾map = new HashMap() 或者直接在循环中一开始就实例化hashmap(Map map = new HashMap();) ...

  7. 解决java中ZipFile解压缩时候的中文路径和乱码问题

    JAVA中对jar文件或zip文件解压的时候,能够使用JDK内置的API:JarFile和ZipFile,在windows下解压这2种格式文件的时候,常常报下面错误: Exception in thr ...

  8. socket 发送字符串0x00时被截断

    发送数据如下: aa 02 02 00 00 00 6f 6b 02 00 00 00 55 数据是以字符数组的形式(char msg[])存储发送的,send时发送长度填写的strlen(msg), ...

  9. ASP.NET开发在JavaScript有中文汉字时出现乱码时简单有效的解决

    一般情况在使用ASP.NET开发使用JavaScript有中文汉字时不会出现乱码情况,比如:alert('您看到我了吗?');这样直接输入中文汉字的代码中是不会出现乱码的,如果出现了,一是检查Web. ...

随机推荐

  1. .NET Core 对象( Transient、Scope、Singleton )生命周期详解 (对象创建以及释放)

    首先我们在VS2019中创建一个.NET Core的控制台程序,方便演示: 需要安装两个依赖包 Microsoft.Extensions.DependencyInjection 依赖注入对象的具体实现 ...

  2. Etcd中Raft日志复制的实现

    Raft state of log commitIndex : A log entry is committed once the leader that created the entry has ...

  3. Linux Limit相关内容设置大全(值得收藏)

    目录 一. /etc/security/limits.conf 详解 /etc/security/limits.conf 配置解析 /etc/security/limits.d/目录 二. ulimi ...

  4. x265 code tracing

    方瑞东的博客专栏 http://blog.csdn.net/frd2009041510/article/details/51182920 cabbage2008的专栏 http://blog.csdn ...

  5. [bug] sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1054, "Unknown column 'recevie_name' in 'field list'")

    Python Flask 开发购物网站,提交订单时报错 根据提示,检查代码,发现是字段名拼写错误导致,数据库对应的字段是receive_name,误写成了recevie_name 另外要注意,灰色字和 ...

  6. [bug] CDH 安装 哈希验证失败

    分析 验证 parcel 文件的哈希值 和 sha 文件不一致:文件损坏,重新下载 和 sha 官网一致:配置httpd文件 参考 哈希值和官网不一致 https://blog.csdn.net/lv ...

  7. [Linux] Linux C编程一站式学习 Part.3

    Linux系统编程 文件与I/O C标准I/O库函数与Unbuffered I/O函数 C标准I/O库函数printf().putchar().fputs(),会在用户空间开辟I/O缓冲区 系统函数o ...

  8. Linux_配置加密的https

    一.配置https 1.安装好httpd服务后,安装mod_ssl模块 //首先查看是否安装mod_ssl [root@localhost ~]# rpm -qa | grep mod_ssl //安 ...

  9. Scala 中的可变(var)与不可变(val)

    引言 Scala 中定义变量分为 var(可变变量)和 val(不可变变量) Scala 中集合框架也分为可变集合和不可变集合.比如 List(列表) 和 Tuple(元组)本身就是不可变的,set ...

  10. Linux进阶之给nginx设置登录用户验证

    一.nginx开启访问验证 使用nginx搭建的站点,如果不想让所有人都能正常访问,那么可以设置访问认证,只有用户输入正确的用户名和密码才能正常访问. 在nginx下,提供了ngx_http_auth ...