http请求过程

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤: 

1. 建立TCP连接

在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。

2. Web浏览器向Web服务器发送请求命令 

一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令(HTTP请求报文的请求行)。例如:GET /sample/hello.jsp HTTP/1.1。

3. Web浏览器发送请求头信息 

浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息(HTTP请求报文的请求头部),之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。 

4. Web服务器应答 

客户机向服务器发出请求后,服务器会向客户机回送应答(HTTP响应报文的状态行), HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。

5. Web服务器发送应答头信息 

正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档(HTTP响应报文的响应头部)。 

6. Web服务器向浏览器发送数据 

Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据(HTTP响应报文的响应正文)

7. Web服务器关闭TCP连接 

一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:

Connection:keep-alive 

TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。


------------------------------------------------------------------------------------------------


web页面由对象组成,一个对象只是一个文件,如一个html文件。一个JPEG图形,一个JAVA小程序,或一个视频片段。

URL由两部分组成,存放对象的服务器主机名和对象的路径名。

以在浏览器输入http://www.myApp.com:8000/mainPage/index.html为例,说明HTTP请求的过程。

1、域名解析点击打开链接

首先浏览器会解析www.myApp.com这个域名对应的ip地址

1) 搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存),看自身的DNS缓存中是否有www.myApp.com对应的条目,而且没有过期,如果有且没有过期,则解析到此结束。

2) 如果在浏览器自身的DNS缓存里没有找到,则搜索操作系统自身的DNS缓存,如果找到且没有过期,则解析到此结束。

3) 如果在操作系统自身的DNS缓存里也没有找到,则尝试读取hosts文件(位于C:\Windows\System32\drivers\etc),如果找到,则解析到此结束。

4) 如果在hosts文件里也没有找到,则会发起一个DNS的系统调用,根据本地配置网络时填写的DNS地址向首选DNS服务器(一般是电信运营商提供的,也可以使用像Google提供的DNS服务器)发起域名解析请求(通过UDP协议向DNS的53端口发起请求,这个请求是递归的请求)。

4.1) 首选DNS服务器首先查找自身的缓存,如果找到且没有过期,则解析到此结束。

4.2) 如果没有找到,则由首选DNS服务器代浏览器发起迭代的DNS解析请求,首先会找根域的DNS服务器的IP地址,找到根域的DNS服务器后,就会向其发起请求(请问www.myApp.com这个域名的IP地址是多少啊?)

4.3) 根域的DNS服务器发现这是一个顶级域com域的一个域名,于是就告诉首选DNS服务器我不知道这个域名的IP地址,但是我知道com域的DNS服务器的IP地址,于是首选DNS服务器就得到了com域的DNS服务器的IP地址,又向com域的DNS服务器发起了请求(请问www.myApp.com这个域名的IP地址是多少?)

4.4)com域的DNS服务器告诉首选DNS服务器我不知道这个域名的IP地址,但是我知道myApp.com这个域的DNS服务器的IP地址,于是首选DNS服务器又向myApp.com这个域的DNS服务器发起请求(请问www.myApp.com这个域名的IP地址是多少啊?),这时linux178.com域的DNS服务器一查,找到了www.myApp.com这个域名对应的IP地址,于是把找到的结果发送给首选DNS服务器,这时首选DNS服务器就拿到了www.myApp.com这个域名对应的IP地址,并返回给Windows系统内核,内核又把结果返回给浏览器,终于浏览器拿到了www.myApp.com对应的IP地址。

注:一般情况下是不会进行以下步骤的

如果经过以上的4个步骤,还没有解析成功,那么会进行如下步骤(以下是针对Windows操作系统):

⑤ 操作系统就会查找NetBIOS name Cache(NetBIOS名称缓存,就存在客户端电脑中的),那这个缓存有什么东西呢?凡是最近一段时间内和我成功通讯的计算机的计算机名和Ip地址,就都会存在这个缓存里面。什么情况下该步能解析成功呢?就是该名称正好是几分钟前和我成功通信过,那么这一步就可以成功解析。

⑥ 如果第⑤步也没有成功,那会查询WINS 服务器(是NETBIOS名称和IP地址对应的服务器)

⑦ 如果第⑥步也没有查询成功,那么客户端就要进行广播查找

⑧ 如果第⑦步也没有成功,那么客户端就读取LMHOSTS文件(和HOSTS文件同一个目录下,写法也一样)

如果第八步还没有解析成功,那么就宣告这次解析失败,那就无法跟目标计算机进行通信。只要这八步中有一步可以解析成功,那就可以成功和目标计算机进行通信。

2、客户端发起TCP的3次握手,通过socket与服务器建立一个TCP连接

拿到域名对应的IP地址之后,现在已经拥有了目标ip和端口号8000,这样我们就可以打开socket连接了。User-Agent即浏览器会以一个随机端口(1024 ~ 65535之间)向服务器端的Web程序发起TCP的连接请求。这个连接请求(原始的HTTP请求经过TCP/IP协议的4层模型的层层封包)到达服务器端后(这中间通过各种路由设备,局域网内除外),进入到网卡,然后是进入到内核的TCP/IP协议栈(用于识别该连接请求,解封包,一层一层的剥开),还有可能要经过Netfilter防火墙(属于内核的模块)的过滤,最终到达服务器端的Web程序,最终建立了TCP/IP的连接。

TCP的三次握手:点击打开链接

1) Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号,表示客户端等待服务器的回复。

2) Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x + 1,同时表明x为止的所有数据都已正确收到,seq = y 表示Server 自己的初始序号,表示Server已经收到Client的连接请求,等待Client的确认。

3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 ,ack = y + 1表示期望收到对方下一个报文段的第一个数据字节序号是y + 1,seq= x + 1 表示Client自己的初始序号,一旦收到Client的确认之后,TCP连接就建立完毕,可以发起HTTP请求了。

3、客户端向服务器发送HTTP请求

TCP连接成功建立后,客户端开始向Web服务器发送请求,这个请求一般是GET或POST命令。

GET命令的格式为:GET 路径/文件名 HTTP/1.0,其中,路径/文件名指出所访问的文件,HTTP/1.0指出Web浏览器使用的HTTP版本。

1次完整的HTTP请求消息包括:一个请求行、若干消息头以及实体内容,而消息头和实体内容可以没有,消息头和实体内容间有一个空行。

[plain]  view plain  copy
  1. Get /mattmarg/ HTTP/1.0  
  2. Accept: text/html  
  3. Referer: http://www.myApp.com:8000/mainPage/index.html  
  4. Accept-Lanague: zh-CN  
  5. Content-Type: text/html  
  6. Accept-Encoding: gzip, deflate  
  7. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36  
  8. Host: http://www.myApp.com  
  9. Connection: keep-alive  
  10. Cookie: name = value  
  11. #空行  
  12. <html><head></head><body>...</body></html>  

第一行是HTTP请求的请求行:GET /mainPage/index.html HTTP/1.0。

接下来的几行是消息头,消息头主要是用来向服务器传达某种信息或指示。如告诉服务器自己的终端(User-Agent)(如果是浏览器则返回相应的浏览器型号),终端可以解释的类型(Accept),是从哪个页面提交的请求(Referer)以及浏览器缓存的Cookie等等。

Accept   告诉服务器自己接受的MIME类型
Referer
告诉服务器请求是从哪个页面提交的
Accept-Lanague
告诉服务器自己接受的语言
Content-Type
告诉服务器自己发送的MIME类型
Accept-Encoding
告诉服务器自己接受的文件压缩方式
User-Agent 
告诉服务器自己的终端,如果是浏览器则返回相应的浏览器型号
Host       
用来标识请求服务器的主机
Connection
告诉服务器自己是否支持Keep-Alive特性
Cookie
每次请求时都会携带Cookie以方便服务器识别是否是同一个客户端

MIME,Multipurpose Internet Mail Extesions多用途互联网邮件扩展,是一个互联网标准,它扩展了电子邮件标准,使其能够支持非ASCII字符、二进制格式附件等多种格式的邮件消息,这个标准被定义在RFC 2045、RFC 2046、RFC 2047、RFC 2048、RFC 2049等RFC中。由RFC 822转变而来的RFC 2822,规定电子邮件标准并不允许在邮件消息中使用7位ASCII字符集以外的字符。正因如此,一些非英语字符消息和二进制文件,图像,声音等非文字消息都不能在电子邮件中传输。MIME规定了用于表示各种各样的数据类型的符号化方法。此外,在万维网中使用的HTTP协议中也使用了MIME的框架,标准被扩展为互联网媒体类型。

MIME 遵循以下格式:主类型/次类型。如:

1) image/jpg

2) image/gif

3) text/html

4) video/quicktime

5) appliation/x-httpd-php

4、服务器向客户端发送HTTP响应

Web服务器收到HTTP请求,进行处理。从它的文档空间中搜索子目录mainPage的文件index.html。如果找到该文件,Web服务器把该文件内容传送给相应的浏览器。

1次完整的HTTP响应消息包括:一个状态行、若干消息头以及实体内容,而消息头和实体内容可以没有,消息头和实体内容间有一个空行。

[plain]  view plain  copy
  1. HTTP 1.0 200 OK  
  2. Accept: text/html  
  3. Accept-Encoding: gzip, deflate  
  4. Accept-Language: zh-CN  
  5. Content-Type: text/html  
  6. Cache-Control: max-age=0  
  7. Connection: keep-alive  
  8. Host: http://www.myApp.com  
  9. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36  
  10. #空行  
  11. <html><head></head><body>...</body></html>  

HTTP 1.0 200 OK是HTTP响应的第一行,列出服务器正在运行的HTTP版本号和应答代码,代码"200 OK"表示请求完成。


那到底服务器端接收到http请求后是怎么样生成html文件?

假设服务器端使用nginx+PHP(fastcgi)架构提供服务

① nginx读取配置文件

我们在浏览器的地址栏里面输入的是 http://www.linux178.com (http://可以不用输入,浏览器会自动帮我们添加),其实完整的应该是http://www.linux178.com./ 后面还有个点(这个点代表就是根域,一般情况下我们不用输入,也不显示),后面的/也是不用添加,浏览器会自动帮我们添加(且看第3部那个图里面的URL),那么实际请求的URL是http://www.linux178.com/,那么好了Nginx在收到 浏览器 GET / 请求时,会读取http请求里面的头部信息,根据Host来匹配 自己的所有的虚拟主机的配置文件的server_name,看看有没有匹配的,有匹配那么就读取该虚拟主机的配置,发现如下配置:

1
root  /web/echo

通过这个就知道所有网页文件的就在这个目录下 这个目录就是/ 当我们http://www.linux178.com/时就是访问这个目录下面的文件,例如访问http://www.linux178.com/index.html,那么代表/web/echo下面有个文件叫index.html

1
index index.html index.htm index.php

通过这个就能得知网站的首页文件是那个文件,也就是我们在入http://www.linux178.com/ ,nginx就会自动帮我们把index.html(假设首页是index.php 当然是会尝试的去找到该文件,如果没有找到该文件就依次往下找,如果这3个文件都没有找到,那么就抛出一个404错误)加到后面,那么添加之后的URL是/index.php,然后根据后面的配置进行处理

1
2
3
4
5
6
7
location ~ .*\.php(\/.*)*$ {
    root  /web/echo ;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    astcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

这一段配置指明凡是请求的URL中匹配(这里是启用了正则表达式进行匹配) *.php后缀的(后面跟的参数)都交给后端的fastcgi进程进行处理。

② 把php文件交给fastcgi进程去处理

于是nginx把/index.php这个URL交给了后端的fastcgi进程处理,等待fastcgi处理完成后(结合数据库查询出数据,填充模板生成html文件)返回给nginx一个index.html文档,Nginx再把这个index.html返回给浏览器,于是乎浏览器就拿到了首页的html代码,同时nginx写一条访问日志到日志文件中去。

注1:nginx是怎么找index.php文件的?

当nginx发现需要/web/echo/index.php文件时,就会向内核发起IO系统调用(因为要跟硬件打交道,这里的硬件是指硬盘,通常需要靠内核来操作,而内核提供的这些功能是通过系统调用来实现的),告诉内核,我需要这个文件,内核从/开始找到web目录,再在web目录下找到echo目录,最后在echo目录下找到index.php文件,于是把这个index.php从硬盘上读取到内核自身的内存空间,然后再把这个文件复制到nginx进程所在的内存空间,于是乎nginx就得到了自己想要的文件了。

注2:寻找文件在文件系统层面是怎么操作的?

比如nginx需要得到/web/echo/index.php这个文件

每个分区(像ext3 ext3等文件系统,block块是文件存储的最小单元 默认是4096字节)都是包含元数据区和数据区,每一个文件在元数据区都有元数据条目(一般是128字节大小),每一个条目都有一个编号,我们称之为inode(index node 索引节点),这个inode里面包含 文件类型、权限、连接次数、属主和数组的ID、时间戳、这个文件占据了那些磁盘块也就是块的编号(block,每个文件可以占用多个block,并且block不一定是连续的,每个block是有编号的),如下图所示:

wKiom1LSXwWRzx75AACjRCdIYcI778.jpg

还有一个要点:目录其实也普通是文件,也需要占用磁盘块,目录不是一个容器。你看默认创建的目录就是4096字节,也就说只需要占用一个磁盘块,但这是不确定的。所以要找到目录也是需要到元数据区里面找到对应的条目,只有找到对应的inode就可找到目录所占用的磁盘块。

那到底目录里面存放着什么,难道不是文件或者其他目录吗?

其实目录存着这么一张表(姑且这么理解),里面放着 目录或者文件的名称和对应的inode号(暂时称之为映射表),如下图:

wKiom1LSX3KATYWYAAAx2GkMEO4103.jpg

假设

/           在数据区占据 1、2号block ,/其实也是一个目录 里面有3个目录  web 111

web         占据 5号block  是目录 里面有2个目录 echo data

echo        占据 11号 block  是目录  里面有1个文件 index.php

index.php   占据 15 16号 block  是文件

其在文件系统中分布如下图所示

wKioL1LSX6OizObEAAHJJkuxCa0943.jpg

那么内核究竟是怎么找到index.php这个文件的呢?

内核拿到nginx的IO系统调用要获取/web/echo/index.php这个文件请求之后

① 内核读取元数据区 / 的inode,从inode里面读取/所对应的数据块的编号,然后在数据区找到其对应的块(1 2号块),读取1号块上的映射表找到web这个名称在元数据区对应的inode号

② 内核读取web对应的inode(3号),从中得知web在数据区对应的块是5号块,于是到数据区找到5号块,从中读取映射表,知道echo对应的inode是5号,于是到元数据区找到5号inode

③ 内核读取5号inode,得到echo在数据区对应的是11号块,于是到数据区读取11号块得到映射表,得到index.php对应的inode是9号

④ 内核到元数据区读取9号inode,得到index.php对应的是15和16号数据块,于是就到数据区域找到15 16号块,读取其中的内容,得到index.php的完整内容

5、服务器发起TCP的4次挥手,关闭与客户端的TCP连接

应答结束后,若Connection为close,则服务器主动关闭TCP连接,客户端被动释放TCP 连接,若Connection为keep alive,则该连接会保持一段时间,在该时间内,下次的请求可以继续使用此连接。

TCP的四次挥手:点击打开链接

1) ServerClient发送一个FIN报文,序列号为iFINACK都置,用来关闭Server到Client的数据传送,也就是告诉Client,Server已经不会再发数据了(当然,在FIN包之前发送出去的数据,如果没有收到对应的ACK确认报文,Client依然会重发这些数据),但此时Server还可以接收数据。

2Client接到FIN(i)报文后,但是如果还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据,Client发送ACK,序列号为i + 1ACK1,告诉Server请求已经收到了,但Client还没准备好,Server继续等待ClientFIN报文。

3) 当Client确定数据已发送完成,则向Server发送FIN报文,序列号为jFINACK都置1,告诉ServerClient数据发完了,准备好关闭连接了。

4) Server收到FIN(j)报文后,就知道可以关闭连接了,Server发送ACK,序列号为j + 1ACK1 Client收到ACK后,就知道可以断开连接了,至此,TCP连接就已经完全关闭了!

6、浏览器解析HTML代码,并请求HTML代码中的资源

浏览器拿到index.html文件后,就开始解析其中的HTML代码,遇到.js.css及图片等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这个时候就用上keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里的顺序,但是由于每个资源大小不一样,而浏览器又多线程请求请求资源,所以从下图看出,这里显示的顺序并不一定是代码里面的顺序。

浏览器在请求静态资源时(在未过期的情况下),向服务器端发起一个HTTP请求,询问从上一次修改到现在有没有对资源进行修改,如果服务器端返回304状态码(Not Modified)(告诉浏览器没有对资源进行修改),那么浏览器会直接读取本地的该资源的缓存文件。

3xx: 重定向状态码

    301: 永久重定向, Location响应首部的值仍为当前URL,因此为隐藏重定向;

    302: 临时重定向,显式重定向, Location响应首部的值为新的URL

    304:Not Modified  未修改,比如本地缓存的资源文件和服务器上比较时,发现并没有修改,服务器返回一个304状态码,告诉浏览器,你不  用请求该资源,直接使用本地的资源即可。


7、浏览器对页面进行渲染呈现给用户

浏览器利用自己内部的工作机制,把请求到的静态资源和HTML代码进行渲染,渲染之后呈现给用户。



HTTP请求的完整过程可以分为以下几个步骤: 1. 建立连接:客户端使用TCP协议与服务器建立连接。首先需要解析URL,获取服务器的主机地址和端口号。然后客户端会发送一个SYN包给服务器,服务器收到后返回一个SYN-ACK包给客户端,最后客户端发送一个ACK包确认连接建立。 2. 发送请求头:连接建立后,客户端会发送一个HTTP请求给服务器。请求包含请求行、请求头和请求体。请求行包括请求方法(GET、POST等)、请求的URI(Uniform Resource Identifier)和HTTP协议版本。请求头包含一些附加的信息,如用户代理、Accept-Language、Cookie等。 3. 服务器响应:服务器接收到请求后,会进行处理并生成相应的HTTP响应。响应包括响应行、响应头和响应体。响应行包括HTTP协议版本、状态码和状态消息。响应头包含服务器信息、内容类型、内容长度等相关信息。响应体包含实际的响应数据。 4. 接收响应:客户端接收到服务器的响应后,会进行解析和处理。根据状态码判断请求是否成功,并根据响应头的信息进行相应的处理,如获取内容长度、内容类型等。 5. 处理响应:根据响应头的内容类型,客户端可以选择不同的方式来处理响应数据。常见的内容类型包括HTML、JSON、XML等。客户端可以将响应数据展示给用户,或者进行进一步的处理,如解析JSON数据、解析XML数据等。 6. 关闭连接:处理完响应后,客户端和服务器会关闭连接。客户端发送一个FIN包给服务器,服务器收到后返回一个ACK包给客户端,最后服务器发送一个FIN包给客户端,客户端收到后返回一个ACK包给服务器,双方完成连接的关闭。 以上就是HTTP请求的全过程。其,客户端和服务器之间的通信是通过TCP协议进行的。PHP是一种服务器端脚本语言,可以用来处理HTTP请求和生成HTTP响应。在PHP,可以使用内置的函数和扩展来处理HTTP请求和响应,如$_GET、$_POST、header()函数等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值