Wednesday, April 29, 2009

解决字符编码问题

字符编码集的问题

这两天和一个第三方平台进行联调,碰到一个字符编码的问题,经过自己的分析并与对方进行沟通,最终问题得到妥善 解决。字符编码让人非常恼火,只有真正理解了其中的机制之后才能快速的找出问题的根源。 幸亏去年读过好些关于字符编码的文章,让我对这个问题有很清晰的认识,这也是这次能快速解决问题的根本。
记录一下解决这个问题时我的分析过程,也反省一下还有些什么可以提高的。
问题背景: 与对方平台的接口采用HTTP POST方式传递XML报文方式交换数据,其中报文体BODY是以DES加密的字符串。由于项目需求,在加密前进行了一次字符串转换,将字符串的byte[]转成Hex字符再进行DES。

问题现象:在公司测试环境上,发现对方传递过来的报文能正确解析出XML结构并正常处理,但是查看最终生成的数据中发现其中的中文为乱码。但是另外有个发现是我在生产测试机上在终端看到了正确的中文字符。

问 题分析解决过程:由于报文解析正常,业务处理也正常,存在中文出现乱码问题,因此我认定是自己内部处理出现错误。由于内部处理过程中也存在两个不同服务的 交互,于是先是假定问题出在两个服务传递数据时的问题(当时对于这个猜测,我并没找到充足的理论依据,事实证明这种猜想是错误的),于是debug接收数 据的服务。Debug中发现接收到的数据已经是乱码(值全部为 65533)。 问过做内部两个服务数据交互接口的开发人员,他说是通过Socket传递数据的,不可能存在编码问题,因此把我问题定位在解析这个过程上。于是针对解析部 分进行Debug,发现DES解密出现的串中,中文已经是乱码 (值全部为65533)。 于是查看解密的代码,在查看解码代码时发现了问题,正是解决该字符集问题的关键!在这里要提一下,幸亏对方给了我们解密与加密的实现代码,否则这问题更难 定位。代码中在将一个String转换成byte[]时采用str.getBytes()方法,该方法采用JVM默认的编码将一个String转换成 byte[]。这时让我想起之前在HP-UX终端打印的看到正确的中文,而在公司测试环境下的Linux下面却是乱码的。我断定是乱码的问题是由于对方加 密时JVM的编码与我们测试系统中解密时采用的编码方式不一致造成的。由于对方工作人员已经下班,没办法直接问他们的字符编码方式。但是想到HP-UX能 正常解析,只要找到HP-UX上JVM启动时设置的编码就能知道问题原因了。但是该死的HP-UX并没有像Linux那样的/porc文件系统来记录进程 信息,而且ps 命令只能显示很少的命令行字符,折腾了半天也没能看清楚cmdline的全貌。这时想起在Linux下面启动时应该和HP-UX下JVM启动参数设置是一 致的, 于是查看Linux下的cmdline。 但是发现JVM启动时并没有指定file.encoding参数,这表明JVM采用操作系统默认字符集了。 查看后发现Linux下是C,HP-UX不熟悉,折腾半天也没搞明白,自己找到编码方式的尝试失败。于是决定等第二天直接问对方采用的字符编码方式。
第二天早上和对方开发人员沟通后,对方告诉我他们是采用GBK编码。 我马上在JVM启动时加上一个启动参数-Dfile.encoding=GBK,再次发送报文测试,新生成的数据中中文不再是乱码,问题终于解决了。

问题的反思:
问题解决了,但是对于问题解决过程的反思更重要。
解决方案中可能引入的新问题是JVM改成GBK编码会不会对该服务中其它部分产生影响?
再 回顾这个问题,罪魁祸首就是用了String的getBytes()方法,或许在使用该方法时应该显示指明其编码类型?所有代码都在单个JVM中运行或许 不需要关心这 个问题,因为编码总是统一的。但是在多个JVM中,并且JVM之间要交换数据,使用这个方法时或许就需要注意一下了。
再回顾问题解 决过程,发现在分析问题过程中还有许多可以改进的地方。 比如开始就就忽略的HP-UX上面编码是正确的这个关键点,而是在折腾了半天之后,定位到是JVM编码问题时才想起这个关键点。从HP-UX与Linux 上出现不同的解密不同时就可以判定在JVM启动时没有指定默认字符集,而不需要花费很多时间去找到进程启动的cmdline. 这些都是在经过清晰,严谨的思考,只要再往前一步就能推断出答案的,而在解决问题中却没有发现。 遇到问题保持头脑清醒,周密,严谨的思考比想到一半就开始实践更有效率。这些都是今后工作中需要提高的。

No comments:

Post a Comment