本文将简要介绍关于网络通信的基本原理. 读完本文, 各位应能大概能明白网络通信过程是如何进行的, 同时, 对此领域的常见术语能有所了解.

1. 网络模型

你可能已经瞄到下面的图了, 是不是想打退堂鼓了? 呵呵~ 别急, 别忙着放弃! 继续往下看, 我会尽量让你不晕 ~

OSI 参考模型 和 TCP/IP 五层模型 是我们讨论网络通信问题很常用的网络模型.

网络模型

按不同的视角, 我们通常把计算机网络模型分作七层 ( OSI 模型 ) 或 五层 ( TCP/IP 模型 ), 它们之间并不矛盾, 只是 OSI 模型中的 “应用层、表示层、会话层” 在 TCP/IP 模型中合并为一层, 即 “应用层” .

问题来了… 为什么要分层? 各层负责干什么事? 各层之间如何协作? 实际应用场景下它们是如何工作的? 听我慢慢道来 …

1.1 为什么要分层?

为什么要提出分层的网络模型? 答案很简单, 因为分层便于设计、实现和管理. 按软件工程的思想, 这就叫 “分而治之”.

在现实生活中, 无论是一个公司或是平顶山莲花洞中的妖怪, 为了便于组织和管理, 我们都会建立所谓的组织架构.

公司架构: 总经理 → 副经理 → 部门经理 → 小组长 → 苦力

妖怪集团: 金角大王 → 总钻风 → 小钻风 → 小妖

金角大王想吃唐僧肉, 于是招来总钻风, 要他把唐僧肉给安排上, 于是总钻风找来小钻风, 添油加醋, 这般那般安排一番, 小钻风又招来一群小妖, 让他们去偶遇唐僧…

这个故事告诉我们, 一旦建立了层级架构, 金角大王只要”想”就行了, 任务会一层层下行, 每一层只要干好自己该干的事情就行, 总钻风无须去管小妖们要不要在巡山的过程中唱上两句, 一切由下级自己处理. 同时, 我们也看到, 任务在下行的过程中会被逐层加工处理, 并逐步具体化.

这回明白为什么要建立分层的网络模型了吧?

最靠近用户的应用层软件( 比如 QQ ) 负责把用户的思想 ( 意图 ) 数字化, 然后交给下层, 逐层处理.

对于在网上追女朋友这件事情, 计算机系统传递的是你数字化的思想, 这事, 你滴, 能理解不? 不然 … 就洗洗睡吧, 你就是传说中的母胎 Solo.

对于多数软件开发工作而言, 主要完成的就是应用层软件的实现, 在这方面有更多更好的理论和分层思想. 因此, 个人觉得多数情况下没必要按 ISO 模型那样对”应用层”进行划分, 至少对于多数软件系统开发而言, 这种划分方法有些诡异和别扭.

本文基于 TCP/IP 五层模型进行讨论.

1.2 数据如何在各层之间传递?

前面我们讨论了分层的必要性, 扯到了金角大王谋算唐僧的故事, 下面就让我们在跑偏的路上继续前行吧!

金角大王想把唐僧送给身处十万八千里外的银角大王, 于是他找来了总钻风安排这个事情.

总钻风手起刀落, 把我们可爱的唐同学切成了数块, 然后给每一块编上号, 找来数个大麻袋, 贴上标签, 注明收货人地址, 发货人地址等信息, 然后交给了小钻风. ( 画面过于血腥, 此处省略数千字… )

小钻风一看, 妈呀! 这一坨坨的是什么呀! 不管三七二十一, 继续切, 于是可怜的唐同学被分成了更小的块. 当然, 小钻风依然给这每一小块编了号, 贴好标签(总钻风贴的标签被复印了数份, 一起附上), 然后转给了底下的小妖.

小妖们都是狠角色, 直接把唐同学剁成了肉泥, 继续给肉泥们编号, 贴标签. 最后找来了搞货运的蚂蚁大军, 一顿操作后, 只见那乌泱泱的 Parts of Tang 就这样一路绝尘而去了…

数日后, 蚂蚁们陆续到达了银角大王驻地. 银角大王团队的组织架构竟和金角大王那边一样, 也有总钻风, 小钻风和小妖, 嘿嘿~ 你说巧不巧!

于是, 众小妖一顿忙活, 卸货, 然后…… 把唐同学的肉泥按编号组装成了小块, 交给小钻风. 然后, 小钻风把小块按编号组装成了一坨坨的样子, 转交给总钻风. 再然后, 总钻风按编号把这坨坨的东西组装了起来. 是的, 你没看错, 唐同学又直挺挺地还原回来了! 再再然后, 总钻风把玉树临风的唐同学交给银角大王.

嘿嘿~ 怎么样, 你有穷的智慧是不是严重限制了你的想象力? 我就喜欢这样, 一本正经地瞎扯!

好了, 言归正传! 研究网络通信的先辈们参考我上面这个故事, 建立了网络通信模型 ( 如下图 ), 数据的传输过程就如图中那条血腥的红线…

网络模型

思考题: 如果把妖怪集团的组织架构对应到 TCP/IP 五层网络模型, 那么总钻风, 小钻风, 小妖, 蚂蚁货运团队分别属于哪一层呢? 如果暂时想不明白, 别急, 继续往后看就会明白.

2. 硬核技术篇

虽然从发送数据的角度看, 数据是自上而下流动的, 但本篇我们按自下而上的顺序来详聊网络模型中各层的故事, 这样更容易理解.

2.1 物理层 ( Physical Layer )

物理层解决的问题是把各种需要进行网络通信的设备连接起来.

物理层规定了工作在这一层的设备的机械特性、电气特性 等.

呵呵, 又开始不说人话了… 举个例子: 规定信号在我们常见的双绞线 (机房中插在电脑屁股上那根) 中传输时的延迟和衰减极限(电气特性), 光缆的最大可弯折程度(机械特性).

常见的物理层的东西包括: 各种传输介质 ( 双绞线、光纤、无线电波 ), 各种网卡( 有线的、无线的 ), 还有各种传说中的: 同轴电缆, 中继器, 集线器…

中继器(repeater): 主要对信号进行整形和放大, 以让信号可以传得更远.

集线器(hub): 用于把多根网线集接到一起, 你可以想像把多根网线剥开, 然后把每根芯对应接好的样子, 只是集线器把这件事做得更美观一些. 当然, 集线器顺便也对信号进行了整形和放大, 即兼具中继器的作用.

物理层的东西对于码农来说关系不大, 除非女朋友说你再不陪我去看电影, 我就拔你网线, 你才会意识到它的存在.

在物理层流动的是形如 1010101100 … 11011101 这样的比特(bit)流, 如果丢给你这样一串东西, 告诉你这是女朋友给你的情书, 估计你瞬间就崩溃了吧. 于是我们引入数据链路层.

2.2 数据链路层 ( Data Link Layer )

数据链路层首先要解决的问题是: 把物理层那些毫无意义的 1010101100 … 11011101 数据切分成有稍意义的数据段, 同时规定每段数据的含义.

这里的每一段数据我们称作: 帧(Frame).

早期, 各个公司对于数据帧有自己的定义, 但大浪淘沙后以太网协议(Ethernet Protocol) 最终留了下来. 它一直沿用至今, 深深地影响着数据链路层的世界.

2.2.1 以太网协议

下图是以太网 ( Ethernet ) 协议规定的数据帧(以太帧)的样子:

网络模型

  • Header : 首部, 长度 14 字节, 其中包含了目的地址(6字节)、源地址(6字节) 和 数据帧类型信息(2字节, 用于标识这个帧应由哪个上层协议处理, 如 0x0800 表示此帧应由 IP 协议处理).

  • Data : 数据部分, 长度 46 ~ 1500 字节. 若数据不足 46 字节则补足, 若超过 1500 字节则分解为多个以太帧. ( 唐同学就在这里被剁成了肉泥 )

  • FCS: 帧检测序列, 4 字节. 简单来说, 就是用来检测帧数据是否出错的冗余校验码.

下面我们重点聊聊 Header 部分的东西…

2.2.2 MAC 地址

数据帧 Header 中的目的地址和源地址是以 MAC 地址 ( Media Access Control Address ) 的形式表述的, 如: f0:18:98:34:e9:af

其中前 3 字节是生产厂家编号, 后 3 字节是厂家生产流水号.

以太网中的每个设备都应该有一个 MAC 地址, 设备出厂时就已经由厂家写好了. 否则数据该发到哪里鬼才知道…

MAC 地址常被 “专业人士” 称作二层(数据链路层)地址.

我们也可以手动改写一个设备的 MAC 地址, 但如果两个设备的 MAC 地址一样, 会发生什么事情? 反过来说, 什么情况下我们会希望两个设备的 MAC 地址一样? 这个问题也许看完本节, 你就会有感觉.

2.2.3 广播

在传统总线型以太网内, 通信基本靠”吼”, 讲的文明些, 叫广播(Broadcast).

怎么个”吼”法呢? 看下面这个图:

网络模型

假设图中 ① 要发送数据给 ②, 于是 ① 直接对着网络一阵吼 ( 把含有数据的数据帧直接丢到网络内 ). 因为是吼, 所以 ②③④⑤ 全听到了. 但别忘了, 数据帧 Header 部分含有目的地的 MAC 地址信息, 将目的地址与自己的 MAC 地址进行对比, 发现不是发给自己的帧就直接装作没听见(丢弃), 而对于 ② 而言, 发现这个数据帧是发给自己的, 就接收下来作后续的处理. 这就是传说中的广播通信!

若数据帧的目的 MAC 地址写作 ff:ff:ff:ff:ff:ff 则网内所有设备均会接收/处理, 此 MAC 地址称作广播地址.

细心的同学可能发现, 上图的所谓总线型网络怎么像接水管一样的接法…… 呵呵, 想当年, 我和同学在宿舍里组的第一个网就是这种总线型结构, 为的就是省下买集线器的钱去吃顿肉. 哎~ 往事不堪回首, 暴露年龄了…

生活中更常见的网络拓扑结构是星型结构(见2.2.4节), 多台电脑连接到一台交换机上, 交换机就是中心那颗最亮的星.

2.2.4 集线器与交换机

如果你对硬件设备不感兴趣, 或是打死也不干网络布线的工作, 只想安安静静地当一个码农, 那么本小节内容你可以直接跳过.

上面我们提到集线器和交换机, 它们都可以把多台主机连接成如下图所示的结构:

网络模型

  • 看到这个图是不是觉得我在忽悠你? 中间那坨东西明明就长得一个样, 凭什么左边那坨叫集线器, 而那右边那坨则叫交换机? 嘿嘿, 事实上, 生活中的集线器和交换机在外观上确实长得差不多. 那它们有什么区别呢?
  • 另外, 是不是组成上图结构的网就一定是所谓的”星型结构”呢?

在 2.1 节中我们提到集线器是一个把多台主机连接在一起的设备, 仅做物理链路上的信号连通和放大, 因此它属于物理层设备.

可以简单理解为网线就像这样被粗暴地拧在一起, 呵呵 ~

网络模型

所以, 上面那个使用集线器连接起来的, 长得确实很像星状结构的网络, 其实是”伪”星型结构, 本质上仍是总线型结构. 只是所谓的总线很短, 就一个点而已, 想一下, 是不是有点道理?

传统总线型以太网中, 大家想传数据都直接吼(广播), 想像一下早晨菜市场的混乱场面…. 为避免大家一起吼, 最终谁也没法正常交流, 引入了 CSMA/CD ( 载波监听多路访问/冲突检测 ) 等协议来避免这种混乱的发生.

关于 CSMA/CD 大家根据名字猜一猜, 就大概能体会它是干什么的东东了, 本文不再展开.

有同学可能听过”广播风暴”这个词, 它包含上面所说的”大家一起乱吼”的情形, 但不仅限于此, 关于这个话题, 说来话长, 不说了.

使用集线器连接的网络中, 各台主机处于同一个冲突域(碰撞域)内, 所以事实上在某一时刻只能有一台主机发送数据, 而另一台主机接收数据. 这样就使得通信效率大打折扣, 相当于所有主机要均分有限的带宽资源.

在人民生活水平日益提高的今天, 集线器基本上已很少使用了…

接下来我们看个稍高档些的设备:

网络模型

这个玩意儿叫交换机, 它比集线器更”聪明”, 通过”学习”它能逐步记住连接在它各个端口上的主机的 MAC 地址(如下图), 这样在 A 要发送数据给 B 的时候就没必要傻傻地把数据广播给网络内所有的主机, 只需要通过连接 B 的端口将数据转发给 B 就行.

网络模型

使用交换机连接的以太网可称作交换型以太网, 而交换机被归为数据链路层设备.

下次进机房的时候, 记得看看传说中的交换机.

如果你钱多, 可以使用更高档的 “三层交换机” , 也就是可工作在网络模型的第 3 层 (网络层) 的交换机, 它牛在什么地方? 看完 2.3 节估计就能明白.

~ ~ ~ ~ ~ ~ ~ ~ ~

华丽丽的分隔线后, 我们来小结一下. 上面说那么多权当小说看就行. 一把年纪了, 记住以下 3 点就算及格:

  • 数据链路层的通信是基于 MAC 地址进行的.
  • 数据链路层通信的最小单位是 “帧 (Frame)”, 帧有头(Header), 身体(Data) 和 屁股(FCS), Data 部分最多 1500 字节.
  • 以太网是一个数据链路层的概念, 传统的以太网为总线型结构, 而目前更常见的是星型结构.

理论上, 我们可以凭借这种简单粗暴的方式把全世界的计算机连成一个巨大的网络, 大家就可以开心的聊天了~

嘿嘿 ~ 别太天真~ 你宿舍里那几台电脑买个交换机, 组个局域网, 几个同学玩游戏还行, 若要玩真正的网游 ( Internet 上玩的那种 ), 或是和千里之外的小女朋友聊天, 那还是老老实实往下看吧…..

在网络模型中, 以及各种网络协议中都会出现类似 “以太帧” 这样的东西, 它的本质是 “数据交换单元”, 也就是说, 以太网中使用 “以太帧” 作为数据交换单元.

但是, 在网络模型的不同层次, 或不同的网络协议中, 对于上面所说的 “数据交换单元” 有不同的称呼, 比如: 帧(Frame), 包(Packet), 数据报(Datagram), 数据段(Segment), 消息(Message) …

相当晕, 对吧? 本文的定位是 “科普类” 文章, 并不想搞得太专业, 大家被这些名词绕晕就不好了, 更没必要去详细区分到底该叫数据包, 数据报, 还是数据段… 所以, 在本文后续的描述中, 我们统一把上面说的 “数据交换单元” 叫做 “PDU ( Protocol Data Unit, 协议数据单元 )“.

哎哟, 妈耶! 太拗口! 就直接简称 “数据包“ 吧! 这样我们就不用再去”咬文嚼字”, 大家知道本文中出现的 “数据包” 指的就是网络模型或各种网络协议中的 “数据交换单元”即可. 专不专业的不重要, “好使” 就行!

好了, 统一口径后, 我们继续……

2.3 网络层 ( Network Layer )

考虑到通信隔离、交换效率、组网的可行性等更复杂问题, 我们会把网络划分成多个子网, 比如下图的样子:

网络模型

在上图两个子网的内部, 其实就是上节说的以太网, 若要在子网内传输数据, 只需把数据封装在以太帧中, 同时在其 Header 部分附上收、发方 MAC 地址, 丢到物理层, 接收方即可收到并处理这个数据包.

那么, 不同子网的两台主机之间 ( 如: ② ↔ ⑤ ) 如何通信呢?

注意到图中那个蓝色的东东了吗? 它叫网关(Gateway), 负责连接不同的子网, 在子网之间进行数据交换.

现实生活中, 我们常用 “路由器(Router)” 来充当网关. 但应注意, “网关”更多是一个通信中的概念, 充当网关的”实物”可以是路由器, 三层交换机, 一台开启了路由协议的计算机或是别的什么东东.

在数据链路层, 我们借助 MAC 地址进行通信, 而在网络层, 我们则引入了一套新的地址系统: IP 地址.

2.3.1 IP 协议

IP 协议(Internet Protocol, 因特网协议) 规定了网络内的每个设备得有一个网络地址, 即 IP 地址.

根据 IPv4 标准, IP 地址长度为 32 bit, 即 4 字节, 通常表达为十进制形式, 例如: 192.168.1.3. IP 地址范围为 0.0.0.0 ~ 255.255.255.255

IP 地址和 MAC 地址之间没有必然的联系, MAC 地址是设备的物理地址, 通常由生产厂家在设备出厂时写入, 而 IP 地址由用户自行设定或由 DHCP 服务器分配.

  • IPv6 标准本文不涉及, 感兴趣的同学请查阅别的资料.

网络层中 IP 协议的数据包是这个样子滴:

网络模型

和以太网的数据帧相比, 少了个屁股(FCS), 但也有 Header 和 Data . 不过, 它的头可比以太帧的头复杂得多, 我们不必了解细节, 但可以猜得到, 必然会包含源 IP 地址和目的 IP 地址. 另外, 数据(Data) 部分的最大长度为 65535 字节. 若实际要传输的数据超过 65535 字节, 则会被拆分成多个 IP 数据包. ( 可怜的唐同学~ )

IP 数据包打包好后会交给数据链路层, 封装成以太帧的样子 ( 如下图, IP 数据包被放在了以太帧的 Data 部分 ).

网络模型

还记得吗? 以太帧的 Data 部分最大长度是 1500 字节, 如果一个 IP 数据包的大小超过 1500 字节, 会被切成多个以太帧 …. ( 可怜的唐同学!!! )

接下来, 我们讨论 3 个问题:

  • 子网内和子网间的通信方式是不一样的, 那么, 如何判定两台主机是否属于同一个子网呢?
  • IP 地址是如何得到的?
  • 如何把 IP 地址与 MAC 地址关联起来?

先上个图, 这是网络配置时经常会见到的界面. 配合这个图, 我们在 2.3.2 ~ 2.3.4 小节分别来回答这 3 个问题.

网络模型

2.3.2 子网掩码

子网掩码即是用来判定两台主机是否属于同一个子网的东东.

例如: 若要判断两台 IP 地址分别为 192.168.1.3 和 192.168.1.4 的主机是否属于同一子网, 配合上图中子网掩码: 255.255.255.0, 我们可以这样来…

把两台主机的 IP 地址分别和子网掩码进行二进制按位与(&)运算, 若运算结果一致则说明这两台主机属同一子网.

呵呵, 怎么个”与”法, 看下图 192.168.1.3 和 255.255.255.0 做”与运算”的样子:

625-13

有点晕!

另一个运算: 192.168.1.4 和 255.255.255.0 做与运算你自己来! 最终会发现结果也是 192.168.1.0 , 因此这两台主机属于同一个子网.

某些时候, 我们会把网卡配置会写成这个样子: 192.168.1.3/24, 它的意思是: IP 地址 192.168.1.3, 而 24 是子网掩码中 1 的个数 (子网掩码总共是 4 字节(32bit), 其中前 24 位都是 1, 剩余位数是 0 ) , 换算成我们熟悉的样子, 其实就是在说, IP 地址是 192.168.1.3, 子网掩码是 255.255.255.0

子网掩码和主机 IP 地址进行”与运算”的结果被称作”网络号” 或 “网络地址”. 所以, 可以这样说, 网络号相同的那些主机就属于同一子网.

例如: 前面的例子, IP: 192.168.1.3, 子网掩码: 255.255.255.0, 计算之后可知网络号是: 192.168.1.0, 在这个子网中最大可容纳的主机数是 256, 但是可用的 IP 地址只有 254 个: 192.168.1.1 ~ 192.168.1.254, 因为 192.168.1.0 是网络号, 不能用, 而 192.168.1.255 是这个子网的广播地址.

来~ 做个测试题, 看看你学会了吗?

若一台主机的网卡配置是: 192.168.1.53/27 , 请写出: (1) 这台主机的 IP 地址; (2) 子网掩码; (3) 该子网的网络号(网络地址); (4) 该子网中可用 IP 地址范围; (5) 该子网的广播地址

答案: (1) 192.168.1.53 (2) 255.255.255.224 (3) 192.168.1.32 (4) 192.168.1.33 ~ 192.168.1.62 (5) 192.168.1.63

2.3.3 DHCP

一台主机的 IP 地址、子网掩码、网关可以由用户自己指定 ( 如上图 “使用下面的 IP 地址”, 然后自己填! ). 对普通老百姓来说, 这也太特么专业了点! 再说了, 万一”专业人士”把子网内多台主机的 IP 地址写成一样了, 不就冲突了吗?

所以, 为了简化这个专业问题, 搞出了一个东东, 叫 DHCP ( Dynamic Host Configuration Protocol,动态主机配置协议 ).

DHCP 负责给子网内的主机分配 IP 地址, 告诉它们子网掩码、网关等信息. 专业点说… 主机(DHCP 客户端) 以广播方式发送报文询问”我还没有 IP 地址, 谁来管管这个事?”, DHCP 服务端收到询问后就回应这台主机…

如果你有进入路由器管理界面乱搞一通的经历, 应该看到过这个所谓的 DHCP 服务的配置界面.

当然, DHCP 服务不一定非得在子网的网关位置, 也可以在别的地方 ( DHCP 客户端广播可到达的任何地方 )

这里提到 DHCP 是因为它帮我们处理了一些网络层的事情, 所以顺带提一嘴. 但请注意 ** DHCP 不属于网络层协议, 它是应用层的协议**.

2.3.3 ARP

MAC 地址怎么和 IP 地址关联在一起呢? 这就得靠 ARP 协议了 ( Address Resolution Protocol, 地址解析协议 ).

其实, 我不用解释, 估计你也能大概猜到这个ARP 是怎么个玩法:

发送数据的主机, 若不知道对方的 MAC 地址, 则广播一个 ARP 请求, 带上对方的 IP 地址. 因为是广播, 所以广播可达所有主机都可以收到这个 ARP 请求. 但当然只有那台 IP 地址匹配的主机才会回应:”我的 MAC 地址是 xxx” .

这样 IP 地址 和 MAC 地址就关联起来了. 注意是根据 IP 查 MAC, 别整反了…

呵呵, 说得轻巧! 不过, 当个码农, 知道这点就差不多了~

2.3.4 ICMP

ICMP (Internet Control Message Protocol, 互联网控制报文协议). 由于网络环境的复杂, 加之很多协议并不完全”可靠”, 因此引入了 ICMP 协议, 用于描述网络是否通畅、主机是否可达、路由器是否可用等网络状态. 虽然 ICMP 协议的数据包并不传输用户数据,但是对于用户数据的传递起着重要的作用.

呵呵, 这段有点突兀, 哈哈 ~

突然提到 ICMP 是因为我们常用的 ping 命令其实就是使用 ICMP 的消息来实现的, 当然 traceroute 命令也是如此. 而 ICMP 属于网络层协议, 我们不正在讲网络层的事情吗, 所以就提一下…

ping 命令常用来测试两台主机之间网络是否通畅, 数据是否可到达对方.

通俗讲: 在计算机 192.168.1.1 控制台执行 ping 192.168.1.2, 则会发送一个数据包给 192.168.1.2, 对方收到后会回应一个数据包, 以此来判断数据是否双向可达, 同时也可知道网络的延迟情况.

( 前方高能预警! 安静的码农可跳过 2.3.5 节… )

2.3.5 子网间到底是怎么通信的?

前面的内容可能让你明白了什么是子网, 子网间需要依靠网关进行通信, 但它到底是怎么通信的呢???

来看下面这个图:

625-13

左边蓝色圈圈中所有计算机子网掩码设置为 255.255.255.0, 网关设置为 192.168.1.250

右边黄色圈圈中所有计算机子网掩码设置为 255.255.255.0, 网关设置为 192.168.2.250

通过计算可知: ①②③ 同属一个子网, 网络号为: 192.168.1.0, 而 ④⑤⑥⑦ 则属于另一个子网, 网络号为 192.168.2.0.

先看子网内通信的情况: 若 ① 要发数据给 ③, 源 IP 地址为 192.168.1.1, 而目的 IP 地址为 192.168.1.3, 源地址与目的地址属同一子网.

(1) 将数据被封装成 IP 数据包, 直接递交数据链路层

(2) 查询 ARP 缓存, 看是否能找到 192.168.1.3 这个目的 IP 地址对应的 MAC 地址, 若找到, 则直接使用缓存的 MAC 地址, 若找不到则发送 ARP 广播, 询问你们谁是 192.168.1.3 呀? 把你的 MAC 地址告诉我! 收到回复后则使用目的 MAC 地址打包以太帧, 并丢到网络上. ( 同时缓存 IP 与 MAC 的对应关系 )

再看子网间通信的情况: 若 ① 要发数据给 ⑤, 源 IP 地址为 192.168.1.1, 而目的 IP 地址为 192.168.2.5, 源地址与目的地址不在同一子网

(1) 将数据封装成 IP 数据包(目的地址 192.168.2.5), 但因目的地址与源地址不在同一子网, IP 数据包封装为以太帧时目的地址取网关192.168.1.250 的 MAC 地址. 也就是说, IP 数据包通过二层链路到达路由器.

(2) 路由器从数据包中取出目的 IP 地址, 通过查询路由表知道发送向目的 IP 192.168.2.5 的数据包应从端口 2 出, 于是将数据包通过端口 2 丢入子网 2

(3) 数据包在子网 2 中畅游, 来到计算机 ⑤, 拆快递, 得到数据.

在上面的故事里, 出现一个新名词: 路由表, 它存储于路由器中, 一个简化版的路由表大概是这样子的:

Destination Mask Interface Matric
192.168.1.0 255.255.255.0 192.168.1.250 1
192.168.2.0 255.255.255.0 192.168.2.250 1

遍历路由表中的每一行, 用数据包目的 IP 与 Mask 做与运算, 看它的结果是否等于 此行的 Destination 与 Mask 做与运算的结果, 若相等则说明此行的路由方案可行.

例如: 上表中, 第 2 行:

数据包的目的 IP 192.168.2.5 & 255.255.255.0 = 192.168.2.0

Destination & Mask = 192.168.2.0 & 255.255.255.0 = 192.168.2.0,

因此匹配此路由, 于是数据包从出口 IP 192.168.2.250 ( 绑定端口 2 ) 送出.

实际的环境中, 网络形成一个真正的”网”, 同一个数据包可能可以从不同的路径到达目的地, 所以在路由表中还有一个字段: Matric, 用于记录”路由成本”, 有多条路由方案可行时, 路由器会选择路由成本较小的链路将数据包送出.

路由表可以是人工写入, 称静态路由表. 也可以是路由器自己通过送达自己的数据包 “学习” 得到, 称动态路由表.

路由表中通常有一个默认路由, 即: 当其它记录均不匹配时, 则从默认路由指向的端口出. 可以猜到, 默认路由经常就会指向上级的 Internet 入口.

是不是有个小疑问, 怎么路由器会有 2 个 IP 地址: 192.168.1.250 和 192.168.2.250 ?

嘿嘿, 你一台电脑装 2 个网卡都有 2 个 IP 地址, 人家路由器那么多端口, 有 2 个 IP 地址过份吗 ? 路由器的每一个端口都可以指定一个 IP 地址.

交换机也一堆端口呀, 能不能也给每个端口指定一个 IP 地址呢?

嘿嘿 ~ 交换机一个二层 ( 数据链路层 ) 设备, 只认识 MAC 地址, 你要 IP 地址留着过年吗 ??

注: 这里说的交换机指的是普通的二层交换机, 还有一种交换机称作 “三层交换机”, 顾名思义, 它具备第三层(网络层) 路由器的一些功能, 实现在子网之间的数据转发. 二层交换机用于小型的局域网内可实现快速交换、端口多、价格低廉, 是个不错的选择. 但如果把大型网络按照部门、地域等等因素划分成很多小局域网, 这将导致大量的网际互访, 单纯的使用二层交换机不能实现网际互访, 单纯使用路由器, 由于接口数量有限、路由转发速度慢, 将限制网络的速度和网络规模. 所以, 采用具有路由功能的快速转发的三层交换机就成为首选.

而三层交换机的最重要的功能是加快大型局域网络内部的数据的快速转发, 加入路由功能也是为这个目的服务的.

~ ~ ~ ~ ~ ~ ~ ~ ~

华丽丽的分隔线后, 老规矩, 小结一下:

  • 网络层借助 “网关(Gateway)” 解决了子网间通信的问题, 其通信以 IP 地址为基础.
  • IP 协议是网络层最亮的那颗星, 也正是因为它, 我们有了 IP 地址这个所谓的三层地址.
  • 网络层的 IP 数据包, 由 Header 和 Data 两部分组成, Data 部分最多 65535 字节. 数据打包好了, 将交给下层(数据链路层)继续处理.

2.4 传输层 ( Network Layer )

经历千山万水, 我们终于爬到了网络模型的第 4 层: 传输层.

先闲聊几句…

个人认为, 在网络层和传输层之间有一条”楚河汉界”, 网络模型的 1 ~ 3 层大多是搞通信的兄弟们奋战的领土, 而上方则多是搞软件开发的程序猿们蹿上跳下的空间. 当然, 搞通信的兄弟可能会跳出来说, 我们只负责 2 ~ 3 层 v

补充一句: 我说的是”大多数”情况下. 搞得很”深”的程序员也可能也会直接和更底层的协议打交道.

以上观点纯属个人看法, 若不同意就当我没说, 拒绝抬杠~

下面简单解释我为什么这么说?

网络模型的 1 ~ 3 层解决了主机到主机的通信问题, 算是”网通了”, 而从第 4 层 (传输层) 开始, 我们要开始解决程序与程序之间的通信问题.

是的, 就是这么简单的解释. 当然, 如果根本不明白我说什么…. 看完后文再回来站队……

在一台计算机上可能会运行着多个需要进行网络通信的程序, 比如, 你可能在使用浏览器上网, 打开 QQ 聊天, 同时还看着在线视频…

你的计算机怎么知道网络上回来的数据是应该给 QQ 还是给浏览器呢?

这就需要在 IP 地址下再细分一级: 端口(Port).

2.4.1 端口

注意! 这里所说的 “端口” 并非网络设备上那些插网线的洞洞 (物理端口) !

每一个需要通过网络交换数据的程序都需要获得至少一个端口的使用权.

事实上, 一个端口对应一个进程, 而一个程序可能会同时启动多个进程. 所以上面说 “至少一个”.

一旦某个端口被占用, 则别的进程就无法使用这个端口.

所以, 端口的加入解决了进程间的通信问题. 有些时候我们把 IP 地址 + 端口号称作 套接字(Socket).

每个端口有一个编号, 称为”端口号”, 在 0 ~ 65535 之间, 但是 1023 前的端口号一般被系统控制, 通常给那些后台比较硬的大人物使用.

比如, HTTP(80), HTTPS(443), FTP(20, 21), SSH(22), Telnet(23), SMTP(25), POP3(110) …..

作为一位合格的码农, 你还应该知道那些比较有名气的软件默认使用的端口号, 以免和人家硬刚. 比如: Microsoft SQL Server (1433), Oracle(1521), MySQL(3306).

《 RFC6335 》对各段端口号的分配有明确说明, 感兴趣可以查阅该文档. 但实际的情况是, 码农们通常在 1024 ~ 65535 之间掷骰子/看心情.

在传输层的世界, 有两颗最璀璨的星星: UDPTCP, 作为程序员, 接触最多的传输层的东西就是他们了.

所以, 下面隆重介绍…… ( 撒花~ )

2.4.2 UDP

UDP ( User Data Protocol, 用户数据报协议) 是一个非连接的面向数据报的通信协议.

关于 UDP 协议, 我们可以暂不深究它的名字里的那个”数据报”是什么鬼, 了解以下内容即可:

  • 非连接: 看到这个词是不是有些意外, 还有不连接就可以传数据的?? 呵呵, 这里据说的”非连接”指的是收发数据的双方在传输数据之前不预告需要”繁琐”地建立连接过程. 自然地, 发完数据也不需要所谓”断开连接”.

    发送方把数据封装成 UDP 数据包就直接丢到网络上, 接下来的事情就是默念六字箴言, 乞求上苍保佑数据包能被接收方收到…

    哈哈, 这事是不是听上去极不靠谱, 完全不符合理工科气质!

    所以, UDP 协议也被称做”不可靠”的协议. 不过, 话说回来, 现实的情况也没那么”不可靠”, 这都已经到网络模型第 4 层了, 你当前 3 层那些东东是吃干饭的吗?

    UDP 协议因为简单、粗暴、直接, 所以常被用来传输那些允许丢失、出错或重复的数据, 比如: 流媒体、网游、IP 电话(VoIP)… 也包括前面提到的 DHCP 和 后面要提到的 DNS … 在这些应用场景下, 速度往往是最重要的, 可不可靠… Who care? ( 是不是有点心疼你充的那些游戏币了? 呵呵~ )

  • 面向数据报: 和前面我们提到的那些协议一样, UDP 数据包也分为 Header 和 Data 部分 ( 不附图了, 自己脑补一下 ). UDP 协议并不对上层传来的数据进行拆分或合并, 也就是说, 它把上层给的数据加个头( Header ) 就直接丢给网络层了.

    说到这里, 是不是为唐同学松了口气, 终于不用被切了… 哈哈, 你想太多了, 切还是要切, 只是不在封装 UDP 数据报的时候切.

UDP 报文总长不能超过 65535 字节 (64K, 8 字节报头 + 65527 字节数据). 事实上, 如果网络层使用的是 IPv4 来传输, 由于 IP 数据报的 Data 部分最多 65535 字节, 所以留给 UDP 报文 Data 部分的空间还会更少(65507字节).

基于以上两点, 对于程序员来说, 你的程序应该处理好两件事:

  • 建立完善的机制, 对于 UDP 数据报丢失、出错、重复、乱序到达等情况进行处理. ( 放心! 如果是靠谱的网游, 你的游戏币没那么容易丢! )
  • 把数据切好再装到 UDP 报文的 Data 里, 要不然… 装不下!

2.4.3 TCP

TCP ( Transmission Control Protocol, 传输控制协议 ) 是一种面向连接的、可靠的、基于字节流的传输层通信协议.

看到这个 TCP 的名词解释, 是不是安心多了… 同样, 3 个关键词解释一下:

  • 面向连接: 和 UDP 不同, 使用 TCP 进行数据传输时要先”建立连接”, 传输完成后还要”断开连接”.

    在这里最有意思的故事莫过于 TCP 的 3 次握手和 4 次挥手了:

    假设 A 要向 B 发送数据, A 先发一个信息给 B: “准备好, 数据要来了” ( 握手1 ), B 回复: “收到” ( 握手2 ), A 再回复:”我收到了你的收到” ( 握手3 ).

    3 次握手后数据传输通道 ( 连接 ) 算是建立起来了, 然后 #$%^&*!@ 传数据.

    数据传输完后, A 向 B 说: “ 数据传完了, 拜拜!” ( 挥手1 ), B 收到后回复: “好的, 等我确认下数据是否无误” ( 挥手2 ), B 准备好后再回复: “好了, 已确认, 没问题, 拜拜!” ( 挥手3 ), A 回复: “拜拜!” ( 挥手4 ), 至此连接断开.

  • 可靠连接: 看上面的 3 次握手 + 4 次挥手你就能体会这有多么可靠了! 呵呵. 当然 TCP 还通过一套相当复杂的机制处理了数据丢包、出错等问题.

  • 基于字节流: TCP 常被称作”流协议”, 开发者在使用 TCP 时无须关心数据包的大小, 也就是说你可以把它当作一个数据传送带(流), 肆无忌惮地往上面丢你要传的数据, 协议本身会自行解决 分片、编号、组装、出错重发… 等问题. ( 切还是要切, 因为网络层的数据包还是有长度限制的, 只是不用你亲自操刀. 有时考虑到效率问题, 参考 IP 数据包长度, 亲自操刀切一切也是不可避免的 )

看完上面 3 点是不是觉得 TCP 还真是一个优质男友, 可靠、省心、源源不断, 哈哈 ~ 不过 TCP 相对来说仍有些自己的问题:

  • TCP 之所以感觉上用起来这么省心是因为协议本身处理了很多通信的问题, 这同时也导致了一些缺陷, 比如: 当你想拥有更灵活的控制权时, 它往往给你设置各种障碍.
  • TCP 相对复杂的机制导致它对系统资源消耗更多, 在需要一台服务器与很多台客户端连接时, 可能导致系统资源紧张, 延迟严重或干脆连接不上.
  • TCP 的机制决定了它是进程间一对一通信, 而 UDP 则可实现一对一/一对多/多对多通信.

一般而言, 如果偶尔发生延迟是可以容忍的, 那么使用 TCP 编程较为简便, 比如纸牌之类的游戏.

如果延迟是难以容忍的, 那么还是用 UDP 吧, 比如一些实时对战游戏.

当然, 使用 UDP 对程序员的编程能力要求也更高, 毕竟你要自己处理通信过程可能发生的复杂问题. ( 也有例外, 据说某知名游戏公司的网游用的就是 TCP, 只能说, 对于真正的大牛, 这都不是事! )

多说一句, 如果由客户端向服务器端间歇性的发送数据, 偶尔的延迟也可以容忍, 那么使用 HTTP / HTTPS 是不错的选择, 若还涉及服务器端向客户端推送数据, 那么 Websocket 可以了解一下.

~ ~ ~ ~ ~ ~ ~ ~ ~

本节小结:

  • 传输层解决了进程(程序) 间通信的问题, 在这一层使用的是 IP地址 + 端口号 ( 套接字 )
  • UDP 和 TCP 是传输层最为闪耀的两颗星, 它们各有优缺点, 有各自的应用场景. UDP 是无连接的, 不可靠的, 而 TCP 是面向连接的, 可靠的.
  • 无论使用 UDP 还是 TCP, 到了网络层和它们对接的都是 IP 协议.

~~ 知识小百科 ~~

在通信领域中 TCP 和 IP 如雷贯耳, 它们两合在一起成立了一个大家庭, 称作 TCP/IP 协议族 ( TCP/IP Protocol Suite ) , 很多小弟都被收到了这个大家庭中, 比如: ARP, IP, TCP, UDP, HTTP, HTTPS …. 回头看看本文开篇的那个网络模型, 连名字都叫 TCP/IP 模型 .

2.5 应用层 ( Application Layer )

应用层是码农们最应该熟悉的战场! 有人把它叫做”程序员层”, 呵呵~ 它是 TCP/IP 网络模型的最高层, 也是都接近用户的一层.

我们熟知的 HTTP(超文本传输协议), HTTPS(安全超文本传输协议), FTP(文件传输协议), SMTP(收 Email 用), POP3(发 Email 用), DNS(域名服务), SSH(用于加密安全登陆) ….. 都是应用层的东西.

列出上面这些应用层协议的目的是让你感性认识什么是”应用层”, 有感觉就够了. 因为…. 我不打算去作严谨的名词解释, 关键是… 我也不会 ~

2.5.1 HTTP

当我们想要浏览 CCTV 的网站, 会打开浏览器, 然后在地址栏输入: http://www.cctv.com, 回车, 然后就等网页内容呈现出来吧…

就这么一个简单的故事, 可能发生在你的每一天. 但在这个故事里, 有不少专业知识科普一下:

  • 故事中, 浏览器地址里输入的那串东西(URL), 开头的 “http” 称作协议头(scheme), 用来告诉浏览器, 你要使用什么协议去和 CCTV 网站服务器通信. 实际生活中你可能直接输入的是 www.cctv.com (域名, 不带协议头), 但其实浏览器会自动帮你补全.

  • 到了应用层, 数据包的结构变得更为复杂… HTTP 的 数据包分为请求数据包(客户端→服务器) 和 响应数据包(服务器→客户端), 它们在结构上有些差异, 但无论如何, HTTP 的数据包 打包完成后会递交给传输层的 TCP 协议继续处理, 然后…. 再往下是网络层的 IP 协议, 再往下….

  • HTTP 数据包中的数据是明文传输的, 即数据不加密. 这样在传输一些敏感信息时(如: 密码) 非常不安全. 当然, 还包括其它一些被坏人攻击的隐患. 因此, 近年来逐渐使用 HTTPS 来替代 HTTP.

  • 在 2.4 节 (传输层) 我们提到, 到了传输层, 程序(进程)间的通信是基于套接字 ( IP 地址 + 端口号 ) 进行的, 怎么来到这里 IP 地址不见了, 端口号也不见了呢?

  • 其实端口号还在, 上面地址栏输入的内容(URL) 完整写应该是: http://www.cctv.com:80, 最后的 80 即是端口号, 只是浏览器知道 HTTP 协议的默认端口是 80, 所以你即使不写, 浏览也会自动补上端口 80 这个信息. 同样, 如果使用 HTTPS 协议, 默认的 443 端口号也可以省略, 浏览器会自动补. BUT, 如果你的程序用的不是 80 端口, 比如我们在用 J2EE/PHP 开发网站时, 经常会用 8080 端口, 这时在访问网站时就必须主动写明端口号.

  • 上面的 URL 中没有出现 IP 地址信息, 是因为有 DNS 的帮忙 ( 下节就谈 DNS )

我们在浏览器地址栏中输入的那串字符, 它的专业名称叫 **URL ** (Uniform Resource Locator, 统一资源定位符)

URL 的完整格式是: scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

对于程序员来说, 关于 HTTP / HTTPS 应该了解的内容还很多, 但本文重在讲述网络通信过程, 所以, 就此打住了. 等有时间和心情, 呵呵, 我们另开一篇来讨论…

2.5.2 DNS

上一小节我们说到, 在 http://www.cctv.com这个 URL 中并未见到 IP 地址信息, 但传输层又是需要 IP 地址, 这可咋办呢?

IP 地址长的那个样子( xxx.xxx.xxx.xxx ) 非常反人类, 所以在更接近用户的这一层, 我们发明了一个新东西: 域名(Domain Name), 本例中即是 www.cctv.com

显然, 域名更符合人类习惯, 而且可以表达更多的信息, 比如: 网站所属的地域, 组织类型等.

那么, 问题来了, 域名怎么转换成 IP 地址呢? 这回就该 DNS (Domain Name System ) 上场了!

在 Internet 的各个角落分布着一些被称作 DNS 服务器的东东, 它们存储着域名和 IP 地址的对应关系.

当我们试图通过域名访问网络时, 我们所使用的程序(如:浏览器) 首先会查你本机的 DNS 缓存, 看能否找到此域名对应的 IP 地址, 若找不到, 则会自动联系 DNS 服务器, 获得域名对应的 IP 地址, 后面的故事就不讲了, 你应该可以想得到…

你可以打开 Chrome 浏览器的调试窗口, 当你访问 www.cctv.com时, 可以看到下图信息:

DNS

其中 182.240.160.100 即是 www.cctv.com 这个域名对应的 IP, 同时我们还可以看到其后跟着端口号(80), 它们组成了传输层需要的 Socket.

你也可以直接在控制台执行 ping www.cctv.com, 同样可以看到, 此域名最终会被解析为 182.240.160.100

一般而言, 在进行网络设置时我们无须主动设置 DNS 服务器地址, 可以设置为”自动获取”, 你的电脑会通过询问网关得到 DNS 服务器的 IP 地址.

但是, 万一你的电脑指向的 DNS 服务器挂了, 连不通, 写错了…. 那么… 呵呵, 恐怕你就无法正常打开网站了, 为什么呢? 自己开动小脑筋想想~

若发生上述不幸的事情, 可以试试使用这两个吉祥的 DNS 服务器地址: 114.114.114.114 或 8.8.8.8 , 前者是国内通用的DNS, 后者是 Google 公司提供的 ( 狡黠地笑一个 ) . 当然, 如果知道离你最近的 DNS, 理论上来说, 这是最好的.

~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

好了, 最终爬到顶啦~

偷个别人做的图来帮大家梳理一下.

DNS

( 做图的兄弟, 你是好人, 就别在意我偷你东西了, 谢谢啦! )

希望通过以上内容的讨论, 你已大致了解一段信息是如何经过网络模型逐层封装, 转换为最朴素的二进制流, 再最终通过物理链路层到达对方.

实际上, 在真实的网络环境中, 特别是进入 Internet 的世界后, 通信双方之间会有 N 台各种各样的网络设备, N 种不同的物理链路, 你的数据也会被各种各样的网络协议拆分、重组. 哎~ 脑壳疼!

学习网络通信领域的知识时, 通常我们是沿着数据的前进方向去理解的, 生活经验常常让我们习惯了沿着一条路一直向前走, 换句话说, 是一个平面的方向. 而讲到网络模型这个通信领域绕不开的话题的时候, 数据又是在网络模型的各个层之间上下移动的, 这样一路前进, 又一路上蹿下跳的运动方式可能就是让我们容易晕的主要原因吧~

因此, 再附一幅图, 希望对你有所帮助:

DNS

不解释, 你感觉一下就好 ~

3. 务实篇

本篇并不是要带大家去布线, 接网络, 而是讨论几个软件开发过程中可能遇到的问题. 常常因为我们对网络的认识不够, 而掉进坑里出不来.

你是否有这样的疑问:

(1) 我的电脑可以连上 Internet, 可以和朋友愉快的聊 QQ, 但为什么我在 “网上邻居” 找不到他 ?

(2) 我做了一个网站, 放在自己的电脑上, 为什么我自己可以访问, 却无法让别的城市的同学访问 ?

(3) 做了一个手机 APP / 小程序, 服务器端程序已启动, 在电脑上调试一切正常, 搬到手机上就怎么也连接不上服务端

带着这些疑问, 接下来, 我们讨论一些作为码农来说更现实的问题.

3.1 Internet 接入

通过第 2 节的学习, 我们知道了借助各种通信协议, 网络设备, 可以把计算机/手机/平板电脑… 连接起来, 即使它们不在一个子网, 也可以通过路由转发实现互通.

那你会不会在想, 按照这套思路, 我们添置更多的设备不就可以把更多的主机连接起来了吗? 要不干脆, 咬咬牙, 再多花点钱, 把世界上所有的计算机都连起来得了!

呵呵, 理是这么个理, 事实上 Internet 这个世界范围内的大网确实是靠着无数的网络设备连接起来的.

但关键是, 谁来管这个事 ?!

你在自己家里建一个网, 那你自己说了算. 在公司建一个网, 那老板说了算. 那要在全国, 仍至全世界建一个网, 谁能说了算呢? 谁来出钱?

实际的情况是, 一个家庭/公司/学校…的网络首先接入 ISP ( Internet Service Provider, 互联网服务提供商 ) 的网络, 而 ISP 则负责花钱搭建自己内部的网络, 同时不同 ISP 之间也相互联通, 于是相互交织, 形成了一张覆盖全球的网.

ISP 到底是谁? 中国电信, 中国联通, 中国移动…… 你滴, 明白?

下面大概展示了你的电脑/手机是如何接入 Internet 的:

DNS

看着也还蛮简单的嘛! 呵呵, 来看一下 ISP 的世界又是什么样子…

DNS

通常同一家 ISP 内部的网络质量相当好, 而不同 ISP 之间往往链路较少, 相比内部网络网速也就慢多了. 所以, 这世界上最遥远的距离莫过于从电信到联通…

因此, 不少大公司会选择在电信、联通、移动… 都放上他们的服务器, 为的就是向你提供最快的服务. 当然也可以把服务器直接放在所谓的双线机房.总之, 有钱就是任性!

顺带说一句, 我们常用的 CDN (Content Delivery Network, 内容分发网络) 也是类似的思想: 把资源放在离你最近的地方. ( 这里的最近不单指地理位置, 你懂的 ~ )

Joke Time ~ 大家快来围观~

DNS

上图年代久远, 在 N 个网站, N 篇帖子中出现, 号称 “中国骨干网拓扑图”.

拜托! 图题不是写了吗? 人家是 “CERNET 拓扑图”, OK ? 难道大家都看成 CHINANET 了 ?

即使是 CHINANET ( 中国公用计算机互联网, 163 骨干网 ) 那也不能代表全中国的骨干嘛…

说起这中国的骨干网呀…. Another joke !

时至今日, 很多一版再版的计算机教材中还在重复着这样的故事: 中国有”四大骨干网”: 中国公用计算机互联网 ( CHINANET ), 中国教育与科研网 ( CERNET ) , 中国科学技术网 ( CSTNET ) , 中国金桥信息网 ( CHINAGBN ) ….

难道我们还在用着 64K 的拨号上网吗? 其它的不说, 就说”中国金桥信息网”, 2002 年负责建设的吉通公司并入网通, 2009 年网通和联通合并, 这儿子的儿子都已经不在人世了……

那我怎么不找一张真图出来呢? 因为…. 我也是个 Joke, 我找不到…. 呵呵~

好了, 开心一刻过完了, 来看看截止 2020 年, 世界海底光缆的建设情况, 感受一下世界人民的互联需求. 呵呵, 这应该是真的…

DNS

跑太偏了…. 回来说点技术问题….

从上面 Internet 接入的过程我们可以看到, 整个 Internet 的结构是”立体”的, 普通网民的电脑 (手机, Pad…) 处于最底层, 而只有那些要向全世界人民提供服务的服务器 (如:网站服务器) 才会站在金字塔的最顶层.

为什么不把大家都放到一层 ? …. 你鬼打墙啊! 本小节一开始不就说了吗 …

事实上, 直接连接 ISP 的那一层绝大多数确实是位于顶层的, 而其它设备就逐层往下走了.

结果会出现什么问题呢?

比如, 一个学校通过中国电信的专线接入 Internet, 那么电信会给学校分配 1 个 ( 谈得好可能会多几个, 呵呵~ ) 所谓的公网 IP 地址 ( Public address, 也称外网 IP 地址 ), 而拥有这个公网 IP 的设备就处理金字塔的顶层.

为什么不多给几个公网 IP 呢? 因为… 地主家也没余粮啊! 如果是 IPv4 标准, 就 xxx.xxx.xxx.xxx 这种 IP 地址, 早就快用完了. 所以, 才搞 IPv6 嘛, 理论上有 2^128 个地址, 似乎有点用力过猛. ( 又跑偏了… )

那么学校里那么多台电脑要上网怎么办呢?

学校内部的电脑们接入校园网后, 就通过拥有公网 IP 的那台设备( S ) “代理” 接入 Internet.

也就是说, 如果你的电脑 A 接入校园网, 你想要看 CCTV 的主页, 那么 A 跟 S 说, 麻烦帮我去拿一下 CCTV 主页, 于是 S 联系 CCTV 网站服务器, 取回网页数据 ( HTML, CSS, JavaScript… ), 然后再递交给电脑 A, 电脑 A 又递给浏览器, 经过浏览器解释执行网页代码, 渲染, 最终在屏幕上呈现出 CCTV 主页内容.

这里面有一个问题, 学校内部的电脑们通过 S 代理接入 Internet, 对于它们来说, CCTV 网站服务器是 “可以看得见的”, 可以发送数据给它.

但是! 相反, CCTV 的服务器收到的网络请求是来自校园网出口服务器 S 的, 那么它回信也只会回给 S, 再由 S 交给 A. 即: CCTV 服务器不是直接与 A 通信的. 校园网内的电脑对 CCTV 服务器来说是”不可见”的.

那么, 能不能把 A 的 IP 地址直接告诉 CCTV 服务器, 让它主动发信息给 A 呢? ( 假设干爹是 CCTV 台长 )

不行! 因为整个校园网内, 只有出口服务器 S 拥有公网 IP 地址, 只有 S 和 CCTV 服务器是 “地位对等” 的, 它们之间可以直接通信.

而校园网内的电脑拥有的只是内部使用的私有 IP地址 ( Private address, 也称内网 IP 地址 ), CCTV 服务器看不到你, 也不能直接向你发信息. 同理, 校园网外的其他同学想直接和你的电脑通信也是不行的. ( 那怎么你和全国各位的同学可以愉快的聊 QQ 呢? 通过服务器中转, 点对点通信…. 这些, 我们以后再说… )

插一句~ 上面的这套机制其实在一定程度上保护了你的电脑, 要不然, 你的电脑直接在公网上裸奔, 只能祈祷它还能见到明早的太阳 ~

上面的故事看得很开心吧~

其中有个问题, 我一笔带过了: CCTV 服务器回传的网页数据来到学校出口服务器 S 的时候, S 怎么知道这个数据应该是给电脑 A 的呢 ?

在 2.4 节我们提到, 传输层通过 IP + 端口号 (套接字) 的方式实现了进程之间的通信. 换句话说, 实现了一个 IP, 多个进程和外界进行通信.

聪明的娃娃, 是不是已经想到怎么办了?

对, IP 之下不是还有端口吗? 可以利用端口转发, 将 S 的一个 IP 服务于多台内网电脑. 这属于 NAT ( Network Address Translation, 网络地址转换 ) 的范畴.

关于 NAT 的话题, 本想在本文用一个小节来简单介绍一下, 但是越扯越多, 甚至还还扯出 DDNS、内网穿透、 点对点通信等话题.

不想把本篇入门级的东西搞得太复杂, 所以, 这里我们暂时作罢.

~ ~ ~ 小实验 ~ ~ ~

如果你现在的电脑是通过 校园网/公司网/家庭路由网络/手机热点 接入 Internet, 那么可以来一起做这个实验.

(1) 查看你本机的 IP 地址: Windows 打开控制台, 输入 ipconfig; MAC / Linux 控制台输入 ifconfig. 应该可以找到一个类似 192.168.x.x / 10.x.x.x / 172.x.x.x 的 IP 地址, 这就是你电脑所谓的私有 IP 地址.

(2) 打开百度, 输入”ip”, 点击”百度一下”. 如下图, 红色框中的内容即是 Internet 上的服务器看到的你的 IP, 即外网用户看到的你的 IP.

DNS

这就是”代理”你接入 Internet 的那台设备的公网 IP 地址.

也就是说, 外网设备看到的”你”其实是那台公网设备, 真正的你对他们来说是”隐身”的.

~ ~ ~ 扩展小知识 ~ ~ ~

私有 IP ( 内网 IP 地址 ) 通常分为 3 类, 也就是说你的内网 IP 地址应在以下 3 类地址段内.

A 类:10.0.0.0~10.255.255.255

B 类:172.16.0.0~172.31.255.255

C 类:192.168.0.0~192.168.255.255

通常, 校园网这样相对较”大”的网会使用 A 类地址, 而家庭/小公司会使用 C 类地址, 你如果使用手机热点上网的话, 可以看到, 应该用的是 B 类地址.

3.2 移动 APP / 小程序调试

作为本文的最后一小节, 我们讨论一个经常在开发 APP、小程序之类的应用时遇到的问题.

当我们在做此类程序的开发时, 总是迫不及待地想看看它们在自己手机上运行的效果, 但无奈电脑上运行的好好的程序, 一搬到手机上就连接不上服务端了.

这是为什么呢? 我们通过分析网络结构就可以轻松地找到原因, 并找到解决方法.

下图是你在电脑上调试时, 程序前后端之间的关系:

DNS

可以看到, 客户端与服务器端均处于你的电脑上 ( 即使客户端处于虚拟机之中也是如此 ), 它们之间的通信不言而喻是畅通的. 因为…. 没有那一台电脑会连不上自己, 是吧? 其实, 客户端与服务器端之间的通信甚至连网络都没有用到, 不信, 你把网线拔了, 把 WIFI 断开, 它照样好好的.

然后, 来看看, 你把客户端 ( APP / 小程序) 移到手机上的样子:

DNS

看到了吧, 手机和电脑压根儿就不在一个”世界”, 虽然它们都在你身边. 你让他们怎么通信?

可能你会说, 那是因为你客户端的代码里访问的还是本机 ( localhost ) 的服务端程序……

呵呵, 能看到这一点, 说明你还算细心! 确实, 现在对于装在手机里的程序来说, localhost ( 或 127.0.0.1 ) 确实指向的是 “手机”, 而不是 “电脑”, 这确实是一个错误.

但即使你把这个错误的地址改成的电脑的 IP, 会发现还是同样的问题: 连不通!

为啥? 如果你现在还回答不出这个问题, 建议你把本文前面的内容再看一遍 ( 特别是 3.1 节 )

咋办?

DNS

图中给出了两种较容易实现的方案:

(1) 左图: 增置一个无线路由器, 手机和电脑均连接到这台无线路由器上, 这样手机和电脑即处于同一个可直接通信的网络环境中了. ( 记得将手机和电脑配置到同一个子网中. 不会? 看 2.3 节 )

(2) 右图: 手机打开热点, 电脑连接手机热点, 这样手机相当于充当了一个无线路由器的角色.

在以上两种方案中, 均需要把客户端 ( APP/小程序/… ) 代码中的服务器端地址 ( localhost ) 改为电脑现在的内网 IP 地址.

若是小程序开发, 服务器端若未配置 HTTPS 的话, 还应注意把项目 “详情” 配置中的 “不校验合法域名、 web-view (业务域名)、TLS 版本以及 HTTPS 证书” 选中.

~ ~ ~ ~ ~ ~ ~ ~ ~ ~

漫漫长路终于走到头了, 本文中我们又挖出了一些坑, 比如: HTTPS, NAT … , 将来如果完成了相关主题的博文, 我会将链接添加本文.

就这样吧, 若有疑问, 或发现文中描述有不妥之处, 欢迎发邮件给我! Email 地址在”头上”.