TCP/IP
协议以字节的方式传输用户数据,并没有对其进行检查和修改。这个特点使得应用程序可以非常灵活地对其中传输的信息进行编码。TCP/IP的唯一约束是,信
息必须在块(chunk)中发送和接收,而块的长度必须是8位的倍数。而字节正好是8位的,因此我们可以认为在TCP/IP协议中传输的信息是字节序列。
鉴于此,我们可以进一步把传输的信息看做数字序列或数组,每个数字的取值范围是0~255(8位)
应用程序协议:明确定义了信息的发送者应该怎样排列和解释这些位序列(bit sequence),同时还要定义接受者应该怎样解析,这样才使得信息的接受者和发送者能够抽取每个字段的意义。
在Java和C#程序中,Int数据都由32位表示(都映射到Int32上),因此,我们可以用4个字节来传输任意的Int变量或者常量。这时要注意的是,对于需要超过一个字节来表示的数据类型,我们必须知道这些字节的发送顺序。显然有2种选择,从右往左或者从左往右。
字符串和文本:
因
为人们习惯于处理各种各样以字符串形式表示的信息,如书本中和电脑显示器上显示的信息。因此,只要我们指定如何对要传输的文本进行编码,我们就几乎能发送其他“任何类型”的数据:先将其表示成文本形式,再对文本进行编码转换成相应类型。用文本表示的类型和二进制表示的类型相比优势之一就是人可以读懂,第二就是可以跨平台。显然,我们可以将数字和boolean类型的数据表示成String类型,如
“123456”,“1.23e33”,"true"等。也可以表示
成"<int>123</int>","<boolean>true</boolean>"等。我们也
知道,通过调用getBytes()方法,可以将一个字符串转换成字节数组。当然,还有其他方法实现这个功能。
为了更好的理解这个过程,我们首先得将文本视为由符号(如感叹号"!",问号"?")和字符组成。实际上每个String实例内部都对应了一个char[]类型。一个char在java内部表示为一个整数。如字符 "a" 与整数97对应,"!"则对应了33.
在一组符号与一组整数之间的映射称为编码字符集(coded character set)。你应该听说过ASCII编码字符集(美国标准信息交换码)。ASCII码将英语字母,数字,标点符号以及一些特殊符号映射成0~127的整数。
可以看出,它忽略了许多英语以外的其他语言所使用的符号,因此用ASCII码来开发应用程序和协议显然并不适合我们的情况。
128个数字明显不够用,可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。因此,Unicode字符集出现了,就像它的名字都表示的,这是一种所有符号的编码。将世界上大部分的语言和符号映射到0~65535之间。Unicode包含了ASCII,也就是说,原来在ASCII中字符所对应的数字在Unicode中也是用同样的数字来表示,这就提供了一定的兼容性。需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
发送者和接收者在字符映射这件事上达成共识就完了吗?这还得看情况而定。对于每个整数值都比255小的一小组字符,则不需要其他信息,因为它可以用一个字节
来表示,不会涉及到多个字节的排序问题。对于可能使用超过一个字节的大整数的编码方式,就需要对这些整数如何表示成字节序列统一意见,这个意见也叫做“编码规范"(encoding
scheme).当然,只要你喜欢,你可以定义自己的编码规范。但是就如同前面所说得,这需要发送者和接收者沟通并达成一致。这挺费事,而且世界上已经有
大量不同的标准,我们只要选择一种标准来共同遵守就可以了。
说来说去,其实就是一个映射问题,拿String实例的getBytes()方法来举例,该方法返回一个[]byte也就是字节数组。通过调用该方法,就完成一个“由字符串到字节的映射过程".
比如调用"Test!".getBytes(),你将获得按照UTF-8字符集编码的数组。
如果按照"Test!".getBytes("IBM037"),返回的结果将会是
上面的例子说明,发送者和接收者必须在文本字符串的表示方式上达成共识。
上面的是java代码,C#中可以明显的看出字符集的定义,由相应的类来表示:
static void Main( string [] args) |
UnicodeEncoding unicodeEncoding = new System.Text.UnicodeEncoding(); |
UTF8Encoding utf8Encoding = new UTF8Encoding(); |
byte [] arr = utf8Encoding.GetBytes( "Test!" ); |