自上世纪80年代末至90年代初互联网诞生以来,Web服务可以说是在互联网的普及过程当中起到了巨大的作用。而Web服务应该是当今世界上普通用户访问互联网的最广泛的方式了,用户只需在浏览器中输入所谓网址的方式即可浏览互联网上的海量信息,而浏览器这种瘦客户端的交互方式也是目前最主流的交互方式。
Web服务的基础是http协议,基于http这个应用层协议才能实现客户端和服务器端之间的数据交互。客户端通过http协议向服务器端发送请求,服务器端接收到这个请求之后,服务器端基于http协议也能够理解客户端发送的请求的含义,同时返回客户端所请求的内容至客户端,而客户端接收到服务器端发送过来的响应之后就能够解析相应内容并通过特定的格式展示给用户。我们可以看到在此过程中,http协议作为双方能够实现通信的这样一种标准而言至关重要。那在介绍http协议的相关知识之前我们在复习一些关于计算机网络的基础知识。
一、网络协议基础简述
1、传输层协议:提供进程地址
我们知道,协议是分层的,正如目前互联网事实上的标准TCP/IP协议族就分为了四层,自下而上依次是:物理层、网际接口层、传输层、应用层,每一层所完成的功能不尽相同,这样做的好处是可以将一个大而复杂的问题拆分为许多小而相对独立的逻辑单元,上层可以很方便的调用其下一层次所提供的服务用以完成自身特定的功能;同时当每一层次的具体实现细节改变时,调用其接口的上层服务也无需做人也修改,不至于影响全局。那TCP/IP这四层协议当中的传输层协议作用之一就是用来提供在主机上运行的各进程需要实现与外部主机通信时需要监听的地址端口。
传输层协议主要有TCP协议和UDP协议两大类。TCP(Transfer Control Protocol)协议是面向连接的协议,即通信双方在通信之前需要建立专用于通信的虚拟链路,在通信结束后需要将链路拆除,此种方式可保证通信的可靠性,但效率会受到一定程度的影响。UDP(User Datagram Protocol)协议则是无连接的协议,即通信双方在通信之前无需建立虚拟链路,此种方式不能保证通信的可靠性,但效率比TCP协议要高。
TCP和UDP是两个相互独立的协议,它们各有一套端口号用来提供给各进程使用,范围为0-65535(其中源端口16位,目标端口16位)。根据端口使用对象划分,可分为如下几类:
- 0-1023:众所周知的端口,为特权端口,只有管理员才有权限操作。为了行业标准的统一及用户使用的便捷性,IANA定义了将此类端口永久地分配给一批特定的应用程序使用,如22/tcp分配给了ssh服务,80/tcp分配给了http服务,443分配给了https服务;
- 1024-41951:为注册端口,但要求不是特别严格,当某程序需要注册为服务监听某端口以实现与外部主机通信时使用。如:11211/tcp(memcached),11211/udp(memcached),3306/tcp(mysql);
- 41952+:为客户端程序随机使用的端口,被称为动态端口,或者私有端口,其范围定义于文件/proc/sys/net/ipv4/ip_local_port_range中;
2、Socket
Socket是IPC(进程间通信)的一种实现机制,它能够实现不同主机甚至是同一主机上的不同进程之间进行通信.程序员可通过Socket API这样一种C标准库调用接口进行Socket编程.第一个真正被大众广泛接收的Socket API大约是在1983年前后发布;这个标准早期是出现在4.2版本的BSD系统上的,后来被广泛移植到了各种各样的Unix发行版上,甚至包括后来出现的Linux系统对其也都有支持.
Socket的格式诸如:IP:PORT每一个IP:PORT,我们称其为一个套接字.因此根据使用协议端口的不同,可分为三类不同的套接字:
- SOCK_STREAM:TCP套接字,使用的是TCP端口;
- SOCK_DGRAM:UDP套接字,使用的是UDP端口;
- SOCK_RAM:裸套接字,既不是使用TCP端口,也不是使用UDP端口,而是直接通过ip报文封装以后
此外,根据所使用的地址的 不同,又出现了Socket Domain这样的概念,即根据Socket使用的IP地址的不同,可作如下分类:
- AF_INET:Address Family,IPv4(IPv4的地址家族)
- AF_INET6:IPv6的地址家族
- AF_UNIX:在同一主机上的不同进程之间进行通信时使用;(即本机既作为客户端,有作为服务器端,本机基于本机进行通信;需要注意的是此种方式绕过了TCP/IP协议栈,直接通过内核完成通信;)
结合传输层协议来看,在上述三种分类中,每一类套接字都至少提供了两种Socket的数据传输机制:流(基于TCP),数据报(基于UDP);对比TCP与UDP的特点,我们即可以知道流与数据报的区别:
流:提供可靠的面向连接数据传输,多个报文发送之间无边界;
数据报:提供不可靠的无连接数据传输,多个报文发送之间有边界(因为是无序发送,需要添加帧头和帧尾用以标识一帧的开始和结束);
套接字相关的系统调用
- socket():创建一个套接字;
- bind():将进程与套接字绑定;
- listen():监听;
- accept():接收请求;
- connect():请求连接建立;
- write():发送数据;
- read():接收数据;
注:
- write(),read()的作用同send(),recv(),sendto(),revcfrom();
- 默认情况下,这些调用在IO无法完成时将会被阻塞;
3、IPv4的分类
按照IPv4的第一个字节所处的范围,大致可分为A,B,C,D,E五类:
A:1-127;
B:128-191;
C:192-223;
D:组播地址,224-239;
E:240-254
为满足建立不与外部网络通信的局域网的需求,IPv4在A,B,C三类中又各预留了一段私有地址供使用,它们分别是:
A:10.0.0.0/8
B:172.16.0.0/16-172.31.0.0/16
C:192.168.0.0/24-192.168.255.0/24
4、TCP协议的特性
- 建立连接:三次握手;
- 将数据打包成段:每一段都包含一个校验和(CRC-32);
- 确认、重传以及超时;
- 排序:逻辑序号;
- 流量控制:滑动窗口算法(可以理解成当接收方每一次确认时窗口有多大都会通知给发送方,说白了就是这里能够实现发送方得知接收报文的空闲空间有多大,以实现流量控制);
如果说A、B主机之间进行通信时,A的发送速度可能大于B的接收速度,为了避免这中情况可能产生的问题,我们应该做流量控制;
滑动窗口算法允许包含总共假如说n个字节或者n个窗口大小的未确认段同时在发送者和接收者之间可以完成传输的,如果接收者的缓冲间完全被塞满了,那么窗口就会被关闭,告诉发送方窗口为0即停止发送;
- 拥塞控制:慢启动和拥塞避免算法
(TCP的拥塞控制算法主要用来防止快速的发送者压垮整个网络的;因此它使用慢启动的方式)
二、http协议基础
http全称为hyper text transfer protocol,即所谓的超文本传输协议。见名知意,从其名称上我们可以得知,http协议是用于实现传输超文本的协议,那什么是超文本呢?
- 超文本
我们可以简单将超文本理解为带超链接的文本,而超链接是超文本当中一种特殊的语法格式,它用于实现超文本之间的跳转。通过点击超链接,我们可以实现从一个超文本跳转到另一个超文本,甚至于实现在同一个超文本之内的不同内容之间进行跳转。比如我们通常在浏览器中访问的一个网页就是一个超文本,点击超链接,它又会跳转到另一个网页中去。
超文本是通过html编写的,html(hyper text mark language,超文本标记语言)是一种编程语言,它是由很多既定的标签组成,每一种标签的用法和属性可能不尽相同。我们在浏览器中所看到的一个个生动形象的网页就是通过html以及借助于CSS(Cascading Style Sheet,层叠样式表)与JS(JavaScript,一种脚本语言)所实现的。
因此http协议就是实现超文本传输的协议,那么http为什么能够实现超文本的传输呢?http协议传输纯文本的数据流之外,为什么还能够支持对如图片等二进制数据的传输呢?http的工作机制到底是什么?那么接下来,我们就来介绍http协议的相关内容。
1、http协议版本
http协议诞生于20世纪80年代的欧洲核物理实验室,诞生的初衷主要是为了能够实现在多个部门之间可以共享文档和快速实现文档定位。http协议在发展过程中进行了多次版本迭代,其所实现的功能随着版本更新也愈加丰富。
- http/0.9
在当时http/0.9是http协议应用最广泛的版本,那个时候的Web服务仅支持纯文本(即由纯ASCII字符组成的文本),这种纯文本还包含有超链接,这种超链接也是表现为纯文本形式的,但这种文本比较独特,因此称为超文本。
- http/1.0
通过参照传统的SMTP协议,在http/1.0中引入了MIME机制,从此之后,http协议也可以传输诸如图片、音频、视频等非文本数据了;此外,http/1.0还引入了缓存机制。
- http/1.1
http是基于TCP的应用层协议,而每一次TCP连接的建立都需要3次握手,在数据交互完成之后,有需要4次断开。因此为了避免由于每一次访问资源时的3次握手和4次断开而造成的资源浪费,推出了http/1.1。
首先,在http/1.1中增强了缓存功能;
其次,引入了长连接机制,即每一次连接在获取到资源之后并不会立即断开,而是继续获取下一个资源。
注意:建议阅读http/1.0和http/1.1的RFC文档。
2、http报文
http报文分为请求报文和响应报文,其格式不尽相同;
2.1 http请求报文
请求报文语法:
<method> <request-URL> <version>
<headers>
<entity-body>
<method>:请求方法,标明客户端希望服务器对资源的执行动作,如GET、PUT、POST等;
常用method:
-
- GET:从服务器获取一个资源;
- HEAD:只从服务器获取文档的响应首部;
- POST:向服务器发送要处理的数据;
- PUT:将请求的主体部分存储在服务器上,简而言之,PUT为向服务器上传数据;
- DELETE:请求删除服务器上指定的文档;
- TRACE:追踪请求报文到达服务器中间经过的代理服务器;
- OPTIONS:请求服务器返回对指定资源支持使用的请求方法;
<request-URL>:请求的资源路径;
<version>:对应的请求资源协议的版本号;
<headers>:http协议请求报文首部,每个请求报文可包含任意个首部,每个首部都有首部名称,后跟一个冒号,而后跟上一个可选空格,接着是一个值,其格式都形如key:value,需要注意的是,首部的最后面一行必须是空白行;
<entity-body>:报文的主体内容;
2.2 响应报文
响应报文语法:
<version> <status> <reason-phrase>
<headers>
<entity-body>
<version>:协议版本;
<status>:状态码,用以标识请求的状态结果(正确与否),大约可分为5类:
-
- 1xx,100-101:纯信息,与请求的资源没有太大关系;
- 2xx,200-206:“成功”类的状态码;
- 3xx,300-305:重定向类的信息,即请求的资源存在,但资源已被挪到其它地方了,如:给服务器端给出的响应为一个参考答案,表示这个资源已经挪走了,请重新发起请求来请求另外一个地址,并且把另外的参考地址也响应给客户端了;(常用的状态码:301(永久重定向,永久的挪到哪个位置去了)、302(临时重定向,繁忙时暂时重定向到指定位置寻找)、304(表示请求的内容未发生任何改变,若缓存过直接使用缓存即可)。重定向的信息未必都是重定向,如304状态码,)
- 4xx,400-415:客户端错误类的信息;(常用的状态码:404(请求了一个不存在的文件))
- 5xx,500-505:服务器端错误类的信息;
常用状态码:
-
- 200:成功响应,请求的所有数据通过响应报文的entity-body部分发送;OK;
- 301:请求的URL指向的资源已经被删除;但在响应报文中通过首部Location指明了资源现在所处的新位置(永久重定向,即资源将被永久的挪到所处的新位置);Moved permanently;
- 302:与301相似,但在响应报文中通过Location指明资源现在所处临时新位置(临时重定向,即繁忙时暂时重定向到指定位置寻找);Found;
- 304:客户端发出了条件式请求,若服务器上的资源未曾发生改变,则通过响应此响应状态码通知客户端(表示请求的内容未发生任何改变,若缓存过直接使用缓存即可);Not Modified;
- 401:需要输入账号和密码认证方能访问资源;Unauthorized;
- 403:请求被禁止;Forbidden;
- 404:服务器无法找到客户端请求的资源;Not Found;
- 500:服务器内部错误;Internal Server Error
- 502:代理服务器从后端服务器收到了一条伪响应;Bad Gateway;
<reason-phrase>:对<status>的进一步补充说明;
<headers>:响应报文首部,每个响应报文可包含任意个首部;每个首部都有首部名称,后跟一个冒号,而后跟上一个可选空格,接着是一个值,同样的,响应报文首部的最后一行的下一行必须是空白行;
<entity-body>:响应报文主体
2.3 举例
请求报文:
GET / HTTP/1.1
Host: www.test.com
Connection: keep-alive
响应报文:
HTTP/1.1 200 OK
X-Powered-By: PHP/5.2.17
Vary: Accept-Encoding,Cookie,User-Agent
Cache-Control: max-age=3, must-revalidate
Content-Encodiing: gzip
Content-Length:6931
注:GET / HTTP/1.1中的/表示此时访问一个网站但没有明确指定访问哪个页面,/表示访问对方的默认页面(主页)。
3、http headers(首部)
不管是http请求报文和http响应报文中都包含有首部,其格式为Name: Value,根据各首部的功能用途大约可分为以下几类:
- 通用首部
- 请求首部
- 响应首部
- 实体首部
- 扩展首部
3.1 通用首部
通用首部即既可以在请求首部又可以在响应首部中的首部;
- Date:报文的创建时间;
- Connection:连接状态;如,keep-alive,close;
- Via:显示报文经过的中间节点;
- Cache-Control:控制缓存的生效机制;
- Pragma:跟缓存相关的首部;
3.2 请求首部
- Accept:通过向服务器定义自己可接受的媒体类型(MIME类型);
- Accept-Charset:可接收的字符集;
- Accept-Encoding:客户端请求一个服务器端资源如果是文本文件为了节约带宽有可能将资源压缩以后传递给客户端,客户端解压以后才能在浏览器中显示,如果客户端浏览器不支持压缩功能的话,那么就无法查看,此项用于定义可接受的编码格式,如gzip;
- Accept-Language:定义可接受的语言,即表示通过服务器只能发送哪些服务器编码的页面给客户端本身;
- Client-IP:客户端IP;
- Host:请求的服务器名称和端口号;
- Referer:包含当前正在请求的资源的上一级资源;
- User-Agent:客户端代理,说白了就是浏览器类型;
3.2.1 条件式请求首部
- Expect:期望发送的信息;
- If-Modified-Since:表示自从在指定的时间之后,请求的资源是否发生修改;
- If-Unmodified-Since:表示自从在指定的时间之后,请求的资源是否未发生修改;
- If-None-Match:表示本地缓存中存储的文档的ETag标签是否与服务器文档的Etag不匹配;
- If-Match:表示本地缓存中存储的文档的ETag标签是否与服务器文档的Etag匹配;
3.2.2 安全请求首部
- Authorization:用于向服务器发送认证信息,如账号和密码;
- Cookie:用于客户端向服务器发送Cookie;
- Cookie2:Cookie有两个版本,此为Version 2;
3.2.3 代理请求首部
若某一个请求中途经由代理服务器请求的话,它还有一个代理请求首部;
- Proxy-Authorization:向代理服务器认证;
3.3 响应请求首部
3.3.1 信息性首部
- Age:响应持续时长,即资源的有效期限;
- Server:服务器程序软件名称和版本;
3.3.2 协商首部
某资源有多种表示方法时使用,比如用户请求的页面有中文版、英文版、阿拉伯语版,因此阿拉伯人访问的时候,其浏览器跟服务器协商版本;
- Accept-Ranges:服务器可接受的请求范围类型;
- Vary:服务器查看的其它首部列表,例如如果有些首部无法正常显示,其值可能会有变化时,就放到Vary中;
3.3.3 安全响应首部
- Set-Cookie:向客户端设置Cookie,说白了就是向客户端发送一个此客户端的唯一标识;
- Set-Cookie2:向客户端设置Cookie2信息;
- WWW-Authenticate:来自服务器对客户端的质询认证表单;
3.4 实体首部
- Allow:用于列出对此实体可使用的请求方法;
- Location:用于告诉客户端真正的实体位于何处;
- Content-Encoding:主体的编码;
- Content-Language:主体的语言;
- Content-Length:主体的长度;
- Content-Location:实体真正所处位置;
- Content-Range:整个资源中此实体表示的字节范围;
- Content-Type:主体的对象类型,为MIME类型;
3.4.1 缓存相关
- ETag:实体的扩展标签,基于标签做条件式请求时会使用;
- Expires:实体的过期时间;
- Last-Modified:最后一次修改的时间;
4、一次完整的http请求处理过程
一次完整的http请求的处理过程包含从客户端发起请求开始到服务器端发送响应报文完成,这一过程也被称为一次完整的http事务;
(1). 建立并处理连接
处理连接的方式也无非就是:接收请求或拒绝请求,以下我们只考虑接收请求的情况;
(2).接收请求
即接收来自于网络的请求报文中对某资源的一次请求的过程;
(3).处理请求
对请求报文进行解析,并获取请求的资源及请求方法等相关信息;
(4).访问资源
获取报文中指定请求的资源;
web服务器,顾名思义为存放了Web资源的服务器,负责向请求者提供对方请求的资源,或动态运行后生成的资源,这些资源通常放置于某特定路径下,此路径通常被称为DocRoot;
例如:在一台名为www.test.com的Web服务器上,其DocRoot为/var/www/html,在此目录下有一images目录,在image目录下有一文件为1.jpg,即/var/www/html/images/1.jpg;因此,在通过浏览器向Web服务器请求此资源时,可通过访问:http://www.test.com/images/1.jpg实现;
(5).构建响应报文
(6).发送响应报文
(7).记录日志
我们再来深入了解一下这个过程,如下图所示:
首先,我们明确一个概念,我们所谓的Web服务器在运行时本质上也无非就是一个运行于用户空间的进程,因为Web服务器是一个应用程序,而只要是应用程序就应该在用户空间。其次,用户所访问的资源其在服务器端表现形式为一个文件,这个或者这些文件也一定是存放于某持久性存储设备,我们此处就以磁盘为例。
当用户通过浏览器发起一个http请求时,此时浏览器作为Web服务通信时的客户端,如果用户在浏览器输入http://www.test.com/index.html。很显然,目前在互联网中的主机之间通信是通过IP地址来作为唯一标识的,因此,首先需要进行域名解析,将我们访问的主机名通过hosts文件或者DNS服务器转换为IP地址。而浏览器将封装请求报文,并发送至网络中,经过层转发,到达Web服务器。当这个请求到达Web服务器主机时,最先经过的是Web服务器主机的内核空间,因为这个客户端的请求一定是通过网络协议发送过来的,而TCP/IP协议工作于内核当中,因此请求首先到达内核空间。
Web服务默认监听在本机的TCP协议的80端口之上,内核空间通过对TCP/IP协议栈中的各种路由机制进行解码等操作发现访问的是本机80端口的套接字,因此内核会将此请求通过套接字转交给用户空间的Web服务器,因此执行流程就从内核空间转移到了用户空间。
若Web服务器发现用户访问的是一个静态文件(如:index.html),而静态文件是存放于磁盘之上的,此时服务器就需要陷入内核重新转换至内核空间,内核从磁盘上加载对应的文件至内核空间,再返回给用户空间,Web服务器接收到这个文件之后就可以返回给客户端了。因此此时再通过套接字返回到内核空间,经由TCP/IP协议栈最终响应给客户端,至此,http的一次事务完成。
我们此时再回过头来分析一遍,如果我们在浏览器输入www.test.com访问,第一步需要把FQDN解析成IP,因此在请求发出之前需要先通过DNS服务器解析FQDN,而DNS服务器可能会进行递归、迭代,需要消耗一些时间,因为有可能是第一次访问,其结果未缓存到本地。之后客户端输入地址发送请求给服务器,服务器在内部需要有一种机制能够接收用户的请求报文,这种机制就叫监听,服务器端的某用户进程会监听在某个端口上,等待客户端请求。一旦用户请求来了,而内核通过TCP/IP协议栈发现有人监听在这个端口上,于是这个请求就可以交给这个套接字了。但如果本地没有任何一个用户进程监听某个套接字,但如果有用户来访问了,此时内核无法知道有谁能够响应这个请求,此时服务器端会拒绝客户端请求,所以必须到内核中去注册需要使用哪个端口并一直在这个端口上处于等待状态。当有请求到来时,通过TCP/IP协议的首部解码后发现用户访问的是这个端口,于是把这个请求交给这个端口,而进程又一直在监听这个端口,一旦有请求来了,可进行响应。
TCP/IP协议在封装报文时,TCP首部主要是源端口(Source Port)、目标端口(Destination Port),IP首部主要是源IP地址(Source IP)、目标IP地址(Destination IP)。但确并没有说明具体访问哪个文档(资源),因此某一种特定的应用在通过TCP/IP协议往外发送时,TCP/IP最多就是将报文传递到目的地的,到达目的地后还需要具体协议的报文首部。因此http协议也有自己的首部,叫http首部(明确定义了基于哪种协议获取哪个资源的),需要说明获取什么文档,如:GET /2.html,并且还需说明获取哪个主机的资源,因此通常还有一个首部叫:Host:www.magedu.com(加主机是HTTP1.0当中的引入的一种机制,在发起获取资源请求时,不但要指明获取哪个资源,还要再加上获取哪个主机的资源,这个主机一定是主机名称,这是为虚拟主机提供准备的)。说白了这一切就是我们之前提到的http报文。
5、Web服务器处理并发连接请求的架构方式(响应模型)(Web I/O)
对于一个正常的网站而言,在同一时间内甚至是同一时刻一定会存在多个用户访问的情况,那么我们的Web服务器同时接收到这些请求之后如何进行处理呢?这就是我们接下来要讨论的Web服务器对于并发连接请求的处理方式。
- 单线程web服务器(Single-threaded web servers)
此种架构方式中,web服务器一次处理一个请求,结束后读取并处理下一个请求。在某请求处理过程中,其它所有的请求将被忽略,因此,在并发请求较多的场景中将会出现严重的性能问题。
- 多进程/多线程web服务器
此种架构方式中,web服务器生成多个进程或线程并行处理多个用户请求,进程或线程可以按需或事先生成。有的web服务器应用程序为每个用户请求生成一个单独的进程或线程来进行响应,不过,一旦并发请求数量达到成千上万时,多个同时运行的进程或线程将会消耗大量的系统资源。
- I/O多路复用web服务器
为了能够支持更多的并发用户请求,越来越多的web服务器正在采用多种复用的架构——同步监控所有的连接请求的活动状态,当一个连接的状态发生改变时(如数据准备完毕或发生某错误),将为其执行一系列特定操作;在操作完成后,此连接将重新变回暂时的稳定态并返回至打开的连接列表中,直到下一次的状态改变。由于其多路复用的特性,进程或线程不会被空闲的连接所占用,因而可以提供高效的工作模式。
- 多路复用多线程web服务器
将多进程和多路复用的功能结合起来形成的web服务器架构,其避免了让一个进程服务于过多的用户请求,并能充分利用多CPU主机所提供的计算能力。
三、常用软件简介
1、常用的客户端浏览器
- IE
- Firefox
- Chrome
- Opera
- Safari
2、常用的Web服务器应用
- httpd:属于ASF;
- IIS:不仅是纯粹意义上的Web服务器还是应用程序服务器,其能结合.net解析asp,asp.net等动态脚本;
- Nginx
- lighttpd:由德国某公司开发的开源软件;
- thttpd:在嵌入式中用到的;
3、应用程序服务器
所谓的应用程序服务器,它们既能提供Web服务,但并不是纯粹意义上的Web服务器,通常还能够编译执行一些特定脚本文件,这是Web服务器所不具备的功能;
- IIS
- Tomcat:Apache的开源(open source)产品,能够解析JSP;
- Websphere:由IBM研发的Java企业级应用程序,能够解析JSP的商业产品(commodity);
- Weblogic:早期属于Bea,后被Oracle收购(83亿美元),能够解析JSP,商业产品(commodity);
- JBoss:当前属于RedHat,分开源和商业版(商业版需要购买RedHat的服务),是重新封装的Tomcat,在其基础上做了更为丰富包装;
注:www.netcraft.com ,此网站每隔一段时间统计一次全球互联网中Web服务器各产品所占的比例,各位若有兴趣可自行查看。