前言
这本书从一个比较宏观的视角,解释了一个网络包从发送到接收的全过程,虽然此前也学过《计算机网络》,但《计算机网络》对每个细节解释的更加详尽,在学习时容易迷失在具体的细节里,觉得枯燥无味,对理论知识也难以联系到实际。
所以我觉得本书适合作为计算机网络的入门书籍,从宏观的角度对网络有一个直观的了解,对于具体某个环节的细节,再去查阅其他更详细的书籍。
第一章:Web 浏览器
浏览器通过 HTTP 报文向服务器发出请求,期待服务器作出某种响应。服务器会对所接受的请求进行反馈,让浏览器获得请求所产生的结果。
IP 地址通过 DNS 服务器来查询
通过 Socket 库中的 API 向 DNS 服务器发出域名解析请求(API 会将操作委托给操作系统的协议栈),然后 DNS 服务器返回请求者一个对应的请求结果,通过域名查询 IP 地址,网络的进行最终还是基于 IP 地址。
DNS 服务器的 IP 查询
从顶级域名向次级域名依照层级查询
委托协议栈发送信息
数据收发依靠协议栈,协议栈会使用套接字建立通信信道,在信道内收发信息
- 创建套接字:每个套接字都有自己的标识符
- 连接管道:通过 IP 地址和端口,找到对方的套接字,从而建立连接
- 传递信息
- 断开连接:会向对方发送通知
第二章:协议栈和网卡
数据传输流程
TCP 协议的数据传输流程:
- 创建套接字
- 连接服务器
- 收发数据:
- IP 与 以太网的包收发操作
- 断开连接并删除套接字
UDP 协议的数据传输流程:
- UDP 协议的收发操作
创建套接字
套接字由应用程序调用 Socket 库进行创建,而 Socket 库又会调用操作系统的协议栈进行创建,所以实际上套接字的创建是由操作系统的协议栈完成的。
套接字本质上就是一些控制信息,他的内容包含:
- 通信协议:例如 TCP 或 UDP
- 发送方的信息:自己的 IP 地址和端口号
- 接受方的信息:对方 IP 地址和端口号
- 通信状态:等待连接、正在通信等等
- PID:套接字所对应程序的进程标识符
创建套接字时,其控制信息是不完全的,需要连接服务器这一个步骤进一步填充控制信息。例如服务器是不知道客户端的 IP 地址的,他需要客户端的请求才能完善控制信息。
连接服务器
连接是一个高层宏观上的抽象形容,在实际的网络链路中,并没有一个连接产生。
TCP 头部:服务器和客户端进行信息交换的控制信息,他的作用是让通信双方知道对方是谁(应用程序级别上的谁,没有 IP 地址信息),以及数据的情况。
连接服务器实际上的操作是一个叫做三次「握手」的 TCP 头部信息交换,这个操作的作用就是让通信双方,知道对方是谁,同时互相确认对方已经知道我是谁。
TCP 头部是由操作系统协议栈完成的,应用程序是感知不到的,对应用程序来说,TCP 头部是不存在的,在应用程序中,知道套接字就足够了。
收发数据
在收发数据中,过大的包(数据)需要切分
使用 ACK 号确认包已收到,通常是滑动窗口下的 ACK 号,确认该 ACK 号之前的包都已收到。
断开连接
断开连接实际上的操作是一个叫做四次「挥手」的 TCP 头部信息交换
在 TCP 连接断开之后,套接字也会在一段时间过后删除。
IP 与以太网的收发操作
实际传输过程中,数据包会经过许多转发设备,他们负责把数据送往该去的地方。
MAC 头部和 IP 头部就负责决定送往哪一个转发设备。
IP 头部:在网络中传输的控制信息,报文发给谁(目的 IP 地址,这里是最终的目的 IP 地址),生存时间等等。
MAC 头部:在网络中传输的控制信息,报文下一跳发给谁,在传输的过程中都会在变。
IP 协议:判断下一个 IP 转发设备(路由器)的位置(在实际传输前,需要知道对方的 MAC 地址)
ARP 协议:获取下一个转发设备的 MAC 地址
MAC 地址:负责将包传输给下一个转发设备(在不同的网络中,例如无线网,就不用 MAC 地址,可能使用不同的头部,但总之他的任务是将包传输给下一个转发设备)
当路由器收到包时,MAC 头部会被丢弃,因为其实际上指向自己,然后加上下一跳的 MAC 地址再发送。
UDP 协议
不需要连接,也没有确认机制
UDP 头部:只有双方端口号、数据长度和校验和四个字段
第三章:转发设备
集线器
集线器的一个端口接收到信号后,会广播至所有其他端口,虽然属于转发设备,其实可以理解成导线,他只是将信号扩散到所有线路。
所以集线器所连接的网络,会发生信号碰撞,不能够支持全双工通信,同时只能发送信号或接收信号。
交换机
交换机转发表:
目标 MAC 地址 | 转发端口 | 其他信息 |
---|---|---|
00-60-97-A5-43-3C | 2 | … |
00-00-C0-16-AE-FD | 7 | … |
交换机通过 MAC 头部信息,根据内部转发表将包转发到特定的端口上。所以隔离了冲突域,网络内不会产生信号碰撞,可以进行全双工通信,即网络内部可以同时进行信号的发送和接收。具体表现为某条线路进行信号的发送,另一条线路进行信号的接收,互不影响。
交换机会检测包的校验信息,如果包产生错误,则会丢弃。
交换机可以同时转发多个包,发布到不同的端口上,相比集线器效率更高。
自动协商:当没有信号传输时,信道会被默认信号填充,其包含了双方所支持的工作模式(全双工、半双工)及相关信息,交换机和计算机就可以据此选择一种双方都支持的传输方式。
交换机内部转发表的维护
- 收到一个包时,会将发送方的 MAC 地址及其输入端口写入转发表中,即记录下这个包发送者的信息。
- 由于网络内的设备可能发生改变,所以转发表需要维护有效性,对于一些失效的信息,要删去,所以转发表内的条目,在一段时间不使用之后就会被删除。
特殊情况
- 转发表内没有目标 MAC 地址相对于的条目,不知道这个包该往哪个端口发送,这时就会进行广播,将包发送到所有其他端口上(不包括包传进来的那个端口)
- 如果目标端口是源端口(包传进来的端口),则不会进行转发,即不会把自己发出去的包传回给自己。
路由器
路由器路由表:
目标地址 | 子网掩码 | 网关 | 接口 | 跃点数 |
---|---|---|---|---|
10.10.1.0 | 255.255.255.0 | ———— | e2 | 1 |
10.10.1.101 | 255.255.255.255 | ———— | e2 | 1 |
192.168.1.0 | 255.255.255.0 | ———— | e3 | 1 |
0.0.0.0 | 0.0.0.0 | 192.0.2.1 | e1 | 1 |
路由器根据 IP 头部信息,根据内部转发表将包转发到特定的 IP 设备上(计算机或路由器),实际上也就是根据 IP 信息将包转发到路由器的特定端口上(路由器的各个端口具有独立的 MAC 地址和 IP 地址),然后端口会将包传输的任务交给交换机或集线器,让他们完成具体的传输工作。
路由器接收到包之后,会检查 MAC 地址是否与自身的匹配,不匹配则丢弃,和计算机是一样的。
路由聚合:将多个子网合并成一个子网,简化路由表。反过来也成立,将一个子网中的网络单独拿出来作为一个路由表项,可以优先匹配发往这个子网。
路由表的维护:使用例如 RIP、OSPC、BGP 等路由协议对路由表进行维护。
包的有效期:在路由中可能产生回路,导致包不断在回路中转发,浪费网络资源,所以当包被转发一定次数之后就会被丢弃。
包的分片:路由器可以将大包进行分片,拆分成小包,以满足各种不同的网络条件。在分片过程中会修改 IP 头部的相关信息(可能还会修改 MAC 头部)
ARP 协议:由于路由器根据 IP 地址进行转发,但实际传输工作基于 MAC 地址,所以没有对方的 MAC 地址,实际的传输就无法完成。路由器会使用 ARP 协议查询对应 IP 地址的 MAC 地址。
转发规则
- 对 IP 地址进行匹配,只会匹配网络号,忽略主机号。子网掩码越长的匹配越优先,例如上表中的第一行和第二行,对于一个 IP
10.10.1.101
的匹配都是符合的,但第二行的子网掩码更长,所以优先级更高,匹配第二行。 - 默认路由:例如上表最后一行,子网掩码为
0.0.0.0
意味着需要匹配的网络号位数为 0,当其他所有匹配都不成立之后,使用这个表项(子网掩码最短,优先级最低) - 如果没有匹配的结果,则通过 ICMP 通知发送方。
地址转换功能
由于 IP 数量有限,规定内网的私有 IP 地址可以重复(例如 192.168.x.x
,许多局域网都是使用这种 IP 地址)。
地址转换实际上就对 IP 地址和端口进行改写,对外采用公网公有 IP 地址(公有不会重复),对内采用私有 IP 地址。包经过路由器传往公网时,地址会被改写为公网的 IP 地址,而公网的包传往内网时,地址会被改写为内网的 IP 地址(这基于路由器的各个端口有不同的 IP 地址和 MAC 地址)
地址转换表示例:内网的不同地址,被映射到同一个公网地址,用端口号加以区分
公有地址(外网) | 端口号 | 私有地址(内网) | 端口号 |
---|---|---|---|
198.18.8.31 | 5436 | 10.10.1.1 | 1025 |
198.18.8.31 | 5437 | 10.10.1.2 | 1025 |
198.18.8.31 | 5438 | 10.10.1.3 | 2538 |
内网访问外网,即使路由器中的地址转换表没有对应的表项,此时只需要生成一个新的即可(没被使用的新端口号),而外网访问内网,如果没有对应的表项,路由器也不知道该转发给谁,此时就只能丢弃,在这个意义上,地址转换功能提高了内网的安全性,能够阻断外界的非法访问。
三者总结
集线器和交换机都没有 MAC 地址和 IP 地址,他们不能作为信号的终端,只能作为传输信号的中介,而路由器和计算机具有 IP 地址和 MAC 地址,他们可以作为信号的终端。
换句话说,我可以将包发到目标计算机或路由器上,但不能说将包发到目标交换机或集线器上,他们即使拿到包,也只会转发,不会保存。所以说发到是没有意义的,只能说经过。
第四章:互联网内部
对于大部分个人或商用的网络,都属于互联网边缘,而运营商及其内部的网络,属于互联网内部。大部分的网络信息,都是从互联网边缘的设备开始,经过互联网内部,从而达到另一个互联网边缘的设备。
BAS 宽带接入服务器
用户数据从家中或公司中传出,接着会传入 BAS 中,这里会进行一个用户认证的过程,例如输入用户密码,确认用户是可上网的状态。
完成认证之后,认证服务器会下发 TCP/IP 配置信息(IP 地址等等),让用户具有公网 IP,才能实现上网功能。
DHCP 动态配置
通常用于局域网内的私有 IP 配置,设备向路由器发出请求,给本机配置一个 IP 地址,DHCP 会自动采用一个未被使用的 IP 地址配置给该设备,这样就不用手动在路由器中设置 IP 地址等相关信息了。
例如 WIFI 路由器,连接时并不需要手动配置 IP 地址就能上网,就是使用了 DHCP 动态配置。
POP 和 NOC
互联网的实体是由多个运营商网络相互连接组成的,这些与用户进行连接的设备就叫做 POP(Point of Presense 接入点),是互联网实体的入口,数据经过 POP 之后就真正进入了互联网的内部。
NOC(Network Operation Center 网络运行中心)是运营商的核心设备,从 POP 传来的数据都会汇集到这里,然后转发给下一个 POP 或运营商,它具有非常高的数据吞吐量。
IX(Internet eXchange 互联网交换中心),数据汇集到这里,集中对数据交换进行管理,IX 的实体实际上就是高性能的交换机。
互联网内部的转发
运营商中设备对数据转发也需要转发表,但一个运营商显然不可能掌握整个互联网的信息,所以运营商间还需要相互交换路由信息,来更新自己的转发表。
另外,由于运营商是个盈利性质的组织,其转发规则更加复杂,最终所转发的线路并不一定是最快的,这里面还有一些优先级相关的规则。
第五章:服务器局域网
Web 服务器的部署
- 直接部署在服务端的局域网内,对外暴露一个公有 IP 地址,让外部进行访问。通过防火墙对非法的流量进行阻断,以及隔离公司内网和公共区域。从外部不允许访问公司内网,但可以访问到公共区域内的 Web 服务器。
- 部署在数据中心,数据中心通常位于运营商的网络内部,具有较快的速度,也有更高的安全性。
防火墙
包过滤方式:
只有满足对应规则的包才能通过防火墙,否则丢弃。包过滤不会查看包的数据部分,会通过 IP 头部、TCP 头部等等信息来进行判断。
过滤目标:例如要禁止服务器主动访问外部网络(阻止某些病毒传播),但同时又要允许客户端能够访问服务器,还要服务器能和客户端进行正常信息交流。
过滤规则:假设 Web 服务器的 IP 地址为 192.0.2.0/24
,程序端口为 80
这是一个简单的防火墙包过滤的例子:
- 第一行代表允许接受方为本机且端口号为 80 的包通过防火墙
- 第二行代表拒绝发送方为本机、端口号为 80 且是向外主动进行 TCP 连接的包通过防火墙(在 TCP 连接的过程中,服务器需要向外发送包,所以我们不能完全阻止向外发送的包,否则和客户端正常的信息交流都完成不了。但可以通过 TCP 控制位判断出主动连接,从而阻止服务器主动对外建立 TCP 连接)
- 第三行代表除了主动建立 TCP 连接,其他向外发送的包都是允许的(限定了端口号),这里也就是和客户端进行正常的信息交流
- 其他情况则一律阻止
一般来说 80 端口号是 HTTP 应用默认的端口号,所以以上的过滤规则,只允许 HTTP 应用的信息交流,其他应用一律都被阻止了,如果还需要对外提供其他功能,还需要单独设置其他端口号的过滤规则。
防火墙只能根据头部信息决定包是否通过,对于数据部分的攻击无法防范,而这部分实际上属于程序漏洞范畴,这部分的攻击是难以防范的。
负载均衡
当访问量非常大时,单台服务器的性能和带宽是有限的,此时只能通过增加服务器数量来分担负载。
DNS 轮询:一个域名可以注册多个 IP 地址,此时 DNS 服务器返回 IP 地址时会循环返回,例如有三个 IP 地址,第一次返回第一个,第二次返回第二个,第三次返回第三个,第四次会再次返回第一个,这样就完成了一个简单的负载均衡,将请求流量均匀的分配给三台服务器。
但 DNS 轮询规则过于简单,在一些复杂的场景下难以胜任。而且无法支持一些需要持续连接的应用,例如当前操作访问第一个服务器,数据保存在第一个服务器上,下一次操作访问第二个服务器,这时候就找不到之前的数据了。
负载均衡器:代替 Web 服务器注册到 DNS 服务器中,所有请求都经过负载均衡器,由负载均衡器决定该请求转发给哪一个服务器。
此时就可以编写一些程序,实现一些复杂的条件判断,负载均衡器还可以查询 Web 服务器的情况,判断服务器压力,从而决定将请求转发给哪个服务器。
也就可以实现持续连接的操作,通过包的信息,指定这些需要持续连接的包只发给同一个服务器。
缓存服务器
另一种减轻 Web 服务器负载的方法是设立缓存服务器,他同样会代替 Web 服务器注册到 DNS 服务器中,客户端只能访问到缓存服务器。
对于一个请求,缓存服务器会查看本机中是否有该资源,如果有则直接响应,没有再向 Web 服务器请求,这样就减轻了 Web 服务器的负担。
资源具有时效性,需要确认该资源是否是最新的,有两种方式:
- 主动询问,当存在该资源时,主动向 Web 服务器发出询问,对比是否是最新的(对比的是某种摘要信息,和源资源相比小很多),如果不是则再向 Web 服务器请求资源,拿到最新的。
- 被动更新,当资源发生更新时,Web 服务器会主动通知缓存服务器,进行资源的更新。
正向代理
客户端一侧的代理服务器,也具有缓存功能,正向代理会代替客户端向服务器发出请求,通常用于防火墙内的设备进行互联网访问。
客户端发出请求时,请求会到达代理服务器,代理服务器随后修改相关信息,然后转发请求,所以服务器只会看到代理服务器,代理服务器代替了客户端进行请求,使得真实客户端对服务器不可见。
但使用正向代理时,需要手动设置代理服务器的 IP 地址,错误设置会导致网络功能异常,所以比较麻烦。
反向代理
服务端一侧的代理服务器,和正向代理相反,代替服务端和客户端进行交流。
客户端发出请求会到达(反向)代理服务器,代理服务器随后查询相关的 Web 服务器获得资源,然后代替 Web 服务器向客户端响应请求,这样客户端只能看到代理服务器,从而使真实服务器对客户端不可见,提高了服务器的安全性。
同时客户端也不需要进行额外的设置,只需要服务器进行一次反向代理的设置即可。
透明代理
透明,顾名思义,双方都察觉不到透明代理的存在,就相当于是一个导线,不过有一些额外的功能。
由于透明的特性,客户端和服务端都不能对他进行配置,所以透明代理通常是放在链路中,拦截所有请求。
可以充当一个缓存服务器、网关或防火墙。例如在公司内网,透明代理可以作为一个访问外网的控制网关,只会转发允许的请求,拦截非法的请求;或者记录用户网络行为,跟踪用户的网络数据。
内容分发服务
缓存服务器设置在服务端时,可以减少 Web 服务器的流量,但无法减少互联网的流量,设置在客户端则可以。不过问题在于,客户端的缓存服务器,服务端无法进行管理,甚至客户端是否采用缓存服务器都不可知。
内容分发服务就是同时减少 Web 服务器流量和互联网流量,尽可能使缓存服务器靠近客户端,同时服务端又能够对其进行管理。
内容分发服务运营商会在互联网中部署很多的缓存服务器,关键的问题在于,客户端如何才能够找到最近的缓存服务器,从而实现上述的两个减少。
- 采集缓存服务器的路由表,集中到服务端的 DNS 服务器中,然后根据路由表等相关信息,查询到客户端 DNS 服务器的距离,以此作为一个距离的估算,从而决定返回哪一个缓存服务器的 IP 地址。这样的方式精度不高。
- 基于重定向,在重定向服务器中集中路由表,但此时发送方是客户端,根据客户端的 IP 地址进行估算可以得到更准确的精度,然后再决定使用哪一个缓存服务器,采用重定向的方式让客户端访问这个缓存服务器。
第六章:服务器内部
服务器程序结构
- 等待连接模块:当程序完成初始化工作之后,就会启动等待连接模块(启动套接字,等待连接),当客户端请求连接时,将连接的套接字移交给通信模块
- 通信模块:与客户端进行通信,每个套接字都会创建一个新的通信模块
套接字生命周期
- 创建套接字,设置为等待状态:
socket()
- 等待连接,接受连接:
bind()
、listen()
、accept()
- 收发数据:
read()
、write()
- 断开连接并删除:
close()
在接受连接的过程中,会生成一个套接字的副本,准备可能的下一个连接,如果不生成副本,套接字用完就无法连接了。
套接字的匹配需要:客户端 IP、端口号,服务端 IP、端口号
IP 模块的接收操作
先查看 IP 头部,确认接受方是自己,然后检查 IP 包是否有分片,如果有分片,暂存至缓存区,等所有分片到达后,组成原始包,然后根据头部协议字段,上交给上层(TCP 或 UDP 模块)
TCP 模块的连接
- 确认 TCP 头部的控制位 SYN
- 检查接收方端口号
- 为相应的等待套接字创建副本
- 记录发送方 IP、端口号等信息
TCP 模块的接收
- 根据包信息找到对应的套接字
- 将数据存至缓存区
- 向客户端返回 ACK
Web 服务器程序
URI 所对应的目录并不一定是服务器中的实际目录,为了安全等需要,Web 服务器程序有一个虚拟目录,与实际目录有一个映射关系。
当 URI 请求的是一个 HTML 文档或图片等静态资源,直接返回文件即可,当 URI 请求的是动态资源时,就会调用 CGI 程序。
服务器会运行 URI 所指定的程序,并将相应的参数递交给他,处理结果会返回至 Web 服务器,然后 Web 服务器对客户端进行相应。
Web 服务器的访问控制
- 根据客户端 IP 地址
- 根据客户端域名(根据客户端 IP 地址反查域名)
- 根据用户名和密码