解决java socket在传输汉字时出现截断导致乱码的问题
解决java socket在传输汉字时出现截断导致乱码的问题
当使用socket进行TCP数据传输时,传输的字符串会编码成字节数组,当采用utf8编码时,数字与字母长度为1个字节,而汉字一般为3个字节。这里参考
字符集之在UTF-8中,一个汉字为什么需要三个字节? - 苦涩的茶 - 博客园 (cnblogs.com)
如果传输的字符串是数字,字符和汉字混杂。在数据的接收端,每次调用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在传输汉字时出现截断导致乱码的问题的更多相关文章
- java socket通信-传输文件图片--传输图片
ClientTcpSend.java client发送类 package com.yjf.test; import java.io.DataOutputStream; import java.io ...
- 解决java web中safari浏览器下载后文件中文乱码问题
解决java web中safari浏览器下载后文件中文乱码问题 String fileName = "测试文件.doc"; String userAgent = request.g ...
- java socket 网络通信 指定端口的监听 多线程 乱码
Java Socket编程 对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket.服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了.首 ...
- java socket之传输实体类对象
一.TCP编程 TCP协议是面向连接的.可靠地.有序的,以字节流的方式发送数据.java实现TCP通信依靠2个类:客户端的Socket类和服务器端的ServerSocket类. 基于TCP通信 ...
- Java socket字节流传输的示例
服务端server端: package com.yuan.socket; import java.io.*; import java.net.ServerSocket; import java.net ...
- 解决 java循环中使用 Map时 在put值时value值被覆盖的问题
其实很简单,只需要把容器换成list 然后在循环中,每次循环末尾map = new HashMap() 或者直接在循环中一开始就实例化hashmap(Map map = new HashMap();) ...
- 解决java中ZipFile解压缩时候的中文路径和乱码问题
JAVA中对jar文件或zip文件解压的时候,能够使用JDK内置的API:JarFile和ZipFile,在windows下解压这2种格式文件的时候,常常报下面错误: Exception in thr ...
- socket 发送字符串0x00时被截断
发送数据如下: aa 02 02 00 00 00 6f 6b 02 00 00 00 55 数据是以字符数组的形式(char msg[])存储发送的,send时发送长度填写的strlen(msg), ...
- ASP.NET开发在JavaScript有中文汉字时出现乱码时简单有效的解决
一般情况在使用ASP.NET开发使用JavaScript有中文汉字时不会出现乱码情况,比如:alert('您看到我了吗?');这样直接输入中文汉字的代码中是不会出现乱码的,如果出现了,一是检查Web. ...
随机推荐
- C#事件总线
目录 简介 实现事件总线 定义事件基类 定义事件参数基类 定义EventBus 使用事件总线 事件及事件参数 定义发布者 定义订阅者 实际使用 总结 参考资料 简介 事件总线是对发布-订阅模式的一种实 ...
- split命令_Linux split命令:切割(拆分)文件
<Linux就该这么学>是一本基于最新Linux系统编写的入门必读书籍,内容面向零基础读者,由浅入深渐进式教学,销量保持国内第一,年销售量预期超过10万本.点此免费在线阅读. 15 分钟之 ...
- 攻防世界(七)ics-06
攻防世界系列:ics-06 1.打开题目 很LiuPi的样子,根据题目提示点开报表中心(其他的点开没什么变化) 注意到URL中有?id=1,测试其他数值无果 3.抓包对id爆破 设置1-10000,爆 ...
- spark-steaming的exactly-once
spark实时计算中会存在数据丢失和数据重复计算的场景, 在receiver收到数据且通过driver的调度executor开始计算数据的时候如果driver突然崩溃,则此时executor就会被杀掉 ...
- Python使用 Kubernetes API 访问集群
通过将身份认证令牌直接传给 API 服务器,可以避免使用 kubectl 代理,像这样:使用 grep/cut 方式: 通过将身份认证令牌直接传给 API 服务器,可以避免使用 kubectl 代理, ...
- 向pom.xml中配置Maven使用JDK8编译
maven项目会用maven-compiler-plugin默认的jdk版本来进行编译,如果不指明版本就容易出现版本不匹配的问题,可能导致编译不通过的问题. 解决办法:在pom文件中配置maven-c ...
- 解决Caused by: org.apache.ibatis.exceptions.PersistenceException:
在mybatis-config核心配置文件中注册了xml以后出现了新的异常错误 Caused by: java.io.IOException: Could not find resource cn.d ...
- 使用Git下载指定分支命令为
使用Git下载指定分支命令为: git clone -b 分支名 仓库地址 例如: git clone -b dev https://github.com/xxx.git 将下载分支名为2D- ...
- bat使用方法汇总
前言 由于日常科研工作中使用C/C++比较多,在进行大规模运行时涉及到的批量处理操作较多,遂将目前遇到的情况记录如下,以便查看: 1.for循环 最基本的for循环操作为在一些数中遍历,如下例子.se ...
- springboot 搭配redis缓存
1.引入依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId&g ...