一不小心画了 24 张图剖析计网应用层协议!

文章的整体脉络如下

在有了之前两篇文章的介绍后,相信读者对计算机网络有了初步的认识,那么下面我们就要对不同的协议层进行分类介绍了,我们还是采用自上而下的方式来介绍,这种介绍对读者来说更容易接纳,吸收程度更好(说白了就是更容易给我的文章点赞,逃)。

一般情况下,用户不太在意网络应用程序实际上是按照怎样的机制运行的,但我们是程序员吖,就套用朱伟的一句话说:你觉得计算机网络程序员不了解,你指着互联网用户去了解吗?有内个味儿没?

应用层指的是 OSI 标准模型的第 5、6、7层,也就是会话层、表现层、应用层

我们介绍的时候都会使用 OSI 标准模型来介绍,因为这样涵盖的层次比较多,这样对于 TCP/IP 模型来说,你也能加深理解。

应用层概念

应用层协议的定义

现如今,越来越多的应用程序利用网络进行通信,这些应用有 Web 浏览器、远程登录、电子邮件、文件传输、文件下载等,应用层的协议正是进行这些行为活动的规则和标准。

应用层协议(application layer protocol) 定义了在不同端系统上的应用程序进程如何相互传递报文。一般来说,会定义如下内容

  • 交换的报文类型:是请求报文还是相应报文
  • 报文字段的解释:对报文中各个字段的详细描述
  • 报文字段的语义:报文各个字段的含义是什么
  • 进程何时、以什么方式发送报文以及响应

应用层体系结构

应用层体系结构 的英文是 Application Architecture,它指的是应用层的结构,一般来说,应用层有两种主流体系结构

  • 客户 - 服务器体系结构 ( client-server architecture )
  • 对等体系结构 ( P2P architecture )

下面我们先来聊一下客户 - 服务器体系结构的概念

在客户-服务器体系结构中,有一个总是打开的主机称为 服务器(Server),它提供来自于 客户(client) 的服务。我们最常见的服务器就是 Web 服务器,Web 服务器服务于来自 浏览器 的请求。

BTPnRe.png

当 Web 服务器通过浏览器接收到用户请求后,它会经过一系列的处理把信息或者页面等通过浏览器呈现给应用。这种模式就是客户 - 服务器模式。

有两点需要注意

  • 在客户 - 服务器模式下,通常客户彼此之间是并不互相通信的。
  • 服务器通常具有固定的、周知的 IP 地址可以提供访问。

客户 - 服务器模式通常会出现随着客户数量的急剧增加导致单台服务器无法完成大量客户请求的情况。为此,通常需要配备大量主机的 数据中心(data center) ,用来跟踪所有的用户请求。

于此相反,P2P 也就是对等体系结构对这种数据中心的依赖性很低,因为在 P2P 体系结构中,应用程序在两个主机之间直接通信,这些主机被称为对等方,与有中心服务器的中央网络系统不同,对等网络的每个用户端既是一个节点,也有服务器的功能。常见的 P2P 体系结构的应用有 文件共享、视频会议、网络电话等。

P2P 一个最大的特点就是 扩展性(self-scalability),因为 P2P 网络的一个重要的目标就是让所有的客户端都能提供资源、获取资源,共享带宽,存储空间等。因此,当有更多节点加入且对系统请求增多,整个系统的容量也增大。这是具有一组固定服务器的客户 - 服务器结构不具备的,这也就是 P2P 的扩展性。

进程通信

我们上面说到了两种体系结构,一种是客户 - 服务器模式,一种是 P2P 对等模式。我们都知道一个计算机允许同时运行多个应用程序,在我们看起来这些应用程序好像是同时运行的,那么它们之间是如何通信的呢?

用操作系统的术语来说,进行通信实际上是 进程(process)而不是程序。一个进程可以被认为是运行在端系统中的程序。当多个进程运行在相同的端系统上时,它们使用进程间的通信机制相互通信。进程间的通信规则由操作系统来确定。

进程与计算机网络之间的接口

计算机是庞大且繁杂的,计算机网络也是,应用程序不可能只有一个进程组成,它同样是多个进程共同作用协商运行,然而,分布在多个端系统之间的进程是如何进行通信的呢?实际上,每个进程之间会有一个 套接字(socket) 的软件接口存在,套接字是应用程序的内部接口,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。

通过一个实例来简单类比一下套接字和网络进程:进程可类比一座房子,而它的套接字相当于是房子的门,当一个进程想要与其他进程进行通信时,它会把报文推出门外,然后通过运输设备把报文运输到另外一座房子,通过门进入房子内部使用。

下图是一个通过套接字进行通信的流程图

BTPePO.png

从图可以看到,Socket 属于主机或者服务进程的内部接口,由应用程序开发人员进行控制,两台端系统之间进行通信会通过 TCP 的缓冲区经由网络传输到另一个端系统的 TCP 缓冲区,Socket 从 TCP 缓冲区读取报文供应用程序内部使用。

套接字是建立网络应用程序的可编程接口,因此套接字也被称为应用程序和网络之间的 应用程序编程接口(Application Programming Interface,API)。应用程序开发人员可以控制套接字内部细节,但是无法控制运输层的传输,只能对运输层的传输协议进行选择,还可以对运输层的传输参数进行选择,比如最大缓存和最大报文长度等。

进程寻址

我们上面提到网络应用程序之间会相互发送报文,那么你怎么知道你应该向哪里发送报文呢?是不是存在某种机制能够让你知道你能够发到哪里?这就好比你要发送电子邮件,你写好了内容但是你不知道发发往哪里,所以这个时候必须要有一种知道对方地址的机制,这种机制能够辨明对方唯一的一个地址,这种地址就是 IP地址。我们会在后面的文章中详细讨论 IP 地址的内容,目前只需要知道 IP 是一个 32 比特的量并且能够唯一标示互联网中任意一台主机的地址就可以了。

只知道 IP 地址是否就可以了呢?我们知道一台计算机可能回运行多个网络应用程序,那么如何确定是哪个网络应用程序接受发送过来的报文呢?所以这时候还需要知道网络应用程序的 端口号(port number)。例如, Web 应用程序需要用 80 端口来标示,邮件服务器程序需要使用 25 来标示。

应用程序如何选择运输服务

我们知道应用程序是属于互联网四层协议的 应用层 协议,并且四层协议必须彼此协助共同完成工作。好了,这时候我们只有应用层协议,我们需要发送报文,我们如何发送报文呢?这就好比你知道目的地是哪里了,你该如何到达目的地呢?是走路,公交,地铁还是打车?

应用程序发送报文的交通工具的选择也有很多,我们可以从 数据传输是否可靠、吞吐量、定时和安全性 来考虑,下面是你需要考虑的具体内容。

  • 数据传输是否可靠

我们之前探讨过,分组在计算机网络中会存在丢包问题,丢包问题的严重性跟网络应用程序的性质有关,如果像是电子邮件、文件传输、远程主机、Web 文档传输的过程中出现问题,数据丢失可能会造成非常严重的后果。如果像是网络游戏,多人视频会议造成的影响可能比较小。鉴于此,数据传输的可靠性也是首先需要考虑的问题。因此,如果一个协议提供了这样的确保数据交付的服务,就认为提供了 可靠数据传输(reliable data transfer),能够忍受数据丢失的应用被称为 容忍丢失的应用(loss-tolerant application)

  • 吞吐量

在之前的文章中我们引入了吞吐量的概念,吞吐量就是在网络应用中数据传输过程中,发送进程能够向接收进程交付比特的速率。具有吞吐量要求的应用程序被称为 带宽敏感的应用(bandwidth-sensitive application)。带宽敏感的应用具有特定的吞吐量要求,而 弹性应用(elastic application) 能够根据当时可用的带宽或多或少地利用可供使用的吞吐量。

  • 定时

定时是什么意思?定时能够确保网络中两个应用程序的收发是否能够在指定的时间内完成,这也是应用程序选择运输服务需要考虑的一个因素,这听起来很自然,你网络应用发送和接收数据包肯定要加以时间的概念,比如在游戏中,你一包数据迟迟发送不过去,对面都推塔了你还卡在半路上呢。

  • 安全性

最后,选择运输协议一定要能够为应用程序提供一种或多种安全性服务。

因特网能够提供的运输服务

说完运输服务的选型,接下来该聊一聊因特网能够提供哪些服务了。实际上,因特网为应用程序提供了两种运输层的协议,即 UDPTCP,下面是一些网络应用的选择要求,可以根据需要来选择适合的运输层协议。

应用 数据丢失 带宽 时间敏感
文件传输 不能丢失 弹性 不敏感
电子邮件 不能丢失 弹性 不敏感
Web 文档 不能丢失 弹性 不敏感
因特网电话/视频会议 容忍丢失 弹性 敏感,100ms
流式存储音频/视频 容忍丢失 弹性 敏感,几秒
交互式游戏 容忍丢失 弹性 是,100ms
智能手机消息 不能丢失 弹性 无所谓

下面我们就来聊一聊这两种运输协议的应用场景

TCP

TCP 服务模型的特性主要有下面几种

  • 面向连接的服务

在应用层数据报发送后, TCP 让客户端和服务器互相交换运输层控制信息。这个握手过程就是提醒客户端和服务器需要准备好接受数据报。握手阶段后,一个 TCP 连接(TCP Connection) 就建立了。这是一条全双工的连接,即连接双方的进程都可以在此连接上同时进行收发报文。当应用程序结束报文发送后,必须拆除连接。

  • 可靠的数据传输

通信进程能够依靠 TCP,无差错、按适当顺序交付所有发送的数据。应用程序能够依靠 TCP 将相同的字节流交付给接收方的套接字,没有字节的丢失和冗余。

  • 拥塞控制

TCP 的拥塞控制并不一定为通信进程带来直接好处,但能为因特网带来整体好处。当接收方和发送方之间的网络出现拥塞时,TCP 的拥塞控制会抑制发送进程(客户端或服务器),我们会在后面具体探讨拥塞控制

UDP

UDP 是一种轻量级的运输协议,它仅提供最小服务。UDP 是无连接的,因此在两个进程通信前没有握手过程。UDP 也不会保证报文是否传输到服务端,它就像是一个撒手掌柜。不仅如此,到达接收进程的报文也可能是乱序到达的。

下面是上表列出来的一些应用所选择的协议

应用 应用层协议 支撑的运输协议
电子邮件 SMTP TCP
远程终端访问 Telnet TCP
Web HTTP TCP
文件传输 FTP TCP
流式多媒体 HTTP TCP
因特网电话 SIP、RTP TCP 或 UDP

应用层协议

下面我们着重介绍一下应用层都有哪些比较重要的应用协议

WWW 和 HTTP

万维网(WWW, World Wide Web) 是将互联网中的信息以超文本的形式展现的系统,也就是 Web 。用来显示 WWW 结果的客户端被称为 Web 浏览器,通过浏览器,我们无需关注想要访问的内容在哪个服务器上,我们只需要知道我们想访问的内容就可以了。

WWW 定义了三个比较重要的概念,这些概念主要有

  • URI,定义了访问信息的手段和位置
  • HTML, 定义了信息的表现形式
  • HTTP,定义了 WWW 的访问规范

URI / URL

URI的全称是(Uniform Resource Identifier),中文名称是统一资源标识符,使用它就能够唯一地标记互联网上资源。

URL的全称是(Uniform Resource Locator),中文名称是统一资源定位符,也就是我们俗称的网址,它实际上是 URI 的一个子集。

URI 不仅包括 URL,还包括 URN(统一资源名称),它们之间的关系如下

URI 已经不局限于标识互联网资源,它可以作为所有资源的识别码。

HTML

HTML 称为超文本标记语言,是一种标识性的语言。它包括一系列标签.通过这些标签可以将网络上的文档格式统一,使分散的 Internet 资源连接为一个逻辑整体。HTML 文本是由 HTML 命令组成的描述性文本,HTML 命令可以说明文字,图形、动画、声音、表格、链接等。

HTTP

Web 的应用层协议就是 HTTP(HyperText Transfer Protocol, HTTP), 超文本传输协议,它是 Web 的核心协议。下面我们需要了解一下 HTTP 中的几个核心概念。

Web 页面

Web 页面也叫做 Web Page,它是由对象组成,一个对象(object) 简单来说就是一个文件,这个文件可以是 HTML 文件、一个图片、一段 Java 应用程序等,它们都可以通过 URI 来找到。一个 Web 页面包含了很多对象,Web 页面可以说是对象的集合体。

浏览器

就如同各大邮箱使用电子邮件传送协议 SMTP 一样,浏览器是使用 HTTP 协议的主要载体,说到浏览器,你能想起来几种?是的,随着网景大战结束后,浏览器迅速发展,至今已经出现过的浏览器主要有

Web 服务器

Web 服务器的正式名称叫做 Web Server,Web 服务器可以向浏览器等 Web 客户端提供文档,也可以放置网站文件,让全世界浏览;可以放置数据文件,让全世界下载。目前最主流的三个 Web 服务器是 Apache、 Nginx 、IIS。

CDN

CDN 的全称是Content Delivery Network,即内容分发网络,它应用了 HTTP 协议里的缓存和代理技术,代替源站响应客户端的请求。CDN 是构建在现有网络基础之上的网络,它依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储分发技术

打比方说你要去亚马逊上买书,之前你只能通过购物网站购买后从美国发货过海关等重重关卡送到你的家里,现在在中国建立一个亚马逊分基地,你就不用通过美国进行邮寄,从中国就能把书尽快给你送到。

WAF

WAF 是一种 Web 应用程序防护系统(Web Application Firewall,简称 WAF),它是一种通过执行一系列针对 HTTP / HTTPS的安全策略来专门为 Web 应用提供保护的一款产品,它是应用层面的防火墙,专门检测 HTTP 流量,是防护 Web 应用的安全技术。

WAF 通常位于 Web 服务器之前,可以阻止如 SQL 注入、跨站脚本等攻击,目前应用较多的一个开源项目是 ModSecurity,它能够完全集成进 Apache 或 Nginx。

WebService

WebService 是一种 Web 应用程序,WebService 是一种跨编程语言和跨操作系统平台的远程调用技术

WebService 是一种由 W3C 定义的应用服务开发规范,使用 client-server 主从架构,通常使用 WSDL 定义服务接口,使用 HTTP 协议传输 XML 或 SOAP 消息,它是一个基于 Web(HTTP)的服务架构技术,既可以运行在内网,也可以在适当保护后运行在外网。

HTTP

HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。HTTP 是一种应用层协议,它使用 TCP 作为运输层协议,因为文档、数据这些信息在我们看来是一种重要的信息,不可丢失。

HTTP 请求响应过程

让我们通过一个例子来探讨一下 HTTP 的请求响应过程,我们假设访问的 URL 地址为 http://www.someSchool.edu/someDepartment/home.index,当我们输入网址并点击回车时,浏览器内部会进行如下操作

  • DNS服务器会首先进行域名的映射,找到访问www.someSchool.edu所在的地址,然后HTTP 客户端进程在 80 端口发起一个到服务器 www.someSchool.edu 的 TCP 连接(80 端口是 HTTP 的默认端口)。在客户和服务器进程中都会有一个套接字与其相连。
  • HTTP 客户端通过它的套接字向服务器发送一个 HTTP 请求报文。该报文中包含了路径 someDepartment/home.index 的资源,我们后面会详细讨论 HTTP 请求报文。
  • HTTP 服务器通过它的套接字接受该报文,进行请求的解析工作,并从其存储器(RAM 或磁盘)中检索出对象 www.someSchool.edu/someDepartment/home.index,然后把检索出来的对象进行封装,封装到 HTTP 响应报文中,并通过套接字向客户进行发送。
  • HTTP 服务器随即通知 TCP 断开 TCP 连接,实际上是需要等到客户接受完响应报文后才会断开 TCP 连接。
  • HTTP 客户端接受完响应报文后,TCP 连接会关闭。HTTP 客户端从响应中提取出报文中是一个 HTML 响应文件,并检查该 HTML 文件,然后循环检查报文中其他内部对象。
  • 检查完成后,HTTP 客户端会把对应的资源通过显示器呈现给用户。

至此,键入网址再按下回车的全过程就结束了。上述过程描述的是一种简单的请求-响应全过程,真实的请求-响应情况可能要比上面描述的过程复杂很多。

HTTP 请求特征

从上面整个过程中我们可以总结出 HTTP 进行分组传输是具有以下特征

  • 支持客户 - 服务器模式
  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快。
  • 灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。
  • 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • 无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

持久链接和非持久链接

我们上面描述的 HTTP 请求响应过程就是一种非持久链接,因为每次 TCP 在传递完报文后,都会关闭 TCP 链接,每个 TCP 连接只传输一个请求报文和响应报文。

非持久性连接有一些缺点

  • 第一,必须为每个请求的对象建立和维护一个全新的连接。
  • 第二,对于每个这样的连接来说,在客户端和服务器中都要分配 TCP 的缓冲区和保持 TCP 变量,这给 Web 服务器带来了严重的负担。因为一台 Web 服务器可能要同时服务于数百甚至上千个客户请求。

在采用 HTTP 1.1 持续连接的情况下,服务器在发送响应后保持该 TCP 连接打开不关闭。在相同的客户与服务器之间,后续的请求和响应报文能够通过相同的连接进行传送。一般来说,如果一跳连接经过一定的时间间隔(可配置)后仍未使用,HTTP 服务器就应该关闭其连接。

HTTP 报文格式

我们上面描述了一下 HTTP 的请求响应过程,相信你对 HTTP 有了更深的认识,下面我们就来一起认识一下 HTTP 的报文格式是怎样的。

HTTP 协议主要由三大部分组成:

  • 起始行(start line):描述请求或响应的基本信息;
  • 头部字段(header):使用 key-value 形式更详细地说明报文;
  • 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。

其中起始行和头部字段并成为 请求头 或者 响应头,统称为 Header;消息正文也叫做实体,称为 body。HTTP 协议规定每次发送的报文必须要有 Header,但是可以没有 body,也就是说头信息是必须的,实体信息可以没有。而且在 header 和 body 之间必须要有一个空行(CRLF)。如果用一幅图来表示一下 HTTP 请求的话,我觉得应该是下面这样

如果细化一点的话,那就是下面这样

这幅图需要注意一下,如果使用 GET 方法,是没有实体体的,如果你使用的是 POST 方法,才会有实体体。当用户提交表单时,HTTP 客户端通常使用 POST 方法;与此相反,HTML 表单的获取通常使用 GET 方法。HEAD 方法类似于 GET 方法,只不过 HEAD 方法不会返回对象。

下面我们来看一下 HTTP 响应报文

可以看到,请求报文和响应报文只有请求头是不同的,其他信息均一致。

请求报文请求行:

GET /some/page.html HTTP/1.1

响应报文:

HTTP/1.1 200 OK

HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性。

Session 是什么

客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个对象便是 Session 对象,存储结构为 ConcurrentHashMap。Session 弥补了 HTTP 无状态特性,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录。

Session 如何判断是否是同一会话

服务器第一次接收到请求时,开辟了一块 Session 空间(创建了Session对象),同时生成一个 sessionId ,并通过响应头的 **Set-Cookie:JSESSIONID=XXXXXXX **命令,向客户端发送要求设置 Cookie 的响应; 客户端收到响应后,在本机客户端设置了一个 **JSESSIONID=XXXXXXX **的 Cookie 信息,该 Cookie 的过期时间为浏览器会话结束;

接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。

Session 的缺点

Session 机制有个缺点,比如 A 服务器存储了 Session,就是做了负载均衡后,假如一段时间内 A 的访问量激增,会转发到 B 进行访问,但是 B 服务器并没有存储 A 的 Session,会导致 Session 的失效。

Cookies 是什么

HTTP 协议中的 Cookie 包括 Web Cookie浏览器 Cookie,它是服务器发送到 Web 浏览器的一小块数据。服务器发送到浏览器的 Cookie,浏览器会进行存储,并与下一个请求一起发送到服务器。通常,它用于判断两个请求是否来自于同一个浏览器,例如用户保持登录状态。

HTTP Cookie 机制是 HTTP 协议无状态的一种补充和改良

Cookie 主要用于下面三个目的

  • 会话管理

登陆、购物车、游戏得分或者服务器应该记住的其他内容

  • 个性化

用户偏好、主题或者其他设置

  • 追踪

记录和分析用户行为

Cookie 曾经用于一般的客户端存储。虽然这是合法的,因为它们是在客户端上存储数据的唯一方法,但如今建议使用现代存储 API。Cookie 随每个请求一起发送,因此它们可能会降低性能(尤其是对于移动数据连接而言)。

当接收到客户端发出的 HTTP 请求时,服务器可以发送带有响应的 Set-Cookie 标头,Cookie 通常由浏览器存储,然后将 Cookie 与 HTTP 标头一同向服务器发出请求。

Set-Cookie HTTP 响应标头将 cookie 从服务器发送到用户代理。下面是一个发送 Cookie 的例子

此标头告诉客户端存储 Cookie

现在,随着对服务器的每个新请求,浏览器将使用 Cookie 头将所有以前存储的 Cookie 发送回服务器。

有两种类型的 Cookies,一种是 Session Cookies,一种是 Persistent Cookies,如果 Cookie 不包含到期日期,则将其视为会话 Cookie。会话 Cookie 存储在内存中,永远不会写入磁盘,当浏览器关闭时,此后 Cookie 将永久丢失。如果 Cookie 包含有效期 ,则将其视为持久性 Cookie。在到期指定的日期,Cookie 将从磁盘中删除。

还有一种是 Cookie的 Secure 和 HttpOnly 标记,下面依次来介绍一下

会话 Cookies

上面的示例创建的是会话 Cookie ,会话 Cookie 有个特征,客户端关闭时 Cookie 会删除,因为它没有指定Expires Max-Age 指令。

但是,Web 浏览器可能会使用会话还原,这会使大多数会话 Cookie 保持永久状态,就像从未关闭过浏览器一样。

永久性 Cookies

永久性 Cookie 不会在客户端关闭时过期,而是在特定日期(Expires)特定时间长度(Max-Age)外过期。例如

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

尽管 Cookie 能够简化用户的网络活动,但是 Cookie 的使用存在争议,因为不少人认为它对用户是一种侵权行为。因为结合 Cookie 和用户提供的账户信息,Web 站点可以知道更多关于用户的信息。

Web 缓存

Web 缓存(Web cache) 也叫做 代理服务器(proxy server),它是代表 HTTP 服务器来满足用户需求的网络实体。Web 缓存器有自己的磁盘存储空间,并会在存储空间内保存最近请求过的对象,如下图所示

Web 缓存可以在用户的浏览器中进行配置,一旦配置后,用户首先访问的就不是初始服务器了,需要先访问代理服务器判断请求的对象是否存在,如果代理服务器没有,再由代理服务器来请求初始服务器把对象返回给客户,同时在自己的磁盘空间保存对象。

这里需要注意,客户和初始服务器的架构是 客户-服务器模式,而代理服务器不仅能当服务器使用,也可以当作客户端使用。

代理服务器一般由 ISP(Internet Service Provider),提供。注意不是老色批。。。ISP 也就是我们常说的运营商,你懂的。

那么为什么需要代理服务器的存在呢?相信你看完上面的描述应该能大致猜到它的作用。

  • 首先,代理服务器可以大大减少对客户请求的响应时间,能够更快给用户响应。
  • 其次,代理服务器可以减少一个机构接入链路到网络的通信量,降低网络带宽,降低运营商成本。
  • 然后,代理服务器可以分担初始服务器的压力,改善应用程序的性能。

DASH

通过上面的描述我们知道 HTTP 是可以传输普通文件、音频、视频的,这些传输的信息统称为 MIME 类型。HTTP 在传递视频中,也只是把视频当作对象来传输,而一个对象其实就是一个文件,一个文件都在 HTTP 中都可以用 URL 来表示。当用户在看视频时,客户与服务器建立一个 TCP 连接并发送对该 URL 的 GET 请求,然后服务器响应给客户端时,客户端会缓存一定量的字节数据,当数据超过预先设定的门限时,客户应用程序就开始播放视频。

这种方式有一种局限性就是对每个客户端来说,尽管每个客户端可用的带宽量不同,但所有客户端都收到相同的视频编码。这就造成带宽浪费。这就相当我是一个 2兆的网络和 50 兆的光纤都能收到相同的视频编码,以几乎相同的等待时间开始播放视频,那么我为什么还要花 50 兆光纤的钱呢?

为了改善这一现象,出现了 HTTP 的 DASH,DASH 即 Dynamic Adaptive Streaming HTTP,动态适应流。它的理念是针对不同流量的网络来说,所能够传输的比特数据也不相同。DASH 允许客户使用不同的因特网传输速率可以播放不同编码速率的视频。对于 3G 用户和光纤用户自然会选择以不同的速率传输比特数据,从而最大限度的使用带宽。

CDN

随着互联网的接入用户变得越来越多,视频逐渐成为了比特传输的瓶颈和用户的强烈需求。作为一个因特网视频公司,最一开始提供流式服务最直接的方式是建立单一的大规模数据中心。在数据中心内缓存所有视频,并直接从数据中心向世界范围内传播视频。但是这种方式存在三种问题

  • 如果客户远离数据中心,那么服务器到客户分组会跨越许多通信链路并且可能通过许多 ISP,这样你的视频播放能快到哪去?
  • 每次视频数据都会重新传递给客户端,这样会严重浪费网络带宽,而且视频公司会支付重复的带宽费用
  • 单点故障问题,只要视频数据中心宕机或者其他事故,直接导致全球范围内的视频无法播放。

为了应对能够向全世界的用户 24 小时不间断的分发视频,几乎所有的主流视频公司都会使用 内容分发网(Content Distribution Network, CDN) 。CDN 管理分布在多个地理位置上的服务器,在每个服务器上缓存各种视频、音频、文件等。

CDN 内容选择策略

CDN 管理分布在多个地理位置上的服务器,在它的服务器上存储视频副本,并且所有试图将每个用户请求定向到一个提供最好用户体验的 CDN 位置。那么服务器如何选址呢?事实上有两种服务器安置原则

  • 深入,它的主要目标是靠近用户,通过减少端用户和 CDN 集群之间链路和路由器的数量,从而改善了用户感受的时延和吞吐量。
  • 邀请做客,这个原则是通过在少量(例如 10 个)关键位置建造大集群来邀请 ISP 来做客,与深入设计原则相比,邀请做客设计通常产生较低的维护和管理开销。

CDN 工作流程

CDN 可以是专用 CDN(private CDN), 即它由内容提供商自己所拥有;另一种 CDN 是 第三方 CDN(third-party CDN),它代表多个内容提供商分发内容。

下面我们来聊一下 CDN 工作流程,如下图所示

  • 用户想要访问指定网站的内容

  • 用户首先发起对本地 DNS,LDNS 的查询,LDNS 会将请求中继到网站 DNS 服务器,网站的 DNS 服务器会返回给 LDNS 一个网站 CDN 权威服务器的地址

  • LDNS 服务器会发送第二个请求给网站 CDN 权威服务器,希望获取网站内容分发服务器的地址,网站 CDN 会把 CDN 内容分发服务器的地址发送给本地 DNS 服务器

  • 本地 DNS 服务器会把网站 CDN 内容分发服务器的地址发送给用户

  • 用户知道网站 CDN 内容分发服务器的地址后,无需额外操作,直接和网站 CDN 内容分发服务器建立 TCP 连接,并且发出 HTTP GET 请求,如果使用了 DASH 流,会根据不同 URL 的版本选择不同速率的块发送给用户。

CDN 集群选择策略

任何 CDN 的部署,其核心是 集群选择策略(cluster selection strategy), 即动态的将客户定向到 CDN 中某个服务器集群或数据中心的机制。一种简单的策略是指派客户到 地理上最为临近(geographically closest) 的集群。这种选择策略忽略了时延和可用带宽随因特网路径时间而变化,总是为特定的客户指派相同的集群;还有一种选择策略是 实时测量(real-time measurement),该机制是基于集群和客户之间的时延和丢包性能执行周期性检查。

DNS 因特网目录服务协议

试想一个问题,我们人类可以有多少种识别自己的方式?可以通过身份证来识别,可以通过社保卡号来识别,也可以通过驾驶证来识别,尽管我们有多种识别方式,但在特定的环境下,某种识别方法可能比另一种方法更为适合。因特网上的主机和人类一样,可以使用多种识别方式进行标识。互联网上主机的一种标识方法是使用它的 主机名(hostname) ,如 www.facebook.com、 www.google.com 等。但是这是我们人类的记忆方式,路由器不会这么理解,路由器喜欢定长的、有层次结构的 IP地址,so,还记得 IP 是什么吗?

IP 地址现在简单表述一下,就是一个由 4 字节组成,并有着严格的层次结构。例如 121.7.106.83 这样一个 IP 地址,其中的每个字节都可以用 . 进行分割,表示了 0 - 255 的十进制数字。(具体的 IP 我们会在后面讨论)

然而,路由器喜欢的是 IP 地址进行解析,我们人类却便于记忆的是网址,那么路由器如何把 IP 地址解析为我们熟悉的网址地址呢?这时候就需要 DNS 出现了。

DNS 的全称是 Domain Name System,DNS ,它是一个由分层的 DNS 服务器(DNS server)实现的分布式数据库;它还是一个使得主机能够查询分布式数据库的应用层协议。DNS 服务器通常是运行 BIND(Berkeley Internet Name Domain) 软件的 UNIX 机器。DNS 协议运行在 UDP 之上,使用 53 端口。

DNS 基本概述

与 HTTP、FTP 和 SMTP 一样,DNS 协议也是应用层的协议,DNS 使用客户-服务器模式运行在通信的端系统之间,在通信的端系统之间通过下面的端到端运输协议来传送 DNS 报文。但是 DNS 不是一个直接和用户打交道的应用。DNS 是为因特网上的用户应用程序以及其他软件提供一种核心功能。

DNS 通常不是一门独立的协议,它通常为其他应用层协议所使用,这些协议包括 HTTP、SMTP 和 FTP,将用户提供的主机名解析为 IP 地址。

下面根据一个示例来描述一下这个 DNS 解析过程,这个和你输入网址后,浏览器做了什么操作有异曲同工之处

你在浏览器键入 www.someschool.edu/index.html 时会发生什么现象?为了使用户主机能够将一个 HTTP 请求报文发送到 Web 服务器 www.someschool.edu ,会经历如下操作

  • 同一台用户主机上运行着 DNS 应用的客户端
  • 浏览器从上述 URL 中抽取出主机名 www.someschool.edu ,并将这台主机名传给 DNS 应用的客户端
  • DNS 客户向 DNS 服务器发送一个包含主机名的请求。
  • DNS 客户最终会收到一份回答报文,其中包含该目标主机的 IP 地址
  • 一旦浏览器收到目标主机的 IP 地址后,它就能够向位于该 IP 地址 80 端口的 HTTP 服务器进程发起一个 TCP 连接。

除了提供 IP 地址到主机名的转换,DNS 还提供了下面几种重要的服务

  • 主机别名(host aliasing),有着复杂的主机名的主机能够拥有一个或多个其他别名,比如说一台名为 relay1.west-coast.enterprise.com 的主机,同时会拥有 enterprise.com 和 www.enterprise.com 的两个主机别名,在这种情况下,relay1.west-coast.enterprise.com 也称为 规范主机名,而主机别名要比规范主机名更加容易记忆。应用程序可以调用 DNS 来获得主机别名对应的规范主机名以及主机的 IP地址。
  • 邮件服务器别名(mail server aliasing),同样的,电子邮件的应用程序也可以调用 DNS 对提供的主机名进行解析。
  • 负载分配(load distribution),DNS 也用于冗余的服务器之间进行负载分配。繁忙的站点例如 cnn.com 被冗余分布在多台服务器上,每台服务器运行在不同的端系统之间,每个都有着不同的 IP 地址。由于这些冗余的 Web 服务器,一个 IP 地址集合因此与同一个规范主机名联系。DNS 数据库中存储着这些 IP 地址的集合。由于客户端每次都会发起 HTTP 请求,所以 DNS 就会在所有这些冗余的 Web 服务器之间循环分配了负载。

DNS 工作概述

DNS 是一个复杂的系统,我们在这里只是就其运行的主要方面进行学习,下面给出一个 DNS 工作过程的总体概述

假设运行在用户主机上的某些应用程序(如 Web 浏览器或邮件阅读器) 需要将主机名转换为 IP 地址。这些应用程序将调用 DNS 的客户端,并指明需要被转换的主机名。用户主机上的 DNS 收到后,会使用 UDP 通过 53 端口向网络上发送一个 DNS 查询报文,经过一段时间后,用户主机上的 DNS 会收到一个主机名对应的 DNS 回答报文。因此,从用户主机的角度来看,DNS 就像是一个黑盒子,其内部的操作你无法看到。但是实际上,实现 DNS 这个服务的黑盒子非常复杂,它由分布于全球的大量 DNS 服务器以及定义了 DNS 服务器与查询主机通信方式的应用层协议组成。

DNS 最早的一种简单设计只是在因特网上使用一个 DNS 服务器。该服务器会包含所有的映射。这是一种集中式的设计,这种设计并不适用于当今的互联网,因为互联网有着数量巨大并且持续增长的主机,这种集中式的设计会存在以下几个问题

  • 单点故障(a single point of failure),如果 DNS 服务器崩溃,那么整个网络随之瘫痪。
  • 通信容量(traaffic volume),单个 DNS 服务器不得不处理所有的 DNS 查询,这种查询级别可能是上百万上千万级
  • 远距离集中式数据库(distant centralized database),单个 DNS 服务器不可能 邻近 所有的用户,假设在美国的 DNS 服务器不可能临近让澳大利亚的查询使用,其中查询请求势必会经过低速和拥堵的链路,造成严重的时延。
  • 维护(maintenance),维护成本巨大,而且还需要频繁更新。

所以 DNS 不可能集中式设计,它完全没有可扩展能力,因此采用分布式设计,所以这种设计的特点如下

分布式、层次数据库

首先分布式设计首先解决的问题就是 DNS 服务器的扩展性问题,因此 DNS 使用了大量的 DNS 服务器,它们的组织模式一般是层次方式,并且分布在全世界范围内。没有一台 DNS 服务器能够拥有因特网上所有主机的映射。相反,这些映射分布在所有的 DNS 服务器上。

大致来说有三种 DNS 服务器:根 DNS 服务器顶级域(Top-Level Domain, TLD) DNS 服务器权威 DNS 服务器 。这些服务器的层次模型如下图所示

假设现在一个 DNS 客户端想要知道 www.amazon.com 的 IP 地址,那么上面的域名服务器是如何解析的呢?首先,客户端会先根服务器之一进行关联,它将返回顶级域名 com 的 TLD 服务器的 IP 地址。该客户则与这些 TLD 服务器之一联系,它将为 amazon.com 返回权威服务器的 IP 地址。最后,该客户与 amazom.com 权威服务器之一联系,它为 www.amazom.com 返回其 IP 地址。

我们现在来讨论一下上面域名服务器的层次系统

  • 根 DNS 服务器 ,有 400 多个根域名服务器遍及全世界,这些根域名服务器由 13 个不同的组织管理。根域名服务器的清单和组织机构可以在 https://root-servers.org/ 中找到,根域名服务器提供 TLD 服务器的 IP 地址。
  • 顶级域 DNS 服务器,对于每个顶级域名比如 com、org、net、edu 和 gov 和所有的国家级域名 uk、fr、ca 和 jp 都有 TLD 服务器或服务器集群。所有的顶级域列表参见 https://tld-list.com/ 。TDL 服务器提供了权威 DNS 服务器的 IP 地址。
  • 权威 DNS 服务器,在因特网上具有公共可访问的主机,如 Web 服务器和邮件服务器,这些主机的组织机构必须提供可供访问的 DNS 记录,这些记录将这些主机的名字映射为 IP 地址。一个组织机构的权威 DNS 服务器收藏了这些 DNS 记录。

一般域名服务器的层次结构主要是以上三种,除此之外,还有另一类重要的 DNS 服务器,它是 本地 DNS 服务器(local DNS server)。严格来说,本地 DNS 服务器并不属于上述层次结构,但是本地 DNS 服务器又是至关重要的。每个 ISP(Internet Service Provider) 比如居民区的 ISP 或者一个机构的 ISP 都有一台本地 DNS 服务器。当主机和 ISP 进行连接时,该 ISP 会提供一台主机的 IP 地址,该主机会具有一台或多台其本地 DNS 服务器的 IP地址。通过访问网络连接,用户能够容易的确定 DNS 服务器的 IP地址。当主机发出 DNS 请求后,该请求被发往本地 DNS 服务器,它起着代理的作用,并将该请求转发到 DNS 服务器层次系统中。

DNS 缓存

DNS 缓存(DNS caching) 有时也叫做 DNS 解析器缓存,它是由操作系统维护的临时数据库,它包含有最近的网站和其他 Internet 域的访问记录。也就是说, DNS 缓存只是计算机为了满足快速的响应速度而把已加载过的资源缓存起来,再次访问时可以直接快速引用的一项技术和手段。那么 DNS 的缓存是如何工作的呢?

DNS 缓存的工作流程

在浏览器向外部发出请求之前,计算机会拦截每个请求并在 DNS 缓存数据库中查找域名,该数据库包含有最近的域名列表,以及 DNS 首次发出请求时 DNS 为它们计算的地址。

DNS 记录和报文

共同实现 DNS 分布式数据库的所有 DNS 服务器存储了资源记录(Resource Record, RR),RR 提供了主机名到 IP 地址的映射。每个 DNS 回答报文中会包含一条或多条资源记录。RR 记录用于回复客户端查询。

资源记录是一个包含了下列字段的 4 元组

(Name, Value, Type, TTL)

RR 会有不同的类型,下面是不同类型的 RR 汇总表

DNS RR 类型 解释
A 记录 IPv4 主机记录,用于将域名映射到 IPv4 地址
AAAA 记录 IPv6 主机记录,用于将域名映射到 IPv6 地址
CNAME 记录 别名记录,用于映射 DNS 域名的别名
MX 记录 邮件交换器,用于将 DNS 域名映射到邮件服务器
PTR 记录 指针,用于反向查找(IP地址到域名解析)
SRV 记录 SRV记录,用于映射可用服务。

DNS 报文

DNS 有两种报文,一种是查询报文,一种是响应报文,并且这两种报文有着相同的格式,下面是 DNS 的报文格式

下面对报文格式进行解释

  • 前 12 个报文是 首部区域,也就是说首部区域有 12 个字节,第一个字段(标识符)是一个 16 比特的数,用于标示该查询。这个标识符会被复制到对查询的回答报文中,以便让客户用它来匹配发送的请求和接受到的回答。 标志字段含有若干标志,标志字段表示为 1 比特,它用于指出报文是 0-查询报文还是 1-响应报文。

  • 问题区域包含着正在进行的查询信息。这个区域包括:1) 名字字段,包含正在被查询的主机名字;2) 类型字段,指出有关该名字的正被询问的问题类型,例如主机地址是与一个名字相关联(类型 A)还是与某个名字的邮件服务器相关联(类型 MX)。

  • 在来自 DNS 服务器的回答中,回答区域包含了对最初请求的名字的资源记录。上面说过 DNS RR记录是个四元组,而且元组中的 Type 会有不同的类型。在回答报文的回答区域中可以包含多条 RR,因此一个主机名能够有多个 IP 地址。

  • 权威区域 包含了其他权威服务器的记录

  • 附加区域 包含了其他有帮助的记录。

关于具体 DNS 记录的详细介绍我会出一篇文章专门探讨。

P2P 文件分发

我们上面探讨的协议 HTTP、SMTP、DNS 都采用了客户-服务器 模式,这种模式会极大依赖总是打开的基础设施服务器。而 P2P 是客户端与客户端模式,对总是打开的基础设施服务器有最小的依赖。

P2P 的全称是 Peer-to-peer, P2P ,是一种分布式体系结构的计算机网络。在 P2P 体系中,所有的计算机和设备都被称为对等体,他们互相交换工作。对等网络中的每个对等方都等于其他对等方。网络中没有特权对等体,也没有主管理员设备。

从某种意义上说,对等网络是计算机世界中最平等的网络。每个对等方都相等,并且每个对等方具有与其他对等方相同的权利和义务。对等体同时是客户端和服务器。

实际上,对等网络中可用的每个资源都是在对等之间共享的,而无需任何中央服务器。P2P 网络中的共享资源可以是诸如处理器使用率,磁盘存储容量或网络带宽等。

P2P 用来做什么

P2P 的主要目标是共享资源并帮助计算机和设备协同工作,提供特定服务或执行特定任务。如前面说到的,P2P 用于共享各种计算资源,例如网络带宽或磁盘存储空间。 但是,对等网络最常见的例子是 Internet 上的文件共享。 对等网络非常适合文件共享,因为它们允许连接到它们计算机等同时接收文件和发送文件。

BitTorrent 是 P2P 使用的主要协议。

P2P 网络的作用

P2P 网络具有一些使它们有用的特征

  • 很难完全掉线,即使其中的一个对等方掉线,其他对等方仍在运行并进行通信。 为了使 P2P(对等)网络停止工作,你必须关闭所有对等网络。对等网络具有很强的可扩展性。 添加新的对等节点很容易,因为你无需在中央服务器上进行任何中央配置。
  • 当涉及到文件共享时,对等网络越大,速度越快。 在 P2P 网络中的许多对等点上存储相同的文件意味着当某人需要下载文件时,该文件会同时从多个位置下载。

TELNET

TELNET 又称为远程登录,是一种应用层协议,它为用户提供了在本地机器上就能够操控远程主机工作的能力。例如下面这幅图所示

主机 A 可以直接通过 TELNET 协议访问主机 B。

TELNET 利用 TCP 的一条连接,通过一条连接向主机发送文字命令并在主机上执行。

使用 TELNET 协议进行远程登录时需要满足一下几个条件

  • 必须知道远程主机的 IP 地址或者域名
  • 必须知道登录标识和口令

TELNET 远程登录一般使用 23 端口

TELNET 的工作过程如下

  • 本地主机与远程主机建立连接,这个连接其实是 TCP 连接,用户需要知道指定主机的 IP 地址或者域名
  • 与远程主机建立连接后,在本地主机终端上输入的字符都会以 NVT(Net Virtual Terminal) 的形式发送至远程主机,这个过程实际上是发送一个数据包到远程主机。
  • 远程主机接受数据包后,产生的输出会以 NVT 的格式发送给本地主机一个数据包,包括输入命令回显和命令执行结果
  • 最后,本地主机终端对远程主机撤销链接,这个过程实际上就是 TCP 断开连接的过程。

SSH

TELNET 有一个非常明显的缺点,那就是在主机和远程主机的发送数据包的过程中是明文传输,未经任何安全加密,这样的后果是容易被互联网上不法分子嗅探到数据包来搞一些坏事,为了数据的安全性,我们一般使用 SSH 进行远程登录。

SSH 是加密的远程登录系统。使用 SSH 可以加密通信内容,即时数据包被嗅探和抓取也无法破解所包含的信息,除此之外,SSH 还有一些其他功能

  • SSH 可以使用更强的认证机制
  • SSH 可以转发文件
  • SSH 可以使用端口转发功能

端口转发(Port forwarding)是 SSH 为网络安全通信使用的一种方法。SSH 可以利用端口转发技术来传输其他 TCP/IP 协议的报文,当使用这种方式时,SSH 就为其他服务在客户端和服务器端建立了一条安全的传输管道端口转发是指将特定端口号所收到的消息转发到指定 IP 地址和端口号的一种机制

FTP

FTP(File Transfer Protocol,文件传输协议) 是应用层协议之一。FTP 协议包括两个组成部分,分为 FTP 服务器和 FTP 客户端。其中 FTP 服务器用来存储文件,用户可以使用 FTP 客户端通过 FTP 协议访问位于 FTP 服务器上的资源。

由于 FTP 传输效率非常高,一般用来在网络上传输大的文件。

默认情况下 FTP 协议使用 TCP 端口中的 20 和 21 这两个端口,其中 20 用于传输数据,21 用于传输控制信息。FTP TCP 21 号端口上进行文件传输时,每次都会建立一个用于数据传输的 TCP 连接,数据传输完毕后,传输数据的这条连接也会被断开,在控制用的连接上继续进行命令或应答的处理。

SMTP

提供电子邮件服务的协议叫做 SMTP(Simple Mail Transfer Protocol), SMTP 在传输层也是用了 TCP 协议。

早起电子邮件是在发送端主机和接收端主机之间直接建立 TCP 连接。发送方编写好邮件之后会将邮件保存在磁盘中,然后与接受主机建立 TCP 连接,将邮件发送到接受主机的磁盘中。当发送方把邮件发送后,再从本地磁盘中删除邮件。如果接受主机因为特殊情况无法接收,发送端将等待一段时间后重新发送。

这种方法虽然能够保证电子邮件的完整性和有效性,但却不适合当今的互联网,因为早期的电子邮件只能在线发送,这种方式显然不够成熟。

针对于此,提出了邮件服务器的概念。邮件服务器构成了整个邮件系统的核心。每个接收方在其中的邮件服务器上会有一个邮箱(mailbox) 存在。用户的邮箱管理和维护发送给他的报文。

一个典型的邮件发送过程是:从发送方的用户代理开始,传输到发送方的邮件服务器,再传输到接收方的邮件服务器,然后在这里被分发到接收方的邮箱中。用接收方的用户想要从邮箱中读取邮件时,他的邮件服务器会对用户进行认证。如果发送方发送的邮件无法正确交付给接收方的服务器,那么发送方的用户代理会把邮件存储在一个报文队列(message queue)中,并在以后尝试再次发送,通常每 30 分钟发送一次,如果一段时间后还发送不成功,服务器就会删除报文队列中的邮件并以电子邮件的方式通知发送方。

现在你知道了两台邮件服务器邮件发送的大体过程,那么,SMTP 是如何将邮件从 Alice 邮件服务器发送到 Bob 的邮件服务器的呢?主要分为下面三个阶段

  • 建立连接:在这一阶段,SMTP 客户请求与服务器的25端口建立一个 TCP 连接。一旦连接建立,SMTP 服务器和客户就开始相互通告自己的域名,同时确认对方的域名。
  • 邮件传送:一旦连接建立后,就开始邮件传输。SMTP 依靠 TCP 能够将邮件准确无误地传输到接收方的邮件服务器中。SMTP 客户将邮件的源地址、目的地址和邮件的具体内容传递给 SMTP 服务器,SMTP 服务器进行相应的响应并接收邮件。
  • 连接释放:SMTP 客户发出退出命令,服务器在处理命令后进行响应,随后关闭 TCP 连接。

MIME 类型

最一开始,互联网中的电子邮件只能处理文本格式,后来也逐渐扩展为 MIME 类型,我们上面也简单提到了一句 MIME 类型,MIME(Multipurpose Internet Mail Extensions) 是用途互联网邮件扩展类型。

它是一个互联网标准,扩展了电子邮件标准,使其能够支持很多格式,这些格式如下

  • 超文本标记语言文本 .html text/html
  • xml文档 .xml text/xml
  • 普通文本 .txt text/plain
  • PNG图像 .png image/png
  • GIF图形 .gif image/gif
  • JPEG图形 .jpeg,.jpg image/jpeg
  • AVI 文件 .avi video/x-msvideo 等。

后记

文章涵盖了许多应用层协议,包括 HTTP、DNS、SMTP、FTP、TELNET 协议等

这些应用层协议我们在日常工作中都会用到,我们不仅仅是用户,还是程序员,势必要对其进行了解,我给你画了一些图帮助你理解清楚这些协议,简化的背后却是复杂而艰巨的规范标准和开发的复杂。

如果文章写的还不错,希望读者朋友们可以点赞、在看、分享、留言,这将是我继续更文的动力,也是我涨粉的动力,希望您可以支持下。

在我的 程序员cxuan 同名公众号下回复 cxuan 领取下面这些 PDF,纯自己手写。

BbqtMt.md.png
BbqJxI.md.png
Bbq8Gd.md.png
BbqGRA.md.png
BbqxFe.md.png
BbqwdS.md.png
BbqUqf.md.png
BbqDiQ.md.png
Bbq0Ig.md.png
BbqhoF.md.png
BbqXdO.md.png
BbqOeK.md.png

posted @ 2020-11-10 09:33  程序员cxuan  阅读(1721)  评论(0编辑  收藏  举报