实验环境:RHEL5.8 32Bit
Web服务及http协议详解
·HTTP(HyperText Transfer Protocol:超文本传输协议)
顾名思义,HTTP协议就是用来传输超文本的协议。
·超文本
超文本指的是带有超级链接的文本。
·超级链接
基于一些链接能够实现在文档之间跳转的文本称为超文本,而这些链接就被称为超级链接。
其实目前来讲,在我们所应用的众多的比较流行的协议当中,Web协议出现的是比较晚的,像ftp、smtp以及TCP/IP这样的协议在互联网诞生不久的时候就已经出现了,而Web协议是在上个世纪90年代初期,80年代末期的时候诞生在欧洲的量子物理实验室,它早期主要用来实现在多个不同的部门之间共享文档并快速实现文档定位的机制,这种机制是在一个文档中给一些文本加上一些背后的链接,而我们通过简单的访问这个文本,就能够定位到和该文本相关的其他文档上去,所以早期的Web协议也就是能够实现文档之间的跳转而已,HTTP协议最后公布于世使得所有人都能够应用这种技术的版本是HTTP协议的0.9版本,而那个时候的Web服务仅支持纯文本的传输,所谓纯文本就是由纯ASCII码组成的文本,但是这种纯文本还包括着超级链接,这种超级链接也表现为纯文本的形式,只不过这种文本比较独特,所以我们将它称为超文本,也就是HTML。
·HTML(HyperText Mark Language:超文本标记语言)
HTML是专门用于开发超文本的语言,这种语言中已经事先规定好将我们的文本通过标签的形式,给文本加上一些独特的标记,而这些文本在被用户客户端浏览器解析的时候能够将这些标记解析为字体格式或者是字体的表现属性,比如Title
,尖括号中的内容就是一组标签,这种标签就是用来定义它们之间的字符的显示属性的,而这组标签就是HTML这种语言内置的实现规定好如何解析字符的标记,而所谓的浏览器(Browser)就是客户端代理的一种,客户端浏览器使得我们通过http协议获得的超文本可以在显示的时候不显示标签,而只显示解析后的内容,早期的http协议也就只能实现这样的功能,但是随着Web服务流行开来之后,全球能够提供Web服务的服务器有很多个,那就意味着这些服务器会给我们提供很多种超文本,甚至两台不同的服务器上面提供的不同的超文本的名字都是一样的,那么客户端怎样才能去识别这样的不用的内容呢?客户端如果仅靠文件名来区分不同的html文档的话会很困难,于是就出现了URI这样的机制,但是URI并不是专门为了Web服务而诞生的,不过URI却是一个很好的能够让客户端去识别互联网上不同html文档的机制。
·URI(Uniform Resource Identifier:统一资源标识符)
URI是用来定义全球范围内,包括互联网范畴在内的所有的可以在全球唯一引用某一个独立的资源的命名方式,去定位一个资源的访问路径的方式,统一指的是格式上的统一,无论是什么样的资源,我们引用它路径的格式都是规范的,即路径格式的统一,URI有一个子对象叫做URL。
·URL(Uniform Resource Locator:统一资源定位符)
URL是URI的一个子对象,一个子集,用于描述互联网资源的统一表示格式,那么我们如何去使用URL来表示互联网上两个虽然同名但是内容不同的两个文件呢?使用URL标识的格式如下:
Protocol://Host/path/to/file
即首先指定获取资源的协议,然后使用冒号和双斜线隔开,接着指定获取资源的主机以及主机上资源的路径,有可能还得执行端口、用户以及用户的密码等等,在某一个路径下不可以有同名的文件出现,所有我们通过URL就可以唯一的标识互联网上的某一个资源。
·Web资源
Web资源就是可以通过URL唯一标记并且能够使得客户端访问的文件,比如说http://www.lkxedu.com/logo.gif中的logo.gif就是一个web资源,但是我们肯定不可能去单独的访问一个图片,在我们访问这个资源的时候,它有可能被展示成了主页的组成部分,所以说在互联网上每一个可以被单独访问的文件都是一个web资源,但是这些所有的文件可能被整个成了一个html文档,因此当我们打开一个web页面的时候,它很有可能包含了多个资源,web资源还有另外一个名称叫做web对象,它俩表示的是同一个概念,html文档还可以将分散在多个web服务器上的资源整合成为一个web页面并且在客户端浏览器中予以显示,这种整合web资源的语言就被称为html语言,一个html文档中的web对象很可能是来自多台服务器的,可能是交叉引用实现的,也可能是跨主机引用其它主机上的资源实现的,但是却可以使得不同的资源在同一个web页面中予以展示。既然一个html文档是由资源组成的,那么我们在使用浏览器访问web服务器的时候该如何获取服务器上面的资源呢?现在我们访问web服务器的时候,不但能够将服务器上的资源获取到本地,还能够向服务器提交资源,比如说我们在网站上面注册一个博客的时候,输入的信息都会被提交到web服务器上面,所以说同样是资源的传输,有些是从服务器端到客户端传输的,而有些是从客户端到服务器传输的,因此同样都是资源的访问,但是访问的手段却是不一样的,因此我们将通过http协议获取互联网上面资源的方法叫做http方法。
·HTTP方法
http协议0.9版本的资源获取方法只有一个,叫做GET方法,GET方法是直接从服务器端获取资源到本地的方法,0.9版本的http协议只有这一种资源获取的方法,GET方法只能够使得我们从服务器端获取资源到本地并且在浏览器上面予以显示,而当http协议升级到1.0版本之后引入了更多的资源获取方法,比如PUT方法、POST方法以及DELETE方法等等,目前来讲常用的HTTP方法主要有8个,而最常用的就是上面我们介绍的几种:
PUT和DELETE方法是一组相对应的方法,它们两个是一组常用的相对应的机制
->PUT方法指的是从服务器上面直接获取资源到本地
->DELETE方法指的是在服务器上面删除文件
POST和GET方法是一组相对应的方法
->GET方法是从远程服务器上面获取资源并在浏览器上面予以显示的方法,不是简单的文件传输,而是一种超文本的传输
->POST方法是通过表单提交数据到服务器上去
因此对于web服务器来讲PUT、POST以及DELETE方法都不安全,因为它们都允许客户端直接操作web服务器上面的资源,因此一般来讲我们需要进行认证之后才允许客户端到服务器上面进行操作的,对web服务器来讲最安全的方法就是GET方法,GET方法就相当于我们只读访问文件系统一样很安全,对于http协议1.0版本来讲最大的改良就是引入了MIME的机制。
·MIME(Multipurpose Internet Mail Extension:多用途互联网邮件扩展)
说到邮件我们得先提一下SMTP协议,SMTP(Simple Mail Transmission Protocol:简单邮件传输协议),早期的邮件传输只能传输纯文本内容,但是我们现在可以通过邮件的附件传输很多不是文本的数据,这就是由于后来在SMTP协议中引入了MIME的机制,MIME能够使得发送方将非文本数据在传输前重新编码为文本格式再进行传输,而后接收方能够使用相反的方式将其重新还原为原来的格式,还能够在本地调用相应的程序来打开此文件,比如说我们之前提到过的base64就是一种文本编码格式,http1.0版本原本也只能够传输纯文本数据,后来参照SMTP的MIME机制将MIME引入到了http协议当中,从此以后我们的http协议也就能够传输非文本数据了,这就是为什么今天我们的浏览器中可以显示各种各样丰富多彩的内容的原因,MIME的实现过程如下:
客户端浏览器在互联网上获取一个资源时,该资源在传递给客户端时会明确说明自己属于哪一类文件,比如说客户端此时想要获取一个p_w_picpath/jpeg格式的图片,客户端在获取该资源的时候会通过http协议的元数据,协议是有元数据的,即协议的首部,在协议的首部中明确告诉客户端自己的文件格式是p_w_picpath/jpeg,所以客户端一发现该文件属于p_w_picpath大类下的一个jpeg小子类,就可以知道该资源是一个jpeg格式的图片,因此当客户端浏览器发现这样一个格式的文件后就会调用自己内部与该文件相匹配的插件去展示这样一个文件,如果我们的浏览器不支持显示这种格式的文件的话,它也可以通过接口调用外部的系统上面安装的插件去显示相关的内容,其实浏览器就是通过插件的机制去解析对应MIME的格式的文档,事实上到今天为止,http协议已经能够支持上百种的文件格式,事实上正是由于这种插件的机制的实现,使得后来出现了动态网页的技术。
·动态网页
要想介绍动态网页,得先聊聊flash,flash其实也是一种编程语言,它将这种编程代码嵌入到html文档里面,然后在客户端浏览器上面有一个flash插件,可以将这种语言所表述的内容予以展示,而这种语言所描述的正是一种动态效果的实现机制,事实上很多语言都可以实现动态效果的机制,早期Java刚出现的时候,Java中有一个东西叫做Applet,我们使用Java语言开发一段能够通过http协议传输的Java代码,这些代码传输到客户端之后,只要客户端安装了JRE,客户端就能够解析这个Applet中所描述的代码,这些代码也可以实现动态效果,和flash一样,但是由于Applet过于重量级,不像flash那么轻巧,所以在与flash的竞争中日薄西山,刚火了几年就不行了,但是不论是flash还是Applet实现的都不是动态网页,而是网页中的动态效果,还是静态网页,静态网页对应的web服务器端中存放的一般都是html文档,而动态网页所对应的web服务器中所存放的不是允许用户访问的网页内容即html文档,而是一些待执行的脚本,这些脚本都是由一些编程语言开发的,这些脚本在客户端进行访问的时候首先会获取客户端的特性,即客户端在发起请求的时候会将自己的客户端地址、浏览器类型等等一并发送给服务端,而服务端会将这些属性当作参数传递给那些脚本,然后使得这些脚本在服务端执行一次,而脚本运行的结果就是对应的html文档,所以说任何一个用户在访问一个动态网页的时候,服务端都是临时的随时生成一个html文档供客户端获取的,这种技术才叫做动态网页的技术,而flash只能说是动态效果而不是动态网页,动态网页的网页内容是根据客户端的请求临时生成的,所以不同的用户在访问动态网页的时候,访问的内容有可能一样也有可能不一样,但是生成html文档的那些脚本的执行和web服务器本身没有关系,而是web服务器通过调用额外的工具来执行脚本的,比如说在web服务器上面有一个php格式的文档index.php,当客户端向服务端发起申请去访问这个文件的时候,服务器会根据该文件的扩展名判断该文件不是一个html文档,而是一个php脚本,接下来服务端不会立即响应客户端的请求,而是通过某种连接协议去调用一个额外的运行程序,即php的解释器,让改解释器去运行脚本,而后运行的结果会自己格式化为html的文档,然后将生成的html文档再通过对应的连接协议返回给服务端,这个html文档还可以去调用其它的静态文件,最后再由web服务端将该文档返回给客户端,所以说执行后端脚本的过程和web服务器本身没有关系,因为web服务器只是一个http服务器,它并不负责处理动态内容,web服务端在服务器主机上只是一个运行的进程,并且该进程运行在内存的用户空间,而用户所要访问的文件本身是存在于服务器主机的磁盘上面的,因此客户端到Web服务器上面请求Web资源的大致过程如下:
首先客户端向Web服务端发起请求,该请求首先会到达服务端的内核空间,因为客户端的请求一定是通过TCP/IP协议传输过来的,而TCP/IP协议栈又工作在内核中,所以请求会首先到达内存的内核空间,内核空间在接受请求之后通过内核空间的的TCP/IP协议栈的各种路由机制发现该请求访问的是80端口的套接字,即本机的ip地址的80端口,Web服务一经启动就会默认监听在TCP协议的80端口上,因此内核空间就会将请求通过该套接字转交给用户空间的Web服务端程序,于是执行流程就从内核空间到达了用户空间,紧接着如果用户访问的是一个静态文件index.html的话,那么就由Web服务端直接处理,此时又会陷入内核模式,即Web服务端向内核发起请求获取磁盘上的index.html文件,接下来内核会将该文件返回给Web服务端,服务端通过套接字将响应报文转交给内核空间,最后内核空间通过TCP/IP协议栈最终响应给客户端,以上就是静态网页获取的整个过程,如果客户端访问的是动态内容的话,服务端在接收到请求之后不会立即去处理请求,而是通过其它协议启动另外一个进程,这个进程一定是一个解释器,而后由解释器陷入内核模式将对应的文件获取到,而后解释器会将给文件执行一遍,执行完成后,将执行后的结果返回给Web服务端,而后由Web服务端继续陷入内核模式将该将响应报文通过TCP/IP协议响应给客户端,获取动态内容需要Web服务端通过某种连接协议启动额外的进程,当任务完成后,该进程就会被销毁,所以对Web服务端来说要想响应动态内容首先得创建一个子进程,这些其实都是系统资源的开销,如果服务端的并发量很高的话,服务器的压力就会很大,所以说在同样并发量的前提下,占用服务器资源越少的Web服务端的性能越好。一个html的网页文件index.html里面可能会通过超链接应用多个Web对象,而这些每一个Web对象都有自己的URL,而动态网页中生成的html文件可以引用Web对象,而Web对象本身都是静态的,所以说一个动态网页中的内容应该包括两部分,静态内容和动态内容。
·静态内容
不需要改变的内容都是静态内容,比如说图片、一个文件以及一个rar压缩包等等都是静态内容。
·动态内容
动态内容需要在服务端执行一次才可以返回给客户端,动态网页中只有动态内容才需要在服务端执行,而静态内容则不需要执行,只不过动态内容执行完成之后将这些静态内容生成为一个html文档一并返回给客户端。
事实上在http协议1.0版本之后就可以支持动态网页的机制了,而又因为在1.0版本中引入了MIME,使得Web服务对多媒体内容的支持得到了扩展,也正是因为1.0版本的出现使得Web服务器迅速在全球流行开来并大大的促进了互联网的发展,在Web出现之前互联网受限于硬件技术和网络本身还不能为大众所接受,因为它和普通用户的生活没有关系,正是由于Web服务的技术的出现尤其是http1.0版本的出现之后才使得互联网真正的彻底的走进了人们的生活并反过来促进了互联网技术的发展,所以说我们现在一旦提到互联网,脑海中想到的第一个东西一定是一个网页,http1.0版本中还引入了缓存的机制。
以纯静态页面来讲,所有用户的请求结果都是一样的,那么客户端向Web服务端发起请求需要经过多少个层次?
首先用户想要访问Web服务端肯定得先通过客户端浏览器输入网址进行访问,所以第一步肯定是先通过DNS服务器解析用户所输入的FQDN,将FQDN解析为对应的IP地址之后,客户端才能够将请求数据包发往对应的服务端,而且服务端是通过监听在某个端口上来等待客户端的请求的,首先客户端的请求会到达服务器的内核空间,而后内核如果发现有进程在监听请求所要访问的套接字的话,就会通过该套接字将请求转交给对应的进程,对应的进程事先得在内核中去注册使用并监听相对应的套接字,只有这样内核才会在请求到达之后将请求通过相应的套接字转交给对应的进程,否则内核就会拒绝接收请求,一旦一个客户端的请求到达内核后,内核通过TCP的首部解码发现该请求需要访问的目标端口,于是就将该请求转交给对应的套接字,而服务端一直都有进程在监听着对应的套接字,一旦请求到达服务端,服务端就会响应请求,这就是Web服务器的工作机制,TCP/IP报文在封装的时候,IP首部主要的信息是源IP和目标IP,TCP首部中主要的内容是源端口和目标端口,这些数据报文在发往服务端的时候要在服务端进行拆封,那么服务器上面有很多的资源,我们如何知道客户端要访问的是哪一个资源呢?TCP/IP报文只能帮助我们将请求发送至目的地,但是到达目的地之后要获取相关的资源还得依靠http协议的报文首部。
·HTTP首部
http首部中包含了获取资源的方法和获取的资源,如下:
GET / 2.html
Host:www.lkxedu.com(Host后面一定要跟上主机的名称,这是为Web的虚拟主机提供准备的)
http1.0版本中又引入了这样一种机制,在使用http协议获取资源的时候,一定得在http首部中指明获取资源的主机。
以上内容就是http的首部,也叫做http格式的报文。
·HTTP报文
http报文有两种:
1,请求报文
2,响应报文
·HTTP报文格式
1,请求报文语法格式
开
->空白行,必须要有,无论是请求报文还是 响应报文都得有空白行
GET / HTTP/1.1 ->/表示的是访问网站的主页
Host:www.lkxedu.com
Connection:keep-alive
2,响应报文的语法格式
请求报文和响应报文的协议版本应该一致,
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-Encoding:gzip
Content-Length:6931
·状态代码
客户端在服务端请求资源的时候,为了使得客户端迅速知道自己所请求的资源是否存在,响应报文得给客户端返回一个标识符,这个标识符就叫做状态代码,状态代码分为5类:
1,1系列
1系列的状态代码通常都是纯粹的信息而已,和我们请求的资源没有什么太大的关系,不常用。
2,2系列
2系列的状态代码表示客户端请求资源正确,2系列常见的代码如下:
->200表示请求资源正常,已获取到资源并且正常响应
->201
->202
不同的2系列代码所代表的意义是不同的。
3,3系列
3系列的状态代码表示重定向,重定向表示客户端请求的资源存在,但是资源路径发生了改变,返回3系列代码的同时也将资源对应的新路径返回给了客户端,3系列的状态代码表示的未必都是重定向,常用的3系列代码如下:
->301表示永久重定向
->302表示临时重定向
->304表示modified,即表示客户端所请求的资源没有发生改变,客户端可以继续使用自己缓存中的内容。
4,4系列
4系列的状态代码表示请求资源错误,但是是客户端错误,常见代码如下:
->404表示客户端请求了一个不存在的资源,即Not Found。
5,5系列
5系列的状态代码也表示请求资源错误,但是是服务器端错误。
状态代码都是由三位数字所组成的,不同系列的状态代码所表示的意义是不一样的,所以我们可以通过状态代码来判断客户端请求的内容是否正确,http方法中的HEAD方法可以使得服务端的响应报文只返回报文的首部而不返回报文的主体。
·Web服务器的主要操作
1,建立连接
接受或拒绝客户端的连接请求
2,接收请求
通过TCP/IP协议栈读取http请求报文
3,处理请求
解析请求报文并作出相应的动作
4,访问资源
访问请求报文中请求的资源
5,构建响应
使用正确的首部生成HTTP响应报文
6,发送响应
向客户端发送生成的响应报文
7,记录日志
将已经完成的http事务记录进日志文件
一个真正意义上的原生态的Web服务器是不会处理动态内容的。
·http缓存
客户端在请求Web服务端上的资源的时候,每一个资源都是单独请求单独传输的,所以我们打开一个Web页面的时候,浏览器都会向服务端发起多次请求,因此为了使得客户端打开一个Web页面的速度能够尽可能的快一点,我们的大部分浏览器都是多线程的,而且又由于我们的http协议是基于tcp协议进行数据传输的,所以每一个请求开始和结束都要经历tcp的三次握手和四次挥手,这样的话,我们每打开一个页面所耗费的时间都会很多,而且也会占用服务端很多的资源,因此为了解决上述问题就出现了http缓存的机制,如果客户端浏览器向服务端请求的是一个静态页面的话,浏览器会将所有的静态内容缓存下来,所以我们的浏览器大部分都是支持本地缓存的,当我们第二次打开相同的页面的时候就会发现速度很快,因此大部分的静态内容都是从缓存中返回的,http1.0中引入的缓存机制大大加快了客户端访问资源的速度,也能够节省我们的网络带宽,但是如果我们不小心在浏览器中刷新了一下页面的话,所有的资源请求还是都得重新开始,而每一次的资源请求又得经过tcp的连接等等的过程,还是会耗费很多的时间,于是http协议就进入了1.1的版本。
·HTTP协议1.1版本
http1.1版本中引入了两种机制,一种是机制是用来加强缓存管理的功能,另一种是引入了长连接的机制。
·长连接
我们上面说过客户端从服务端获取资源的时候是一个一个获取的,每一次的获取都会经历tcp的连接建立和连接断开,而长连接指的是客户端与服务端之间的连接永远不会断开,长连接的好处是可以使得同一个客户端在获取第二个资源的时候,可以大大缩短发起请求的时间,而且也可以降低服务端的资源占用率,但是长连接也有坏处,当服务端的并发量巨大的时候,后来的客户端会很难和服务端建立连接,因此服务端为了资源的有效利用,通常会给使用长连接的客户端两种限定:
1,空闲超时->如果某个客户端在请求完所有的资源之后,就会给该客户端定义一个超时时间,到达时限后连接会自动断开
2,如果某个客户端一直在请求资源的话,就会给客户端定义一个请求次数的最大值,到达限制次数后,也会断开该连接
·Web服务端响应客户端请求的四种模型
1,单进程/单线程模型
服务端进程一个一个处理和响应客户端的请求,这是最简单的模型。
2,多进程/多线程模型
服务端有一个主进程专门用来接收客户端的请求,但是它不直接处理和响应请求,而是由它所生成的子进程去处理客户端的请求,这样服务端就可以实现同时处理多个客户端的请求了。
3,单进程多请求模型
服务端有一个进程,所有的客户端请求都有这一个进程同时响应,该进程可以一次性的处理多个客户端请求,但是我们如何在这一个进程当中去判断哪个请求响应结束了呢?服务端进程是依靠轮循的机制来扫描每一个请求,并查看请求是否响应结束,事实上每一个被处理的请求都有一个状态,只要请求的状态发生改变,服务端进程在扫描的时候就会认为该请求已经响应结束,这种机制叫做事件驱动,而且每一个请求在自己的状态发生改变的时候还会主动向服务端进程进行通知,所以第三种模型是依靠事件驱动和通知的机制来提高服务器的效率的,事件驱动机制分为两种:
1,水平触发
请求只向服务端进程发起一次通知
2,边缘触发
请求会一直向服务端发起通知,直到服务端进程过来处理它
4,多进程多请求模型
第四种模型是将第二种和第三种模型整合起来的一种模型,在服务端有一个总理(master)进程,只负责接收客户端请求,然后将请求转交给其它请求进行处理,而每一个进程都可以同时处理多个请求,master进程只是一个主导进程,它主要是为了管理其它进程进行工作,在请求处理完毕之后,相对应的进程就会被销毁,第四种模型可以使得服务端同时响应多个请求,而且支持高并发,系统占用资源小。
有些服务器软件提供了各种模型,我们可以自己选择使用哪一种模型,比如说httpd软件,它给我们提供了很多种服务端响应模型,最常用的一种模型叫做prefork,还有work和event等等,这些服务端响应模型我们统称为httpd的MPM(多道处理模块)。
Web服务器事实上是一种C/S结构的模型:
C(Client Agent:客户端代理程序),最著名的客户端代理程序就是浏览器(Browser)了,除了浏览器之外还有很多的客户端代理程序,比如spider(网络蜘蛛,即搜索引擎)。
S(Server),我们使用URL来标记客户端请求的资源,并通过http报文首部明确客户端请求的资源。
HTTP方法一共有8种:
GET、HEAD、PUT、POST、DELETE、TRACE、OPTIONS以及CONNECTION