📚 第一章 网络基础知识
📄 一句话概括

计算机通信只要求比特送到;信息通信还要求两端对人看到的内容有共识。 同一句话用不同编码承载,字节数就不一样——这就是「多出来的一层麻烦」。

📝 本节和前面几节的关系

1.1.2 把通道打通了,这一节讲通道里跑的东西。 1.1.4 会回到「通道拿来干什么」:共享资源是网络最早、也是最主要的用途。

一、两种「通」的区别

「计算机通信」这个词在书里指早期那种点对点传数据:把一台机器上的比特原样搬到另一台机器上。 「信息通信」多了一层:搬过去的比特要能被理解——文字要认得出字,图像要认得出画面。

关注点 计算机通信 信息通信
成功标准 比特到齐 人能看懂
要协商什么 速率、校验 编码、格式、压缩方式
出错表现 丢包、错包 乱码、花屏、打不开

第三行那个「乱码」是最典型的症状:比特一个没丢,但两端用的码表不一样。 所以「信息通信」的排障和「计算机通信」完全不同:前者查编码声明,后者查链路质量。

二、编码决定字节数

同一段文字,用不同编码写出来长度不同,因为这个字符集里每个字符占几个字节不一样。

编码 ASCII 字符 汉字 特点
ASCII 1 字节 编不出来 只有 128 个字符
UTF-8 1 字节 3 字节 变长,兼容 ASCII
GBK 1 字节 2 字节 中文专用,双字节
UTF-16 2 字节 2 字节 定长,ASCII 也占 2

第二行那个 3 字节是 UTF-8 的关键设计:常用汉字落在三字节区,生僻字落在四字节区。 而 ASCII 只占 1 字节——所以「UTF-8 兼容 ASCII」这句话是字面意思: 一段纯英文文本,存成 UTF-8 和存成 ASCII,字节完全一样。 这也是 UTF-8 能取代 GBK 的技术原因之一:它不需要为英文单独优化,天生就省。

三、声明在哪里

接收端怎么知道该用哪张码表?靠声明。 声明的位置分三层:

层 写在哪 例子
传输层 HTTP 头 Content-Type: text/html; charset=utf-8
文档内部 HTML 的 meta <meta charset="utf-8">
文件开头 BOM EF BB BF 三个字节

第三行那个 BOM 是最容易出问题的一种:它在文件最前面塞三个看不见的字节, 有些程序(尤其是脚本解释器)会被它绊倒。 排障经验:看到「第一行明明是对的却报语法错」,先查有没有 BOM。 用十六进制看一眼头三个字节就知道——EF BB BF 就是它。

四、动手 Demo:从计算机通信到信息通信

把同一句话用四种编码写出来,比字节数,并检查能不能原样还原。 这段文字故意混了汉字和 ASCII,这样四种编码的差别才看得出来。

s = u'图解TCP/IP'
for enc in ('ascii', 'utf-8', 'gbk', 'utf-16-le'):
    try:
        b = s.encode(enc)
        print('%-10s %2d 字节  %s' % (enc, len(b), b[:12].hex(' ')))
    except UnicodeEncodeError:
        print('%-10s 编不出来' % enc)

实跑输出:

--------------------------------------------------------------------
从计算机通信到信息通信
--------------------------------------------------------------------
**「计算机通信」关心的是把比特送到;「信息通信」关心的是送过去之后人能不能看懂。**同一句话,
用不同编码承载,字节数就不一样。

  编码          字节数     前 12 字节                              能还原
  --------------------------------------------------------------------------
  ascii         —          —                                       编不出来
  utf-8         12         e5 9b be e8 a7 a3 54 43 50 2f 49 50     能
  gbk           10         cd bc bd e2 54 43 50 2f 49 50           能
  utf-16-le     16         fe 56 e3 89 54 00 43 00 50 00 2f 00     能

**同一段文字,ascii 直接编不出来、utf-8 要 12 字节、gbk 要 10 字节、utf-16 要 16 字节。**
**汉字越多,几种编码的差距越大**:utf-8 一个汉字占 3 字节,gbk 占 2 字节,
utf-16 一律 2 字节但每个 ASCII 字符也要 2 字节。

**这就是「信息通信」比「计算机通信」多出来的那一层麻烦**:
**比特送到了还不算完,两端得先说好用哪张码表。**
**HTTP 里那个 `Content-Type: text/html; charset=utf-8` 就是干这个的。**

ascii 那一行是「编不出来」,不是「编出来更短」—— 「图解」两个汉字不在 ASCII 的 128 个字符里。 这一行最能说明「编码是一张表」:表里没有的字符,编码器只能报错,不能凭空造。 注意 utf-16-le 那行的前几个字节 fe 56 e3 89: 「图」的 Unicode 码位是 U+56FE,小端存放就变成 fe 56。 这也是为什么叫「小端」——低位字节放前面。

30 秒实验

  1. 把字符串换成纯英文 u'hello',看四种编码的字节数变成多少,验证「UTF-8 兼容 ASCII」这句话。
  2. 把字符串换成 u'龟'(一个四字节 UTF-8 的字?先查一下),看 utf-8 是不是真的变 4 字节。
  3. 试试 u'\ufeffhello' 编码成 utf-8,看头三个字节是不是 ef bb bf。

常见误区

  1. 以为 UTF-8 里一个汉字总是 3 字节。 常用字 3 字节,生僻字 4 字节,扩展区甚至更多。
  2. 以为 ASCII 是 UTF-8 的子集所以可以混用。 短文本没问题,长文本会有 BOM、行尾等差异, 混用迟早出事。
  3. 以为乱码是「数据坏了」。 乱码的数据往往一个字节没少,只是两端码表不一致。 改声明就能修好。
自测三问(点开对答案) 1. 为什么「UTF-8 兼容 ASCII」这句话是准确的? —— **因为 UTF-8 里 U+0000~U+007F 的编码就是一个字节,值与 ASCII 完全相同。** 纯 ASCII 文本存成 UTF-8,字节一个不差。 2. 同一句中文,UTF-8 和 GBK 哪个更短?为什么? —— **GBK 更短,因为汉字在 GBK 里固定 2 字节,在 UTF-8 里通常 3 字节。** 但 UTF-8 的 ASCII 部分省一半,**混排文本要看比例。** 3. 「第一行看起来正确却报语法错」,第一步查什么? —— **查文件头三个字节是不是 `EF BB BF`(BOM)。**很多解释器不认 BOM,会把它当成代码的一部分。

下一节回到用途:通道有了、编码定了,网络最早被拿来干的事是「共享」。

相关笔记