计算机网络原理(二):应用层

  • 应用层协议原理
  • web和HTTP
  • 英特网中的电子邮件
  • DNS:英特网的目录服务
  • P2P文件分发
  • 视频流和网络内容分发网:CDN
  • 套字节编程

 一、应用层协议原理

1.1网络应用程序体系结构

网络应用程序是运行在不同的端系统上,通过网络彼此通信的程序。现代网络应用程序中所使用的两种主流体系结构是:客户——服务器体系结构、对等(P2P)体系结构。

客户——服务器体系结构: 即client-server architecture,C/S模式。有一个总是打开的主机称为服务器,它服务于来自许多其他称为客户的主机的请求。服务器接受来自某客户对某对象的请 求时,它向客户发送所请求的对象作为响应。需要注意的是,在客户——服务器体系结构所具备的一些特征:客户之间不直接通信、服务器具有固定的、周知的地 址,该地址称为IP地址。在C/S模式中,常常会出现一台服务器会跟不上所有客户的请求,并且性能会随着用户请求的增长会出现断崖式下降,为此配备大量主 机的数据中心常被用于创建强大的虚拟服务器,并且还可能会采用多个数据中心的方式架构服务系统。

对等(P2P)体系结构: 即P2P architecture,对位于数据中心的专用服务器有最小的(或者没有)依赖。应用程序在间断连接的主机之间使用直接通信,这些主机对被称为对等方。 这些对等机并不为服务提供商用,相反却为用户控制的桌面机和膝上机所有,因为对等方通信不必通过专门的服务器,该体系结构被称为对等方到对等方。许多目前 流行的、流量密集型应用都是P2P体系结构。包括文件共享、对等方协助下载加速器、英特网电话和视频会议。

某些应用具有混合的体系结构,它结合了客户——服务器和P2P的元素,对于许多即时通讯应用而言,服务器被用于跟踪用户的IP地址,但用户到用户的报文在用户主机之间(无须通过中间服务器)直接发送。

1.2进程通信

构 建网络应用程序前,需要对运行在多个端系统上的程序是如何通信的情况有一个基本了解。用操作系统的术语来说,进程通信实际上实际上是进程,而不是程序,一 个进程可以被认为是运行在端系统中的一个程序。当多个进程运行在相同的端系统上时,它们使用进程通信机制相互通信(比如管道和消息队列)。

在两个不同端系统上的进程,通过跨计算机网络交换报文(message)而相互通信。发送进程生成并向网络发送报文,接收进程接收这些报文并可能通过会送报文进行响应。

客户和服务器进程: 网络应用程序由成对的进程组成,这些进程通过网络相互发送报文。没对通信进程通常将这两个进程表示为:客户(client)、服务器(server)。例 如在web应用程序中,一个客户浏览器进程与一台web服务器进程交换报文;在P2P文件共享系统中,文件从一个对等方中的进程传输到另一个对等方的进 程。对于web而言,浏览器是一个客户进程,web服务器是一台服务器进程。对于P2P文件共享,下载文件的对等方标识为客户,上载文件的对等方标识为服 务器。

进程与计算机网络之间的接口: 没对通信进程基于下面的网络交换报文,在进程和网络之间通过一个称为套字节(sock-et)的软件接口实现报文的发送和接收,当一个进程想向另一台主机 上的进程发送报文时,它将报文通过套字节推向网络;当一个进程需要接收另一台主机通过网络发送过来的报文,它通过套字节从网络接收报文。

套 字节是同一台主机内应用层与运输层之间的接口,由于套字节是建立网络应用程序的可编程接口,因此套字节也称为应用程序和网络之间的应用程序编程接口 (Application Programming Interface,即API)。应用程序开发者可以控制套字节在应用层端的一切,但对于该套字节的运输层端几乎没有控制权。应用程序开发者对于运输层的 控制仅限于:选择运输层协议、设置部分运输层参数(如:缓存大小、报文长度等)。

进程寻址:一台主机上运行的进程为了向在另一台主机上运行的进程发送分组,接收进程需要有一个地址,为了标识该进程需要定义两种信息:主机的地址(即IP)、另一台主机中指定接收进程的标识符(即端口号)。

1.3可供应用程序使用的运输服务

在 前面进程通信中介绍了套字节,应用程序将报文推进套字节,套字节的另一侧运输层协议从套字节得到报文。包括英特网在内的很多网络提供了不止一种运输层协 议,开发一个应用时,必须选择一种可用的运输层协议,基于不同的运输层协议提供的服务特性,选择适合应用的运输层协议。运输层面对应用程序服务要求一般本 可以分为:可靠数据传输、吞吐量、定时、安全性。

可靠数据传输: 因为在计算机网络中,由于路由中的缓存溢出或者分组中某些比特损坏后可能会丢失,但在很多实际应用程序中是不允许数据传输出现不完整或丢失的,为了支持这 些应用程序一端发送的数据正确、完整地交付给该应用程序的另一端,如果一个协议提供了这样的服务,就认为提供了可靠数据传输(reliable data transfer)。而支持这种服务特性的是运输层协议,当发送进程通过套字节将其数据传递进套字节,支持可靠数据传输的运输层协议就会通过相应的服务机 制保证数据无差错的传送到接收进程。相反,如果运输层协议不提供可靠数据传输时,发送进程发送的某些数据可能就到达不了接收进程,这种运输层协议通常被应 用在能容忍数据丢失的应用程序,比如音频/视频传输的相关应用。

吞吐量: 发送进程能够向接收进程交付比特的速率。因为通常情况下两个进程之间的网络路径的资源是与其他应用程序共享的,两个进程之间的数据传输会因为其他应用到达 或退出其共享的网络资源,影响其传输速率,而有的应用程序需要一定的速率保障才能够实现相关的功能,而运输层协议能够以某种特定的速率确保可用的吞吐量。

也 就是应用程序能够请求r比特/秒的确保吞吐量,并且运输层协议能够确保可用吞吐量总是至少r比特/秒。如果运输层协议不能提供这种吞吐量,该应用程序或以 较低速率进行编码或者必须放弃发送。比如英特网电话应用对语音以32kbps的速率进行编码,如果吞吐量只有其一半就几乎或根本没有用处,这种具有吞吐量 要求的应用程序也被称为带宽敏感应用。而有的应用程序可以利用当前或多或少的吞吐量,这种应用被称为弹性应用,比如电子邮件、文件传输、web传输等。

定时:所谓定时是指发送进程将数据注入套字节中后,每个比特到达接收方的套字节不迟于指定的时间。运输层也能够提供这种定时保证,这种服务对于时实应用程序非常重要,比如网络电话、虚拟环境、电话会议、多方游戏等。

安全性: 运输层协议能够为应用提供一种或多种安全性服务,比如在发送主机中,运输层协议能够加密由发送进程传输的所有数据,在接收方主机中,运输层协议能够在将数 据交付给接收进程之前解密这些数据。除了这种数据加密的安全性保障,还包括数据完整性和端点鉴别,这部分内容在后面的网络安全相关博客中详细介绍。

1.4英特网提供的运输服务

英特网为应用程序提供两个运输层协议,即UDP和TCP。

TCP服务模型包 括面向连接服务和可靠数据传输服务,当应用程序调用TCP作为其运输协议时,该应用程序就能获取来自TCP的这两种服务。TCP还具有拥塞控制机制,这种 服务不一定能为通信进程带来好处,但能为因特网带来整体好处。如果发送方和接收方之间的网络出现拥塞时,TCP的拥塞控制机制会抑制发送进程。

面向连接的服务: 在应用层数据报文开始流动之前,TCP让客户和服务器相互交换运输层控制信息。这所谓的握手过程提醒客户端和服务器,让它们为大量分组到来做好准备。在握 手阶段后,一个TCP连接就在两个套字节之间建立了。这条链接是双工的,连接双方的进程可以在此连接上同时进行报文收发。当应用程序结束报文发送时,必须 拆除该连接。

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

UDP服务模型是 一种不提供必要服务的轻量级运输协议,它仅提供最小服务。UDP是无连接的,因此两个进程通信前没有握手过程。UDP协议提供的是一种不可靠数据传输服 务,也就是说当进程将一个报文发送进UDP套接字时,UDP协议并不保证该报文将到达接收进程,不仅如此,接收进程的报文可能是乱序到达的。UDP没有拥 塞控制机制,所以UDP的发送端可以用它选定的任何速度向其下层网络注入数据(而实际端到端吞吐量可能小于该速率)。

英特网运输协议所部提供的服务: 吞吐量和定时服务,这两个服务虽然没有协议提供,但不代表对传输速率和传输时间敏感的相关应用就不能运行在今天的因特网上,而且相反,随着技术的发展及相 关机构对互联网基础设施的大量投入,网络环境越来越好,越来越多的传输速率和传输时间敏感的应用越来越多。虽然目前没有协议提供这两种服务,但可以通过一 些设计技巧来解决相关的问题,在后面的相关具体博客中介绍。

1.5应用层协议

应用层协议(application-layer protocol)定义了运行在不同端系统上的应用程序进程如何相互传递报文,包括:交换报文的类型、各种报文类型的语法、字段的语义、确定一个进程何时以及如何发送报文,对报文进行相应的规则。

 二、Web和HTTP

Web 即万维网(world wide web),它将因特网从只是很多数据网之一的地位提升为仅有一个数据网。web最具吸引力的特性是Web的按需操作,当用户需要什么,就能得到所想要的内 容。除了按需操作以外,还有超链接和搜索引擎帮助我们在Web站点的海洋里导航等。

2.1HTTP协议概述

Web 的应用层协议是超文本传输协议(HyperText Transfer Protocol,HTTP),它是Web的核心,在RFC 1945和RFC 2616中进行定义。HTTP有两个程序实现:客户端程序和服务端程序。客户端程序和服务端程序运行在不同的端系统中,通过交换HTTP报文会话。 HTTP定义了这些报文的结构以及客户和服务器进行报文交换的方式。在解析HTTP之前,先来了解一些Web术语:

Web页面:即Web page,也叫文档。是由对象组成的,一个对象只是一个文件,比如HTML文件、JPEG图形、Java小程序、视频片段。

URL 地址:多数Web页面含有一个HTML基本文件以及几个引用对象。HTML基本文件通过对象的URL地址引用页面中的其他对象。URL地址由两部分组成: 存放对象的服务器主机名和对象的路径名(比如:http://www.xxxx.xxx/xxxx/xxxx.xx,www.xxxx.xxx表示的就是 主机名,/xxxx/xxxx.xx是对象在主机中的路径名)。

Web浏览器:实现了HTTP的客户端,所以在Web环境中会交替使用"浏览器"和“客户”这两个术语。

Web服务器:实现了HTTP的服务端,它用于存储Web对象,每个对象由URL寻址。

 HTTP 使用TCP作为它的支撑运输协议,HTTP客户首先发起一个与服务器的TCP连接,一旦连接建立,该浏览器和服务器进程就可以通过套接字接口访问TCP。 客户向它的套接字接口发送HTTP请求报文并从它的套接字接口接收HTTP相应报文。服务器从它的套接字接口接收请求报文和向它的套接字接口发送HTTP 响应报文。

一旦客户向它的套接字接口发送了一个请求报文,该报文就脱离了客户端控制并进入TCP的控制。TCP为HTTP提供可靠数据传 输 服务,保障HTTP响应报文能完整的到达。这就是网络分层体系结构的优点,即HTTP协议不必担心数据丢失,也不关注TCP从网络的数据丢失和乱序故障中 恢复的细节。

HTTP服务器并不保存关于客户的任何信息,所以说HTTP是无状态协议。比如客户在短短几秒内连续多次请求一个对象,服务器并不会因为为客户提供了该对象不再做出反应,而是会针对每次请求都响应一次对象。

2.2非持续连接和持续连接

在许多英特网应用程序中,客户和服务器在一段时间范围内通信,其客户会发出一系列请求并且服务器对每个请求进行响应。每个请求/响应对是经过单独的TCP连接发送(即非持续连接),还是所有请求经过同一个TCP连接发送(即持续连接)。

非持续连接: 使用HTTP非持续连接的请求响应模式要经过五个步骤完成一个对象的传输操作,这五个步骤是:HTTP客户进程在端口80发起到服务器的TCP连接、 HTTP客户经它的套接字向服务器发送一个HTTP请求报文、HTTP服务器进程经过它的套接字接收该请求,并根据URL的资源地址从其存储器中检索出对 象通过套接字向客户端响应报文、HTTP服务器进程通知TCP断开TCP连接、HTTP客户接收响应报文,TCP连接关闭。在非持续连接模式下每个对象的 请求都需要重复经过这五个过程,可以通过下面的示图来更值观的了解这个过程:

HTTP请求与相应的往返时间:即Round-Trip Time,RTT。RTT包括分组传输时延、分组在中间路由器和交换机上的排队时延、分组的处理时延。

非持续连接的缺点: 第一,比如为每一个请求的对象建立和维护全新的连接,对于这样的连接,在客户和服务器中都需要分配TCP的缓冲和保持TCP变量,这给Web服务器带来严 重的负担,因为一台服务器可能同时服务于数以百计不同的客户。第二,每一个对象经受两倍的RTT的交付延时,及一个RTT用于创建TCP连接,另一个 RTT用于请求和接收一个对象。

持续连接: 所谓持续连接就是多个HTTP请求可以在同一个TCP连接上完成,而不需要像非持续连接那样针对每一个HTTP请求建立一个TCP连接。而持续连接又分为 流水线方式的持久HTTP和非流水线方式的持久HTTP。流水线方式的持久HTTP就是在建立一个TCP连接之后,客户可以遇到一个引用对象就产生一个请 求,客户端的请求只根据需求来发起,而不受其他约束,这是HTTP1.1的持续连接实现方式。非流水方式的持久HTTP是在建立一个TCP连接之后,客户 在遇到一个引用对象后,需要收到前一个请求的响应才能向服务器发送这个引用对象的请求,这是HTTP2的持续连接实现方式。

当一个持续连接的HTTP的TCP连接在一定时间内没有被使用,HTTP服务器就会自动关闭该链接。HTTP的默认模式时使用带流水线的持续连接,在HTTP2是在HTTP1.1的基础上构建了非流水线的持久HTTP。

2.3HTTP报文

HTTP规范RFC1945、RFC2616、RFC7540包含了HTTP报文格式的定义,HTTP报文有两种:请求报文和响应报文。

请求报文:HTTP 请求报文的第一行叫做请求行(request line),其后继的行叫作首部行(header line)。请求行有三个字段:方法字段、URL字段、HTTP版本字段。方法取值包括:get、post、head、put、delete,相当于是协 议命令。如果是post和put请求方法,就还会在报文首部后面增加一个报文实体(entity body),实体部分就是请求时携带的上传数据或者表单字段的输入值。下面再来看看请求报文中一些重要的首部:

host:www.xxxx.xxx,用于指明对象所在的主机。

connection:close,用于浏览器告诉服务器不要使用持续连接,当服务器发送完请求的对象就关闭连接。

user-agent:mozilla/5.0,用于指定用户代理,服务器可以根据不同类型的用户代理实际发送相同对象的不同版本。

Accept-language:表示用户想得到该对象的语法版本。

响应报文:HTTP响应报文的第一行叫做状态行(status line),后继是首部行,然后是报文实体。状态行有三个字段:协议版本字段、状态码、相应的状态信息。协议版本字段表示正在使用的协议版本信息,状态码指示了请求响应的状态信息。响应报文中比较重要的一些首部:

connection:close,告诉客户发完报文后关闭连接。

date:服务器产生并发送报文的日期和时间。

server:表示报文由一台什么服务器产生,类似user-agent的作用。

last-modified:表示对象创建或最后修改的日期和时间。

length:表示发送的对象的字节数;

content-type:表示实体类型,浏览器内核会根据这个类型来加载和解析该对象。

 状态码及相应的短语:

200 OK:请求成功,信息在返回的响应报文中;
301 moved permanently:请求的对象已经永久转移了,新的URL定义在响应报文的location首部行中,这也是常说的重定向。客户将自动获取新的URL的对象作为原本要请求的对象。
400 BadRequest:一个通用差错代码,指示该请求不能被服务器理解。
404 Not Found:被请求的对象不在服务器上,一般请求路径写错就会报这个状态码。
505 HTTP Version Not Supported:服务器不支持请求报文使用的HTTP协议版本。

2.4用户与服务器的交互:cookie

前面提到过HTTP服务器是无状态的,但有些时候Web站点有希望能够识别用户,为此HTTP使用了cookie,cookie在RFC6265中定义,它允许站点对用户进行跟踪,目前大多数站点都使用了cookie。

cookie包括四个组件:HTTP响应报文中的cookie首部、HTTP请求报文中的cookie首部、在客户端系统中保留有一个cookie文件,并由用户的浏览器进行管理、Web站点的一个后端数据库。

支 持cookie实现用户跟踪的报文首部有:响应报文中的set-cookie,它负责携带服务器给用户的相关标识信息,当浏览器接收到报文以后,浏览器会 将这个首部携带的数据存入cookie文件。下次用户向服务器发送请求时,在请求报文上的cookie首部上携带上次存入cookie文件的数据,服务器 收到这个请求后就会知道这个用户是谁了,然后实现相关的用户跟踪功能。

2.5Web缓存

Web 缓存器(Web cache)也叫代理服务器(proxy server),它是能够代表初始Web服务器HTTP请求的实体。Web缓存器有自己的磁盘空间,并在存储空间中保持最近请求过的对象的副本,使得用户 的所有请求先指向Web缓存器。下面具体描述一下用户通过Web缓存器请求资源的全部过程:

1.浏览器创建一个到Web缓存器的TCP连接,并向缓存器中的对象发送一个HTTP请求。

2.Web缓存器检查存储空间看看是否存储了该对象,如果有,Web缓存器就向客户响应该对象。

3.如果Web缓存器中没有该对象,它就打开一个与对象的初始服务器的TCP连接,Web缓存器则在这个缓存器到服务器的TCP连接上发送一个对该对象的HTTP请求。在收到该请求后,初始服务器向该Web缓存器发送具有该对象的HTTP响应。

4.当Web缓存器接收到该对象时,它在本地存储空间存储一份副本,并向客户的浏览器用HTTP响应报文发送该副本。

Web 缓存器一般由ISP提供,一般大学可能在他的校园网络上安装一台缓存器,并将所有校园网上的用户浏览器配置指向它。或者,一个住宅ISP可能在他的网络上 安装一台或多台Web缓存器,并预先配置其配套的浏览器指向这些缓存器。Web缓存器可以大大减少客户请求的响应时间,但也可能引入一个新的问题,即存放 在缓存器中的对象副本可能是成就的。HTTP协议通过条件GET方法来解决,如果请求报文使用的是get方法,并且请求报文中包含一个"if- modified-dince"首部行,该缓存器在缓存对象资源时服务器响应的报文携带一个last-modified首部,这个首部的值是对象最后的修 改时间。当下一次浏览器载器使用HTTP协议请求这个URL对象资源时,请求报文中的if-modified-dince就会将上次服务器响应的 last-modified的值作为自己的值发送到服务器与相对应的对象资源的修改时间比较,如果相等就会给缓存器响应一个304状态并由缓存器将缓存的 对象资源响应给浏览器,如果if-modified-dince与服务器中的对象资源最后修改时间不一致,服务器就会给缓存器响应200状态码并且在报文 实体部分携带最新的对象数据和last-modified首部携带最新的对象文件修改时间给缓存器,缓存器再将最新的数据转发给浏览器。

关于HTTP缓存可以参考我之前一篇专门解析的博客,这篇博客还包含了不同缓存策略示例代码:HTTP缓存机制

 三、英特网中的电子邮件

3.1电子邮件系统的总体概要

英特网电子邮件系统主要有三个组成部分:用户代理(user agent)、邮件服务器(mail server)、简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)。

用户代理:又被称为"邮件阅读器",也就是日常使用的邮件应用客户端,用于撰写、编辑、阅读邮件,比如outlook、foxmail、qq邮箱、163邮箱等。

邮件服务器:邮件系统中管理和维护发送给用户的邮件,输出报文队列保持待发邮件报文。

EMail: 邮件服务器之间的SMTP协议实现email报文发送,使用TCP在客户端和服务器之间传送报文,端口号25。负责将邮件从发送方服务器传输到接收方服务 器,传输包含三个阶段:握手、传输报文、关闭连接。在邮件服务器上,EMail协议通过ASCII码文本命令/响应交互,EMail报文首部必须是 ASCLL码字符。

简单的描述一下邮件的收发流程:1. 用户A通过邮件代理编辑邮件并将邮件通过指定用户B的邮件地址发送给用户B、2.用户A的邮件代理把报文发送给它的邮件服务器,该报文会被存入邮件队列、 3.运行在用户A邮件服务器上的SMTP客户端发现报文队列中的这个邮件报文,它就与用户B的邮件服务器上的SMTP服务器建立一个TCP连接、4.经过 初始的SMTP握手后,SMTP客户端通过TCP连接发送用户A的邮件报文、5.在用户B的邮件服务器上,SMTP的服务器接收该报文,B用户的邮件服务 器将该报文放入B用户的邮箱中、6.用户B方便的时候,调用用户代理阅读该报文。

3.2SMTP

SMTP 是英特网电子邮件中主要的应用层协议,它使用TCP可靠数据传输服务,跟大多数应用层协议一样,SMTP由两部分:即运行在发送方邮件服务器的客户端和运 行在接受方邮件服务器的服务端,但每台邮件服务器即运行SMTP的客户端也运行SMTP的服务端,当一个邮件服务器向其他邮件服务器发送邮件时,它就表示 为SMTP的客户端;当邮件服务器从其他邮件服务器上接收邮件时,它就表示为SMTP的服务端。

下面通过一段SMTP客户(C)与SMTP服务器(S)之间交换报文文本的示例,来理解SMTP的原理:

S:200 hamburger.edu                      #客户向服务器请求建立连接以后,SMTP服务器响应200状态码,描述自己是hamburger.edu
C:HELO crepes.fr                        #SMTP客户使用HELO命令确认收到响应,描述自己是crepes.fr
S:250 Hello crepes.fr, pleased to meet you         #SMTP服务器响应250状态码,确认连接建立成功
C:MAIL FROM:<alice@crepes.fr>                #SMTP客户使用MAIL FROM命令告诉服务器发件人是alice@crepes.fr
S:250 alice@crepes.fr ... Sender ok             #SMTP服务器响应250状态码,确认收到发件人邮箱地址
C:RCPT TO:<bob@hamburger.edu>                #SMTP客户使用RCPT TO命令告诉服务器收件人是bob@hamburger.edu
S:250 bob@hamburger.edu ... Recipient ok          #SMTP服务器响应250状态码,确认收到收件人的邮箱地址
C:DATA                              #SMTP客户使用DATA命令告诉服务器接下来要传输邮件数据了
S:354 Enter mail, end with "." on aline by itself     #SMTP服务器响应354状态码,并告诉客户端使用"."来表示数据传输结束
C:Do you like ketchup?                     #这里是邮件的内容
C:How about pickles?                      #这里是邮件的内容
C:.                                #这里告诉SMTP服务器邮件数据传输结束
S:250 Message accepted for delivery              #SMTP服务器响应250状态码,确认数据接收完成并收到数据传输结束的消息
C:QUIT                               #SMTP客户使用QUIT命令,告诉SMTP服务器这次会话可以结束了
S:221 hamburger.edu closing connection            #SMTP服务器响应221状态码,表示确认关闭连接

通 过上面的示例可以看到SMTP客户端使用命令向SMTP服务器传递各种类型的消息,SMTP服务器通过各种状态码来响应,通过命令和状态码的交互方式实现 邮件报文的传输。SMTP客户端在示例中使用的命令有:HELO、MAIL、RCPT TO、DATA、QUIT,除了这些命令以外在示例中没有出现的AUTH LOGIN命令用于用户身份认证。SMTP同样使用持久连接,SMTP通常情况下并不会为一个邮件启动一个连接,而是采用定时的间隔的方式启动一次连接, 一次连接会将多个邮件报文一次推向服务器。

SMTP与HTTP对比:HTTP是客户端向服务器拉取数据(pull),SMTP客户端向服务器推数据(push),二者都是ASCII形式的命令/响应交互及状态码,SMTP一个报文可以包含多个对象的数据而HTTP只有一个对象的数据。

SMTP 邮件报文:首部行和主体,首部包含To、From、Subject。因为SMTP的报文首部和主体都只能使用ASCLL编码,当传输数据中包含其他编码格 式的字符时,会采用中间编码格式进行转换,比如先将实际数据中的字符转换base64编码格式就可以再使用ASCLL进行编码了,这个中间用于转换的编码 格式可以使用SMTP的content-Transfer-Encoding首部来指定实现,邮件服务器根据这个中间编码格式来转换数据,所以邮件也就可 以实现传输非ASCLL编码的文件了,比如传输中文字符、图片、视频文件等。

3.3邮件访问协议

当用户要查看自己邮箱中的邮件时,也就是用户代理从邮件服务器上拉取数据可以通过POP3、IMAP、HTTP等协议实现。

POP3: 即邮件访问协议(由RFC 1939定义),POP3通过三个阶段进行工作,用户身份认证、事务处理、更新邮箱。用户身份认证通过用户名和口令鉴别用户;事务处理包括用户代理取回报 文,对邮件做删除标记或取消文件删除标记,以及获取邮件的统计信息;更新邮箱是在客户发出了quit命令后,邮件服务器根据删除标记删除对应的邮件并结束 POP3会话。

IMAP:IMAP 服务器将每个报文与一个文件夹联系起来,允许用户用目录来组织报文,允许用户读取报文组件,IMAP在会话过程中保留用户状态包括:目录名、报文ID与目 录名之间的映射。而POP3需要将邮件下载到本地主机上,然后将邮件放到本地文件系统的目录中来管理,在不同的客户机上同一个邮箱用户只能通过主机之间的 拷贝来实现共同管理,而IMAP是通过远程操作将邮件放到邮件服务器的文件系统的相关目录下进行管理,不同的客户机可以同步远程管理邮件服务器上的文件系 统来管理邮件。

基于Web的电子邮件就是通过HTTP协议来实现的邮件访问操作,主要是可以通过Web的丰富功能实现更友好的访问页面,当然也可以通过HTTP的通信交互来实现远程的邮箱管理。

 四、DNS:英特网的目录服务

在 计算机网络中识别主机有两种方式:主机名和IP地址,由于IP地址不方便记忆,通常情况下我们要访问某个主机都是使用主机名,比如 www.baidu.com、www.goolgle.com、gaia.cs.umass.edu等。而路由器则喜欢定长的、有层次结构的IP地址,在 实际的网络数据传输过程中,路由器都是通过IP地址来确定传输链路的各个节点,所以我们需要一种能进行主机名到IP地址转换的目录服务。这就是域名系统 (Domain Name System,DNS)的主要任务。

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

4.1DNS提供的服务

DNS 通常由其他应用层协议所使用,包括HTTP、SMTP、FTP等,将用户提供的主机名解析为IP地址。假设某用户主机上的浏览器请求URL www.someschool.edu/inde.html页面,为了使用户主机能够将一个HTTP请求报文发送到Web服务器 www.someschool.edu,该用户主机必须获得www.someschool.edu的IP地址。其大概过程如下:

1.同一台用户主机上运行着DNS应用的客户端。

2.浏览器从上述URL中抽取出主机名www.someschool.edu,并将这台主机名传给DNS应用的客户端。

3.DNS客户向DNS服务器发送一个包含主机名的请求。

4.DNS客户最终收到一份回答报文,其中含有对应于该主机名的IP地址。

5.浏览器从DNS客户端进程接收到该IP地址,然后向该IP地址的HTTP服务器进程发起一个TCP连接。

从 上面的示例描述中可以看到DNS使其他英特网应用带来了额外的时延,所以为了降低这个时延,想获得的IP地址一般缓存在“附近”的DNS服务器中,这有助 于减少DNS的网络浏览和DNS的平均时延。DNS除了提供IP地址的转换以外,还提供一些重要的服务,比如主机别名、邮件服务器别名、负载分配。

主机别名(host aliasing): 相对复杂的主机名能拥有一个或多个别名,比如relay1.west-cost.enterprise.com的主机,可能还有两个别名enter- prise.com和www.enterprise.com。在这种情况下,relay1.west-coast.enterprise.com也称为规 范主机名(canonical hostname),主机别名比主机规范名更容易记忆。应用程序可以调用DNS来获取主机别名对应的规范主机名以及主机的IP地址。

邮件服务器别名(mail server aliasing): 跟主机别名一样,邮件服务器别名也是为了更好的记忆,比如Bob的雅虎邮件地址bob@ya-hoo.com是这样,雅虎邮件服务器的别名就是ya- hoo.com,其规范主机名可能是这样的relay1.west-coast.hotmail.com。事实上MX记录允许邮件服务器和web服务器使 用相同的别名,例如一家公司的Web服务器和邮件服务器都能叫做enterprise.com。

负载分配(load distribution):DNS 也用于在冗余的服务器之间进行负载分配。比如cnn.com被冗余的分配在多台服务器上,每台服务器运行在不同的端系统上,每个都有不同的IP。一个IP 地址集合与同一个规范主机名相联系,DNS数据库中存储着这些IP地址集合,DNS服务器可以循环使用集合中的IP地址响应给请求该主机名解析的DNS请 求。

DNS由RFC 1034和RFC 1035定义,并且在几个附加的RFC中进行了更新,想深入的了解可以查阅这些RFC或者可以参考Albitz和Liu写的书[Albitz 1993],也可以参考[Mockapetris 1998]和[Mockapetris 2005]。

4.2DNS工作原理概述

根 据前面对DNS服务的简单描述大概了解其就是用于英特网各个应用的客户访问应用主机时,可以使用简单明了的主机名或主机别名通过DNS获取到其真正的IP 地址,从而使各种应用的客户端应用层协议使用真实的IP实现网络连接和数据传输,简单的来说DNS就相当于使一个域名和IP的表,那么只需要一个维护这样 一张表的服务器就可以实现各个主机通过主机名访问到任意英特网上的其他主机。而事实上相对英特网庞大的主机数量来说,这显然是一个不可取的方式,如果采用 一个服务器来实现这个DNS服务存在的设计问题包括:单点故障、通信容量、远距离的集中式数据库、维护这些问题。

单点故障:即如果该DNS服务器崩溃,整个英特网都会随之瘫痪;

通信容量:即单个DNS服务器不得不处理所有DNS查询上亿台甚至更多主机所产生的所有HTTP请求报文和电子邮件报文服务,无论是这台DNS服务器的带宽还是主机的硬件性能都不是能支撑这样的并发量的。

远距离的集中式数据库:这样的设计带来的网络时延是肯定不乐观的。

维护:单个DNS服务器将不得不为所有英特网主机保留记录,如果要维护这样一个庞大的数据肯定需要非常不乐观的时间,风险也是极高的。

为了解决DNS服务器这样的一些需求问题,DNS采用了分布式、层次数据库和DNS缓存的方式实现,下面就从这些角度来解析DNS的工作原理:

4.2.1分布式、层次数据库

为 了处理DNS服务器的扩展性问题,DNS使用大量的DNS服务器以层次结构组织,并且分布在全世界范围内。没有一台DNS服务器拥有因特网上所有主机的映 射,相反这些映射分布在所有DNS服务器。DNS服务器大致可以分为三类,根DNS服务器、顶级域(Top-Level Domain,TLD)DNS服务器、权威DNS服务器,这些服务器以上面的层次结构组织起来。

根DNS服务器:全世界有400多个根名字服务器遍布各地,这些根名字服务器由13个不同的组织管理,根DNS服务器提供TLD服务器的IP地址。

顶级域(DNS)服务器:即TLD服务器,每个顶级域(如com、org、net、edu、gov)和所有国家顶级域(如cn、uk、fr、ca、jp)都有TLD服务器,TLD服务器提供了权威DNS服务器的IP地址。

权威DNS服务器: 在因特网上具有公共可访问主机的每个组织机构必须提供公共可访问的DNS记录,这些记录将这些主机的名字映射为IP地址,一个组织机构的权威DNS服务器 收藏了这些DNS记录,一个组织机构能够选择实现他自己的权威DNS服务器保存这些记录,或者支付费用给某个权威DNS服务器的提供商记录存储这些记录。

根、TLD、权威DNS服务器都处于DNS服务器的层次结构中,还有另一种重要的DNS服务器,称为本地DNS服务器(local DNS server)。严格来说一个本地DNS服务器并不属于该服务器的层次结构中,但它对DNS层次结构至关重要。

当 主机与某个ISP连接时,该ISP提供一台或多台本地DNS服务器,对于一些机构的ISP而言,本地DNS服务器就与主机同处一个局域网中。当主机发出 DNS请求时,该请求被发往本地DNS服务器,本地DNS服务器起到代理作用,并将该请求转发到DNS服务器层次结构中。下图是一台主机 cis.poly.edu通过本地DNS服务器向DNS服务层次结构获取目的主机gaia.cs.umass.edu的IP地址的流程图:

通过上面的示图的看到cis.poly.edu请求DNS服务器查询gaia.cs.umass.edu的过程是采用本地DNS服务器的迭代查询实现,整个流程的解析如下:

1.cis.poly.edu向本地DNS服务器dns.poly.edu发出解析gaia.cs.umass.edu主机名的DNS查询请求,该报文中包含由要被转换的主机名gaia.cs.umass.edu;

2.本地DNS服务器dns.poly.edu收到查询请求后,本地DNS服务器将该报文转发给根DNS服务器;

3.根DNS服务器根据报文中要转换的主机名gaia.cs.umass.edu的edu,即顶级域,然后将edu这个顶级域的IP地址返回给本地DNS服务器;

4.本地DNS服务器dns.poly.edu收到根DNS服务器的响应后,获得gaia.cs.umass.edu所属的DNS层次结构中的TLD DNS服务器的IP地址,再将cis.poly.edu的请求报文转发给该TLD DNS服务器。

5.TLD DNS服务器收到报文后又根据gaia.cs.umass.edu找到记录其DNS记录的权威DNS服务器dns.cs.umass.edu的IP地址,然后将dns.cs.umass.edu的IP地址返回给本地DNS服务器。

6. 本地DNS服务器dns.poly.edu收到权威DNS服务器的响应后,获取dns.cs.umass.edu的IP地址(即目的主机的IP地址),然 后本地DNS服务器将刚刚权威DNS响应携带有目的主机IP地址的报文转发给发出请求的主机cis.poly.edu。

7.发出请求的主 机 cis.poly.edu收到带有目的服务器gaia.cs.umass.edu主机名的DNS解析响应报文,从这个报文中获取目的主机 gaia.cs.umass.edu的IP地址,根据这个IP地址发出请求的主机cis.poly.edu就可以与gaia.cs.umass.edu进 行运输层的通信交互了。

迭代查询: 以上的示例解析就是DNS解析的全部过程,为了获得一台主机的IP地址共发送了8分报文,4份查询报文和4份回答报文,上面这种查询报文都是由本地DNS 服务器向DNS各个层次服务器结构层的DNS服务器转发查询报文,这种本地DNS服务器向DNS各个层次服务器结构层转发查询报文的方式就是迭代查询。除 了迭代查询,DNS查询还有一种递归查询,可以通过下面的示图来了解:

递归查询:就是当发起DNS查询请求后,DNS查询报文被逐层转发,查找到目的服务器地址以后生成DNS解析响应报文再逐层返回响应到发起请求的主机上。

上 面无论是迭代查询还是递归查询,都假设TLD DNS服务器直接有所有权威DNS服务器的DNS记录,而事实上并不一定,TLD DNS服务器可能只知道中间某个DNS服务器有某个权威DNS服务器的的DNS记录,甚至这个中间还是由多台DNS服务器组成的查询层级结构。另一方面就 是可以看到,好像每一个DNS查询请求都需要经过根DNS服务器,想象一下全球400多台根服务器为全球所有英特网域名解析提供DNS查询,看示400多 个不少了,但实际上相对于全球的英特网DNS查询需求来说依然是一个不可能完成的任务,为了庞大的DNS查询需求对根DNS服务器带来的压力,以及降低 DNS查询的时延,在每台DNS服务器中都提供了DNS记录的缓存,下面具体来了解。

4.2.2DNS缓存

实 际上DNS缓存原理非常简单,当在一个请求链中,某个DNS服务器接收一个DNS回答时,它就能将映射缓存在本地存储器中,当下一次有同一个主机名的 DNS请求时就会直接使用这个缓存的映射打包成一个DNS解析报文响应给这个请求,这就大大降低了DNS查询的时延和DNS服务器的压力,当然因为主机名 可能随时发生变化,为了保证这个存储的映射是有效的,一般DNS缓存都是短期内的,比如一两天的缓存时间,还有会根据最新的对权威DNS服务器的请求去更 新缓存。

比如一个主机请求解析另一个主机名的IP,在本地DNS服务器内就缓存了这个主机的IP和主机名的映射,本地DNS服务器就会直 接 将这个映射返回给请求主机。又比如在本地DNS服务器中可能没有这个映射,但当本地主机向根DNS服务器请求时,在根DNS服务器上缓存了这个映射,那么 这个DNS解析请求就会直接通过根DNS服务器根据请求链路返回给请求主机。因为可能在不同的DNS服务器节点上因为不同的用户请求都会产生缓存,所以当 一个DNS解析请求到来时,往往并不需要每次都到权威DNS服务器去获取这个DNS映射,这样就通过DNS缓存提高了DNS解析的效率。

4.3DNS记录

共 同实现DNS分布式数据库的所有DNS服务器存储了资源记录(Resource Record,RR),即缓存了这些资源记录,RR提供了主机名到IP地址的映射。每个DNS回答报文包含了一条或多条资源记录。关于详细的DNS资源记 录和报文信息可以在[Albitz 1993]或RFC 1034、RFC 1035中找到,这里大概的做一些介绍:

DNS资源记录包含的四个字段:(Name, Value, Type, TTL),这四个字段的含义分别是:

TTL表示的是该记录的生存时间,它决定了资源记录应当从缓存中删除的时间。而Name和Value的值取决于Type,下面通过对Type这个字段的解析来逐个分析:

当Type=A时,Name就是主机名,Value就是该主机名对应的IP地址。因此一条A类型的资源记录提供了标准的主机名到IP地址的映射。比如(relay1.bar.foo.com,145.37.93.126,A)就是一条A记录。

当Type=NS,Name就是一个域(如foo.com),而Value是直到如何获取该域中主机IP地址的权威DNS服务器的主机名,这个记录用于沿着查询链来路由DNS查询。比如(foo.com,dns.foo.com,NS)这就是一条NS记录。

当Type=CNAME,则Value是别名为Name的的主机对应的规范主机名。该记录能够想查询的主机提供一个规范的主机名,例如(foo.com,relay1.bar.foo.com,CNAME)就是一条CNAME类型的记录。

当 Type=MX,则Value是个别名为Name的邮件服务器的规范主机名。例如(foo.com,mail.bar.foo.com,MX)就是一条 MX记录。所以个公司的邮件服务器和其他的服务器(Web服务器)可以使用同一个别名,而为了邮寄服务器的规范主机名。当要获得邮件服务器的规范主机名 时,DNS客户请求一条MX记录即可,而为了获得其他服务器的规范主机名,DNS客户端就要请求一条CNAME记录。

假设一台DNS服务器是一台权威DNS服务器,那么该DNS服务器会有一条用于该主机名的A类型记录。

假 设一台DNS服务器不是权威DNS服务器,那么该DNS服务器除了会有一条用于该主机的A类型记录,还将包含一条NS类型记录,用于对应其主机名的域。这 条NS类型记录就是用来解析其域下的其他DNS服务器,TLD DNS服务器就是这类DNS服务器的一种。当一个DNS查询通过它上层的DNS服务器提供它的NS记录中的域名,就可以查找到这台DNS服务器的主机名, 然后通过它的A记录的IP地址就可以访问到它了。

4.4DNS报文

在DNS网络协议中只有查询报文和回答报文,通过下面的DNS报文格式来值观的了解基本结构:

前面12个字节是报文的首部:

标识符字段:是一个16比特的数,用于标识该查询,这个标识符会被复制到该查询报文的回答报文中,以便让客户用来匹配发送的请求和接收的回答,也就是说DNS查询报文于其对应的DNS回答报文的标识符是一样的;

标志字段: 是个1比特的数,用来表示该报文是查询报文还是回答报文,查询报文的标志用0表示,回答报文用1表示。除了这两种标志以外,在报文中还包括1比特的“权威 的”、“希望递归”、“递归可用”三种标志。如果是权威DNS服务器的回答报文会使用1比特的“权威的”标志、如果是递归查询的话查询报文的标志为1比特 的“希望递归”标志、如果是递归查询在某个DNS服务器节点获得可用的查询结果,该DNS服务器就会给上层节点响应回答报文,该回答报文的标志就是1比特 的“递归可用”标志;

问题数、回答数、权威RR数、附加RR数:分别用于记录当前的查询请求的次数、当前查询的回答次数、当前查询获得的权威DNS添加的记录的次数、当前查询获得的附加信息记录的次数。

然后下面再来看看DNS报文主体部分的四个字段:

问题区域:这个区域包含两个字段,分别是名字字段和类型字段,也就是当前正在被查询的主机名和查询记录的类型。

回答区域:这里是每一次回答报文添加DNS记录的区域,如果当前DNS查询经过多个DNS服务器节点,该区域可能会被添加多个DNS记录的数据。

权威区域:这里会被添加DNS权威服务器的DNS记录。

附加区域:包含其他有帮助的记录,比如对于一个MX类型的请求的回答报文,在该回答报文的附加区域会添加上类型A的DNS记录,而回答区域添加的是MX类型的DNS记录。

4.5DNS数据库

当 我们注册一个域名时实际上就是给自己的主机名注册一个别名,而一般域名提供方就是(TLD)顶级域DNS服务提供方,在前面的DNS服务器层次结构中介绍 过,顶级域DNS服务器下面是权威DNS服务器,所以我们需要向域名注册机构提供权基本威DNS服务器和辅助权威DNS服务器的名字和IP地址,该域名注 册等级机构会基于域名和主机名生成NS记录和基于主机名和IP地址生成A记录,并将这两个基本和辅助权威DNS服务器的四条记录添加到顶级域DNS服务器 中。也就是说DNS权威服务器就是说域名解析的主机就是这两台DNS权威服务器,然后再在权威DNS服务器中添加解析你的具体英特网应用服务器的记录,这 时候着你的英特网应用服务就可以被英特网上的其他主机通过域名访问到了。

每台DNS服务中的内容可以是静态手动配置,也可以通过DNS协议中的UPDATE选项实现动态更新(删除和添加),RFC 2136和RFC 3007定义了DNS动态更新,如果有需要可以参考。

关于这一步要想深入的理解,还需要手动操作一些DNS服务器的配置就会更清晰的理解,后续在服务器架设相关博客中我会专门解析。

 五、P2P文件分发

5.1P2P架构

前面描述的应用层内容都是客户——服务器的c/s体系结构,这种结构极大的依赖总是打开的服务器,使用P2P体系结构对总是打开的服务器依赖最小或没有依赖。与之相反成对的主机(对等方)彼此直接通信,这些对等方并不为服务提供商所有,而是受用户控制的桌面和膝上计算机。

P2P架构的特征:没有(或极少)一直运行的服务器、任务端系统都可以直接通信、利用peer的服务能力、Peer节点间歇上网,每次IP地址都有可能变化。

P2P架构的应用场景:文件分发(BitTorrent)、流媒体(KanKan)、VoIP(Skype)。

5.2C/S架构与P2P架构的比较(定量研究)

假设现在考虑用于两种体系结构类型的简单定量模型,即将一个文件分发给一个固定对等方集合,服务器和对等方使用接入链路与英特网相连,然后比较这N个对等方(peer)得到这个文件副本所需要的时间,即分发时间(distribution time)。

us:表示服务器接入链路的上载速率;

ui:表示第i对等方接入链路的上载速率;

di:表示第i对等方接入链路的下载速率;

F:表示被分发的文件长度(以比特计);

N:表示N个对等方;

Dcs:表示C/S结构的分发时间;

Dp2p:表示P2P结构的分发时间;

文件分发问题示图

客户——服务器体系结构中的文件分发时间,即DCS

服务器必须向N个对等方的每个传输该文件的副本,因此服务器必须传输NF比特。因为该服务器的上载速度是us,则分发该文件的时间是至少为NF/us

令dmin表示最小下载速率,即dmin=min{d1, d2, ..., dN},那么具有最小下载速率的对等方不可能少于F/dmin秒时间内获得该文件的所有F比特。因此最小分发时间至少为F/dmin

通过上面分析就可以得出C/S结构体系中文件分发时间的最小值:Dcs≥{NF/us,F/dmin},如果需要表达实际发送时间,就可以取最小分发时间的下界作为实际发送时间,即Dcs={NF/us,F/dmin}。

P2P体系结构中的文件分发时间,即Dp2p

在P2P结构中分发开始时,为了使对等方得到该文件,该服务器必须经其接入链路至少发送该文件每个比特一次。因此最小分发时间是F/us

在P2P结构中各个对等方的最低下载速率和文件大小决定了其最小分发时间,即F/dmin

最后,各个对等方在相互传输文件数据时,一个对等方获取文件的分发时间还取决各个为其提供文件数据的上载速度总和,并且此时服务器也会当作一个数据提供方的对等方,所以文件的上载速度可以表示为utotal=us+u1+ ... +uN,那么基于上载速度决定的分发最小时间为:

那么经过上面的分析可以得出P2P结构体系中文件分发时间的最小值,以及可取最小分发时间的下界作为实际分发时间为:

由于在C/S体系结构的文件分发都需要依赖服务器,当随着对等方数量的增长,C/S体系结构的文件分发会因为对等方数量N的增长而增长。而P2P体系结构由于对等方可以作为数据的提供方,所以随着对等方的增长文件的分发时间会出现增长放缓。大概可以用下图来描述它们的分发时间随着对等方N的增长而变化:

5.3P2P分发协议:BitTorrent

BitTorrent是一种用于文件分发的流行P2P协议[Chao 2011]。用于BitTorrent的术语来说,参与一个特定文件分发的所有对等方的集合被称为一个洪流(torrent)。

洪流:对等方彼此下载等长的文件块(chunk),彼此服务,典型的块长度为256KB。

对等方加入洪流的的机制

缓存块的子集及提供文件分发服务:当一个对等方加入一个洪流时,它没有块,随着时间的推移它会缓存越来越多的块,这些块就会形成当前对等方的一个子集,当它的拥有一个子集时就可以向其他对等方提供这些子集的分发服务。

追踪器(tracher):每个洪流具有一个基础设施节点,称为追踪器。当一个对等方加入某个洪流时,它向追踪器注册自己,并周期性的通知追踪器它仍在洪流中。也就是说一个对等方可以主动选择留在这个洪流中还是离开这个洪流,当它在周期时间内没有通知追踪器,追踪器就会从洪流中排除,并且任何时候这个对等方都可以重新加入这个洪流。

 BitToorent的请求与发送实现文件共享的机制

获取对等方子集:就是指当一个对等方加入一个洪流时,追踪器会在对等方集合中随机选择一部分对等方作为一个子集,然后将这些对等方子集的IP地址发送给这个刚刚加入的对等方。

请求块以及临近方:假设刚刚加入的对等方叫做Alice,Alice在接收到这些对等方子集的IP以后,他会基于这些IP地址通过TCP连接向这些对等方请求他希望的块。与Alice成功建立TCP连接的对等方被称为临近方,当然在这个过程中对等方和临近方都会随时退出,也会有新的对等方和临近方加入。Alice会周期性的询问每个临近方所具有的块列表,也就是说这时候Alice具有子集块的子集并且还知道他的临近方块的子集。

最稀缺优先原则:Alice在请求块时,会根据临近方块的子集找出被缓存的最少的块,并且会优先请求这些块,也就是最稀缺优先原则。

发送块:当Alice拥有一定数量的块以后,他就可以向其他对等方发送块了,Alice会选择众多的对等方中能够以最高速率向他提供数据的邻居,其中的速率最快的4个作为他们的邻居向他们发送数据。

疏通:Alice会每隔10重新计算上传速率并修改四个对等方的集合,即生成新的临近方。

一报还一报:即tit-for-tat,例如Alice除了会给四个最快速率的临近方分发数据,还会每隔30秒随机选一位新的对换伴侣,如果双方的都满足此对换(拥有对方没有数据块,上传速率靠前),他们就会彼此交换数据块。

关于BitTorrent的基本原理就介绍到这里,除了上面介绍的一些机制以外,BitTorrent中比较重要的机制还包括:残局模型和反怠慢[Cohen 2003]。P2P在具体实现上还可以分为结构化模式和非结构化模式,结构化模式的P2P也被称为分布式散列表(DHT),这些在以后具体的P2P协议解析的博客中再详细的介绍。

 六、视频流和网络内容分发网:CDN

视频流量占据互联网大部分的带宽,且用户数量非常庞大,如何同时向大量的用户同时提供视频相关服务。而且每个网络节点的带宽不同如何保证视频传输的流畅性,不同的客户设备还需要为这些异构的客户端提供不同分辨率的视频资源,解决这些问题的方案就可以采用分布式的内容分发服务来实现,即内容分发网(Content Distribution Network,CDN)。

当然除了CDN以外基于HTTP协议的HTTP流也可以提供视频流的服务,也被称为经HTTP的动态适应性流(Dynamic Adaptive Streaming over HTTP,DASH)。

DASH针对不同客户不同的时间带宽大小的不同,提供了不同版本的视频编码,每个版本具有不同的比特率,对应不同质量水平的视频流。客户动态的请求来自不同版本且长度为几秒的视频段数据块,当带宽较高时客户自然的选择来自高速率版本的块,当可用带宽较低时客户自然的选择来自较低的速率版本。

客户使用HTTP GET请求报文一次选择一个不同的块,每个版本都有一个不同的URL,HTTP服务也有一个告示文件(manifest file),为每个版本提供一个URL及其比特率。客户首先请求告示文件得知各种各样的版本,然后通过HTTP GET请求报文中对每块指定一个URL和一个字节范围。客户端在接收块的同时,也测量接收带宽并运行一个速率决定算法来选择请求的块,因此DASH允许客户自由地在不同的质量等级之间切换。

6.1内容分发网:CDN

CDN从服务提供方来说可以分为专用CDN(即private CDN)和第三方CDN(即third-party CDN),专用CDN一般是视频业务量非常大的应用厂商为自己的视频业务搭建(比如TouTube),而第三方一般是专业的内容分发提供商搭建(例如Akamai)。

CDN的安置原则:深入、邀请做客,下面来详细的了解这两种安置原则。

深入原则:通过遍布全球的接入ISP中部署服务器集群来深入到ISP的接入网中,其目标是靠近客户,通过减少用户和CDN集群之间链路和路由器的数量,从而改善用户感受的时延和吞吐量。

邀请做客原则:通过在少量关键位置建造大集群来邀请到ISP做客,而不是将集群放在接入ISP中,这些CDN通常将它们的集群放置在因特网交换点(IXP)。与深入原则对比,邀请做客通常产生较低的维护和管理开销,可能对端用户的产生较高时延和较低吞吐量为代价。

CND集群对资源的拉策略:不是每个集群都包含所有视频资源,因为有些视频很少观看或有些地区很少观看,许多CDN就不会将这类视频推入到它们的集群,而是使用一种简单的拉策略,即当一个用户向一个未存放请求视频的集群发出资源请求时,该集群就会从中心仓库或另一个集群检索该视频,从其他集群获取到视频流在转发给用户的同时存储一个副本到当前集群上。

6.2CDN的操作细节

当用主机中一个浏览器指令检索一个特定的视频(URL标识)时,CDN必须截获该请求,以便能够:①找到适合于该用户的CDN服务器集群;②将客户的请求重定向到该集群的某台服务器。大多数CDN都是利用CDN来截获和重定向请求。

假设一个内容提供商NetCinema,雇佣第三方CDN公司KingCDN来向其客户分发视频,每个视频都被指派了一个URL,该URL包括了字符串“video”以及该视频本身的独特标识符,例如一个视频的RUL是:http://video.netcinema.com/6Y7B23V,下面就是这个视频请求的CDN操作过程示图以及解析:

示例的CDN操作过程示图以及解析:

1.用户访问位于NetCinema的Web网页;

2.当用户点击连接http://video.netcinema.com/6Y7B23V时,该用户主机发送了一个对于video.netcinema.com的DNS请求;

3.本户DNS服务器将该请求中继到NetCinema的权威DNS服务器,该服务器观察到主机名中的“video”,将该请求移交给KingCDN。NetCinema权威DNS服务器并不返回一个IP地址,而是向本地DNS服务器返回KingCDN域的主机名,如:a1105.kingcdn.com;

4.从这时起,DNS请求被本地DNS服务器发送给KingCDN的专用DNS基础设施,KingCDN向本地DNS服务器返回KingCDN内容服务器的IP地址;

5.本地DNS服务器向用户主机转发CDN节点的IP地址;

6.用户主机接收到KingCDN内服务器的CDN节点IP地址后,于其建立TCP连接,并发出对该视频的HTTP GET请求。

 6.3集群选择策略

地理上最为邻近的集群策略:这个地理邻近是指基于用户请求的本地DNS服务器的地理位置,本地DNS服务器的IP地址都会映射一个地理位置,这种解决方案对大部分用户来说都是非常好的,但某些客户会因为网络路径的长度或跳数,即使地理位置邻近但并不是最近的集群。

基于当前流量条件为客户决定最好的集群策略:CDN能够对集群和客户之间的时延和丢包性能执行周期性的时实测量(real-time measurement),但这种方法的缺点是许多本地DNS服务器被配置为不响应这些探测。

关于CDN的内容就介绍到这里,这里只是非常简单的介绍一些原理,后面还会有相抵的原理解析甚至实际应用解析的博客。

 七、套接字编程

在前面的内容中提到过,网络应用是由一对程序组成,即客户程序和服务器程序,它们位于不同的端系统中。当运行这两个程序时,创建了一个客户进程和一个服务器进程,同时它们从套接字读出和写入数据在彼此之间进行通信。

网络应用程序有两类:一类是由协议标准定义的操作实现,另一类是专用的网络应用程序。简单的来说由协议标准定义的操作就是基于应用层协议实现的统一默认程序,程序根据从套接字读取的报文内容实现默认的响应,比如根据请求方法和资源路径对相对应的文件做出默认操作,并且根据报文内容采取默认的方式将响应报文写入套接字。而对于专用的网络应用程序来说,我们需要从套接字读取出报文内容后,根据自己的需求编写程序对报文做相应的解析,然后执行我们自定义的程序,并向套接字写入我们自定义的报文数据,简单的来说专用的网络应用程序就是在套接字的接口上实现专用的程序逻辑。

端口号(port number):套接字相当于是应用层和运输层的一扇门,应用程序相当于房间内的一侧,运输层相当于房间外的一侧,应用程序通过套接字应控制应用层一侧所有东西,运输层的一侧由相关协议实现掌控。用UDP和TCP的套接字来说,在运输层它们通过目标地址来传输分组,这个目标地址中除了需要包含IP地址,还会携带目标主机上对应应用程序的端口号的标识符,这个端口号实际上就是应用程序的进程标识符,即进程ID。因为在一个主机上可能会有多个网络应用的进程,当运输层接收到分组并解析成应用层的报文数据时,运输层会基于端口号将报文数据交给相对应的应用程序进程的套接字。

套接字实际上就是网络应用程序与运输层交换数据的接口,也可以说是交换应用层协议的相关交互的命令接口,服务端应用进程基于套接字将自己的操作传递给客户端的应用进程,当然其中也就会包含相关操作必要的数据,从这个方面来说套接字相当于是客户进程和服务进程实现进程通信的接口。

套接字编程实现的基本逻辑

1.启动一个服务进程监听对象;

2.当客户端请求到达后生成一个基于运输层的套接字;

3.基于套接字获得应用层请求报文内容;

4.根据应用层请求报文执行相关程序

7.1TCP套接字编程

关于TCP的套接字编程需要了解的更多细节是,TCP是一个有连接的运输层协议,关于什么是有连接的这在下一篇博客:计算机网络运输层中会详细介绍,这里暂时只需要知道当一个基于TCP的应用程序客户端与服务端建立连接成功以后,它们会生成一个套接字,并且这个套接字会暂时的维护双方的通信连接,连接建立成功以后客户端才会基于套接字向服务端传输应用层的请求报文数据,并且客户端会等待服务端做完相关处理以后的相应结果,这个相应结果同样是基于套接字从服务端写入,客户端的套接字读取相应响应报文数据获取数据。

服务端在响应数据以后还要向客户端发送一个关闭连接的报文,客户端收到完整的数据以后同样也会给服务端发送一个关闭连接的报文,以实现将刚刚建立的通信连接断开方便端系统回收内存资源。需要注意的是无论是服务器还是客户端发送的关闭连接报文后都会收到双方的确认响应报文,一般套接字编程不需要去处理这个关闭连接的响应,但实际上套接字底层依然会处理这个响应报文,一般套接字组件上都会有一个关闭连接的事件end,这个事件就是在套接字接收到关闭连接的响应后触发。

下面使用nodejs的套接字接口作为描述套接字编程的示例做一个逻辑描述(不是完整程序代码):

服务端的示例代码

const HOST = 'localhost'    //主机名
const PORT = '12345'    //端口号
const server = net.createServer()    //实例化一个服务对象
server.on('connection', (socker) =>{    //服务通过connection事件监听来自客户端的连接请求,一旦连接建立成功就会实例化一个套接字对象,并作为事件回调的实参
    socker.on('data',(chunk) => {    //套接字通过客户端送达的数据报文触发data事件
        //这里省略接收到数据后的业务逻辑
        socker.write(....... ,()=>{     //执行完相关处理程序以后给客户端响应数据
            socker.end()    //数据发送完以后关闭连接,由于后面一般不会有实际的业务任务,关闭连接的响应事件就不注册了
        });   
    })
})
server.listen(PORT,HOST)    //基于主机名和端口号为服务对象实例启动一个监听进程,这时候服务就可以收到客户端的请求消息了

客户端的示例

const HOST = 'localhost'    //服务器主机名称
const PORT = 12345    //服务端口号
const client = net.createConnection({port:PORT,host:HOST},()=>{    //通过基于服务主机名和端口号,创建一个TCP连接,连接建立成功后会调用这里的回调任务
    client.write(......)    //连接成功以后基于套接字像服务发送请求数据的报文的数据(报文实体)
})
client.on('data',(data)=>{
    //这里通过套接字接收服务响应回来的数据处理相关业务功能...
    client.end()    //数据接收完以后关闭连接
})
client.on('end',()=>{
    //当接收到服务端对关闭连接的响应报文以后会触发这个事件,表示连接会被正常关闭
})

以上就是基于TCP的套接字编程演示,基本逻辑代码都完整的实现了,在不同的编程语言中接口和代码的语法结构会有一些差异,但套接字编程本身只关注TCP和上层应用的数据传送,也就是应用层报文数据的读写,这个环节与编程语言无关。

7.2UDP套接字编程

关于UDP的套接字编程相比TCP从逻辑结构上会要显得简单很多,因为UDP不需要建立连接,客户端直接使用套接字向服务端发送携带有数据的报文,服务端也同样是直接使用套接字发送携带有数据的报文,更没有什么关闭连接通信了。UDP这看起来很简单的方式,实际上用简陋来描述更合理,因为这种简单是建立在不可靠传输的基础上,意思就是UDP的运输层不保证传输的数据是完整的,甚至不保证数据能否送达。

虽然这种不可靠传输看起来有点不靠谱,但其没有TCP的拥塞、重传、回退这些机制,让UDP的传输速率和带宽使用效率具有非常非常大的优势,在某些不考虑数据的完整性的情况下,UDP相比TCP就是更好的选择,这些内容的详细原理和机制是在下一篇博客:计算机网络运输层中介绍的,这里只是简单的做些介绍,因为这样的特性最终会在套接字的编程上体现出来。

下面使用nodejs的套接字接口作为描述套接字编程的示例做一个逻辑描述(不是完整程序代码):

服务端代码

const server = dgram.createSocket('udp4');    //实例化一个UDP服务对象,这个实例化对象实际上就是一个套接字实例
server.on('message',(msg, rinfo) => {    //当套接字接收到自客户端的消息消息后,触发message事件,并将消息数据作为msg的实参,消息来源标识符IP、端口号的键值对作为rinfo的实参
    console.log(`服务器收到:${msg}来自${rinfo.address}:${rinfo.port}`)
client.send(message, rinfo.port, rinfo.address);  //将接收到的数据回传给客户 }) server.bind(
41234,'127.0.0.1')  //基于端口号、主机地址启动一个UDP服务监听进程

客户端代码

const client = dgram.createSocket('udp4',(mas,rinfo)=>{    //实例化一个UDP的用户套接字对象,并且通过这个回调任务来接收当前UDP端口到来的消息
    console.log(msg.toString()); //将到来的消息内容打印出来
})
const message = Buffer.from('Some bytes');  //需要传输的数据
client.send(message, 41234, '127.0.0.1',(err)=>{  //通过指定的端口和IP地址将数据发送到服务
    client.close();
})
client.bind(1234);  //给用套接字对象绑定一个监听消息的进程

以上就是套接字编程,实际上套接字编程就是客户端和服务端之间,一方单纯的发送数据报文,另一方单纯的接收数据报文,如果需要接收数据报给套节字一个固定监听端口来实现,在套接字的一侧实例上既可以通过监听端口和message事件实现接收收据,也可以通过send实现消息发送。根TCP一样,在不同的编程语言实现的套接字程序组件中代码的语法结构会有一些差异,但其套接字的功能实现基本是一致的。

 

posted @ 2022-09-30 21:10  他乡踏雪  阅读(611)  评论(0编辑  收藏  举报