关于网络通讯编码问题

由于在工作中需要进行网络通讯方面的编程,其中又涉及到服务器端使用的是C++,而客户端有一方使用的是JAVA,我在实际使用中,对这种情况下的编码问题有了较深刻的体会,可以分享一下。

通常情况下,我们做网络通信方面时,局限于我们的项目应用规模,比如可能只是应用于C++环境的服务器和客户端,或者只应用于JAVA环境,或者只应用于中文操作系统平台下。所以即使大家能够做这方面的C/S通信,但是有时是没有细究深层次的一些编码问题的,但是也是仍然能够通过。不过我觉得应该深入了解一下网络传输过程中的编码的问题,这个我觉得挺重要,如果明白了编码的原因,可以解决很多中文字符乱码的问题。

下面我说说我的实际使用情况,我的服务器端使用的是C++WIN32控制台程序,它是一个服务器,没有界面元素,而只做服务运算的。而我的客户端有两种情况下使用,一是JAVA平台的,另一个是C++平台的。

C++的服务器端使用的是本地的ASCII编码,也就是说,我们通常使用的char型的字符串,或者字符数组都是本地ASCII编码的,这个东东是什么意思呢,其实通常情况下本地的ASCII编码会和你的操作系统的语言编码一至,比如我的操作系统是windows平台的简体中文版本,默认情况下这个操作系统的中文编码是gb2312编码,所以我们的本地ASCII编码也是gb2312编码。这种编码通常的情况下表现的是在标准的ASCII内部是没有什么变化的,比如英文,数字等,而简体中文通常表现是两个字符,因此如下面的示例:

Char test[] = “123abc中文”;

如果我们用C函数strlen去求test字符串的长度,则其长度值是10,那么这个10表示什么意思呢,它表示的是这个字符串占有10个字节的长度(一个字节为8bit位)。但是如果我们要用字符来表示其长度的话(所谓字符,是指一个有特定编码意义的文字,比如数字1,字母a,中文‘中’等),按这种方式来求test字符串的长度的话,则其长度应该是8,而不是10,因为其字符只有8个,当然字节编码有10个,这个很重要,这也是为什么通常有中文乱码的原因所致。如果同在一个开发语言下,比如C++中,如果不存在对test字符串进行其他编码的情况下,来解析收到的这样的字符串,也按本地ASCII来进行,则是没有什么问题的,现在的关键问题是如果客户端不是C++呢?以及客户端不是服务器端的gb2312编码呢?

JAVA平台下,字符串String以及字符类型Char都是UNICODE编码,这种编码和gb2312是有较大区别的。有些中文会编码为3个字节。在JAVA平台中下面的字符串的长度就和C++的本地ASCII不一样。如:String test = “123abc中文”;这个字符串长度是按UNICODE编码,当调用length时,其长度按字符进行计算,则其长度为8。(要注意字节与字符的区别,前面已说明),而在JAVA平台中的byte这个类型,呵呵,提供了面向字节流的方式编码,则当你调用String类型中的getbytes方法时,返回的就是按字节进行编码的字节流,这时你再求上面的长度时,则其和本地的ASCII编码相同。这里的byte类型和C中的字符类型是一样的,都是表示一个字节长度,即8bit位。

好了,说完这个,再说一下网络传输的问题,通常情况下,如果你的开发和使用平台都是JAVA的,我建议直接建立字符流,传String类型就可以了,这样在你的项目中能提高开发速度,不用进行字节与字符之间的转换,因为你通常在项目中使用字符串String,而不是字符流byte[]吧。而如果都是C++平台,则也不用考虑编码的问题,现在如果象我开始描述的情况,服务器端用的是C++平台,而客户端是JAVA平台,则需要考虑编码的问题了。

客观而言,JAVA在网络传输方面的处理要好些(或者说IO方面),因为其将字符明确分为字节流和字符流,而在Socket传输中,一般传输字节流,字节流与特定的字符意义无关,说白了就是一些二进制的比特流。而在C++中通常所见的网络传输却不是比特流,而是本地ASCII流,这主要原因是通常都是同一种开发语言平台所致。在C++中要传输比特流,就我所见好象很少,通常是传输如Char这样的数组,而不会传输byte这样的数组,这里的byte通常是指signed   char,而这个只是区别于有符号和无符号,并不是我们想要的比特流,所以用处有限,当然这是我的理解,可能实际中还有其他用处,限于我自己能力所限,目前没有发现。

基于上面所说的原因,一方面是对字符串的编码采用方式不一样,JAVA中是UNICODE,而C++是本地ASCII,同时在网络传输过程中,通常二者也是不一样,JAVA采用的是比特流的方式(当然你也可以用字符流,但是跨语言平台请慎用),C++仍是本地ASCII方式,所以需要在这两种不同的开发语言平台下,需要约定采用的编码方式,通常可以采用UTF8进行编码和解码,这样就不会出问题。

比如,如果C++的服务器端是用本地ASCII进行的逻辑代码编写,当进行Socket传输时,先将本地ASCII编码进行一个UTF8转换,这样,你要传输的代码的长度可能会变化,但是如果按这样的方式进行相应的解码的话,就不会有问题了(即使此时你传输的仍可能是Char这样的数组,此时编码方式不一样了,其里面的内容是按UTF8的方式进行的)。因此接收端接收到这样的比特流时,按UTF8的方式进行解码,则可以还原出你真实的传输的数据。这种方式通常在网络传输中被广泛使用,而UTF8UNICODE的一种实现方式,其已经得到很多实际项目的支持和验证。

当然还有一种方式可以解决方面所提到的问题,服务器端仍是C++平台,传输的仍是本地ASCII编码,在JAVA平台中接收到了一个编码之后,它首先是接收的一串比特流,按开发人员之间的约定,将对其进行本地ASCII的解码,然后JAVA开发人员再将其转化为UNICODE编码,这样也是可以的,示例如下:

你所要传输的字节流是这样的(按字节进行的编码,不是指每一个具体的bit)010010abc123中国

对上面的传输的字节流的解释是这样的,前面两个字节01表示包的标识,后面的0010四个字节表示其后面的字节流长度,而后面的abc123中国,则是要传输的数据。在JAVA端接收到这样的字节流之后,则按这样的协议提取出“abc123中国”出来,这是一个byte型的数组,然后再将这个byte型的数组转化为字符串。就可以用了,如果没有转化为String的话,此时的byte数组长度是10,当转化为String之后,则其长度将按UNICODE的方式进行编码,其长度将会变为8

综上所述,对中文或者其他语言进行编码时,需要考虑编码方式(这通常是乱码产生的原由),通常建议采用UTF8这样的编码,因为这种编码方式包含了世界所有国家的语言的编码,其中就含有gb2312的包含,在同一种编码方式下,不管是编码还是解码都可以有效的使对应的位对齐,则解析出来的字符自然不会乱码。 

你可能感兴趣的:(java,网络,String,服务器,平台,通讯)