应用层编码与网络接口层的数据发送

来源:互联网 发布:矩阵与线阵音响 编辑:程序博客网 时间:2024/05/16 05:12

我们知道,在所有的网络应用程序中,数据最终都要被转化为二进制然后通过网络接口层的一个处理芯片将这些二进制的数据转化成电信号或者光信号通过物理网络发送的。 那么,在应用层到网络接口层的这一个过程中,这些应用层的数据是如何和网络接口层的二进制对应起来的呢。这就是编码的作用了。

   我们在应用程序中看到的数据,通过编码与二进制来对应。 比如:ASCII编码。用一个字节的后7bit,最前面的1位统一规定为0,规定了128个字符的编码(包括32个不能打印出来的控制符号),比如空格"SPACE"是32(二进制00100000),大写的字母A是65(二进制01000001)。计算机将应用程序中所有的英语字符以及相关的字符转换为一个8bit,当然这8bit的首位都是0(表示未使用),然后,送到网络接口层,当远程接收到该二进制串的时候,就会按照ASCII编码来以每8bit为单位来分割该二进制串,并且在应用程序中通过查找编码对应表来翻译出这些以8bit为基本单位的串所对应的真正的字符,从而达到了传输英语字符串的目的。可见,编码和解码需要同一个编码方案(编码格式),才可以正确的得到数据。
  同样的道理,在飞网络应用程序中,在本地计算机中的所有应用程序中的处理也是一样的,因为在计算机中,所有的数据在真正保存的时候(也就是说当到达物理层的时候,都是需要转化为二进制的串,然后再转化为电信号的). 比如,磁盘的话是转化为电磁信号的高低,光盘的话是把最终的二进制串转化为光盘激光的凹凸来存储的。当读取数据的时候,在物理层都是通过物理的高低来读取的二进制串, 这样,就需要在应用层对这些二进制串进行解码, 这时候,如果解码所采用的方式与编码的时候不一样的话, 就会出现乱码也就是无法正常解码。 表现在应用层的话,就是格式,其实就是编码格式, 也就是说, word如果用非word格式的程序来打开(也就是用不同的应用程序来解码)的话, 就会解码错误。  所以 说, 我们关心的就是应用层到物理层的转化编码问题,不管是在网络还是非网络环境下都是这样的。

下面我们再来看看其它的主要编码方式:

在英语中,采用ASCII来编码英语,但是用ASCII编码方法来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号。
   在欧洲国家,因为所使用的语言符号除了英语中使用的那些基本语言符号外,还使用了其它的一些符号,关靠一个字节的低7位来编码是不够的,所以开始使用了一个字节的首位来表示字符编码, 这样,在这些国家,采用的就是基于ASCII的非ASCII编码。 在这些编码中低7位与美国语言所使用的ASCII编码是一样的,代表一样的符号,但是剩下的则是特有于ASCII的编码了,而且各个国家的编码虽然低7位都是与美国语言的ASCII编码一样的,但是低7位以外的另外的128个编码则是各不相同的。
亚洲国家语言使用的语言符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示256x256=65536个符号。

这样,一来,各个语言都使用了不同的编码方案,但是至少欧美语言的编码中的8位编码的低7位至少都是一样的(即相同的二进制串代表相同的语言符号)。

这样就带来了一个问题,对应不同国家的文件,采用了不同的编码,那么在解读的时候,必须采用与编码相同的编码格式才能正确的显示出源文件的真正内容,简单的说,比如,欧洲的一个国家写了一封邮件,采用了他自己国家语言的编码,那么当发到欧洲的其它一个国家的时候,如果采用一个不同的编码格式来打开的话, 那么就无法解读了。比如, 同样的二进制位数10001100,在写信人来说,编码是é(实际上所有的英语都被编码在了一个字节的低7位中,这也就是英语不管在哪里打开都不会出现乱码的原因,因为英语是用一个字节的最后7位来表示的,而不管在哪个国家,都是这样的,而且表示的都是同一个字符)。而在收信人打开时候,因为采用了不同的编码来打开,那么在这个编码中二进制位数10001100代表?,那么他所看到的就是?了,显然,可以说是误解了写信人的内容了。

这样,不同国家的人交流就容易产生解码错误,那么如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的统一编码。
Unicode只是一个二进制符号与语言文字符号的对映集,它只规定了语言符号的二进制代码,却没有规定这个二进制代码应该如何存储。也就是说,对世界上的任何一个字符,都有一个二进制串和它对应,但是因为世界上所有语言的符号加在一起,数量巨大,这样对任意的一个符号,可能存储的时候需要一个字节就够了比如英语(UNICODE编码中,对英语的编码也是与ACSII一样的,可以说ASCII编码是其它所有编码的元祖,且被其它所有编码都是在它上面扩展开来的,因此,英语字符在任意编码中都是一致的,不会产生解码错误,这也就是为什么在一个文件中,除了英语能正常显示以外,其它的都是乱码的原因,因为英语被所有编码格式支持。),可能需要4个字节,那么究竟用多少个字节来表示一个字符,才可以既节省存储同时也能完全表示所有的字符呢?好像真的很难权衡的。在上面的例子中,如果用4个字节的话,那么原来本来用一个字节就可以表示的英语文本就会因为现在统一采用4个字节而使得表示同样内容的英语文本的大小一下子增加了4倍(其中所有4字节为单位的前3个字节都会被浪费,都是0)。    这显然是难以接受的。 还有一个问题,就是,因为现在表示一个符号的时候,可能是一个字节,也可能是3个字节,那么当遇到一个3个字节的二进制串的时候,究竟是代表三个符号(每个符号占一个字节),还是代表一个符号呢?(一个符号占3个字节),局难以判断了,这就是unicode编码的两大困局。

结果是,针对这连个困局,不同的机构提供了不同的解决方案,可以说是unicode编码的不同实现方法吧。到这里,我们应该理解了,unicode编码本身是不能使用的,它只是一个方法或者说是一个解决不同语言乱码问题的思路,真正可以使用的是那些unicode编码的实现方案,但是,通常说说的unicode编码,实际上指的就是unicode编码的实现方案了。我们应该了解这一点。

在很长一段时间内,出现了unicode编码的许多种不同的实现方式,UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。UTF-8编码是Unicode编码的实现方式之一,也可以说是一种unicode编码。

那我们具体的来看看,UTF-8又是如何实现unicode编码的呢?

UTF-8采用变长的编码方式,使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,这样就很好的达到了节省存储的目的。
 下面,我们来看看UTF-8又是如何来解决多字节编码时候如何确定编码字节的数目的问题吧。

它是通过以下的UTF-8的编码规则来达到目的的:
 1)在ASCII编码上扩展, 对于英语语系的字符,仍然采用单字节,且第一位设为0。因此对于英语字母,UTF-8编码和ASCII码是相同的。
 2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。

    Unicode符号范围 | UTF-8编码方式
    (十六进制) | (二进制)
    --------------------+---------------------------------------------
    0000 0000-0000 007F | 0xxxxxxx
    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,还是以汉字"严"为例,演示如何实现UTF-8编码。

    已知"严"的unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800-0000 FFFF),因此"严"的UTF-8编码需要三个字节,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然后,从"严"的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,"严"的UTF-8编码是"11100100 10111000 10100101",转换成十六进制就是E4B8A5。

这样,可以用UTF-8来对世界上任何国家的任何字符进行编码了,那么又如何对编码后的文件解码呢?

其实,我们应该注意到了,UTF-8编码实际上把编码所用的字节数的信息也编码进去了(当然是浪费了一点点存储的了,就是上图中的标志位),这主要是为了在解码时能利用该信息来正确的解码,从而解决了上述的第二个困局。
在解码时, 解码器对二进制串进行扫描,所有以0为开始的字节是一个编码单位,所有以10开始的字节为编码单位的中间部分。 所有以2个到4个1外加一个0开始的字节为编码单位的开始部分,当遇到编码单位的开始部分的时候,通过计算起始1的个数n,来得到编码单位应该包含的字节数。再通过连续扫描后续n-1个以10起始的字节来组合出一个编码单位,最后通过对照编码表,就可以得到这些编码所对应的真实的内容字符了。





到此为止,我们分析了所有的编码。总结起来,编码的发展是这样的:以ASCII编码为始祖(可以说是“统”),然后,欧美国家在ASCII编码的基础上使用了一个字节的首位的0,就产生了各个国家独自的编码格式,包括亚洲国家的多字节编码格式(比如中文的GB2312就使用了2个字节来编码汉字,但是其第一字节的低7位仍然保持和ASCII编码一样,即这7位二进制,相同的7位二进制代表着相同的英语字符)。后来,因为各个国家的编码不同带来的乱码问题,出现了全世界统一编码unicode编码的思想,通过解决unicode编码思想的两个困局,产生了unicode编码的实现UTF-8,UTF-16,UTF-32等,其中使用最多的就是UTF-8了。

在unicode编码和其实现UTF-8编码之间,其实是这样的关系,比如对一个字符,比如汉字“严”,在unicode编码中,规定汉字“严”的二进制编码为“100111000100101”(15个二进制bit),但是没有规定如何存储,在UTF-8编码中,通过上述法则,将该汉字编码为“11100100 10111000 10100101”(用三个字节来存储,从左到右,多于的bit补0)。    一般,在计算机中实际使用的是二进制序列,但是这些二进制序列由于位数过多,写出来会很长,不是很方便,所以一般实际表示这些二进制数据的时候都是用其16进制形式来表示的,这样一方面减少了表示时候的长度,一个16进制位能表示4个二进制位,另一方面,相对于8进制和10进制来说,能更好的与二进制形式对应起来,这样当需要还原到其二进制形式的时候会更方便,(因为10进制 与二进制并不存在严格的位数对应关系)。所以,我们实际使用中,常常看到的二进制是通过16进制表示的。

经过加入标准位后的UTF-8编码比实际的unicode编码会多出一些标志位,这样一来,其二进制串的长度也就会长一些了,当转为16进制的时候,与原始的unicode编码的二进制串转化为的16进制会有所不同,但是其实际的数据位(除去标志位后的那些二进制位)与unicode编码的二进制串就是一模一样的了。呵呵。UTF-8编码实际解码时,会根据位置标志位抽出数据位从而还原出unicode编码二进制串,对照unicode编码表来得到文件的真实字符内容(当然,这些都是通过软件来实现的。)

 

 

 

原创粉丝点击