声明:本文章中的说法仅是个人理解总结,不一定完全正确,但是可以有助于理解。
关于HTTP协议可以参考以下:
一.域名解析
(2).如果浏览器自身的缓存里面没有找到对应的条目,那么Chrome会搜索操作系统自身的DNS缓存,如果找到且没有过期则停止搜索解析到此结束. 注:怎么查看操作系统自身的DNS缓存,以Windows系统为例,可以在命令行下使用 ipconfig /displaydns 来进行查看
tcp3ci.png
抓包分析:
1 号包,这个是那台虚拟机在广播,要获取192.168.100.254(也就是网关)的MAC地址,因为局域网的通信靠的是MAC地址,它为什么需要跟网关进行通信是因为我们的DNS服务器IP是外围IP,要出去必须要依靠网关帮我们出去才行。2 号包,这个是网关收到了虚拟机的广播之后,回应给虚拟机的回应,告诉虚拟机自己的MAC地址,于是客户端找到了路由出口。3 号包,这个包是wget命令向系统配置的DNS服务器提出域名解析请求(准确的说应该是wget发起了一个DNS解析的系统调用),请求的域名,期望得到的是IP6的地址(AAAA代表的是IPv6地址)4 号包,这个DNS服务器给系统的响应,很显然目前使用IPv6的还是极少数,所以得不到AAAA记录的5 号包,这个还是请求解析IPv6地址,但是这个主机名是不存在的,所以得到结果就是no such name6 号包,这个才是请求的域名对应的IPv4地址(A记录)7 号包,DNS服务器不管是从缓存里面,还是进行迭代查询最终得到了域名的IP地址,响应给了系统,系统再给了wget命令,wget于是得到了的IP地址,这里也可以看出客户端和本地的DNS服务器是递归的查询(也就是服务器必须给客户端一个结果)这就可以开始下一步了,进行TCP的三次握手。
二.发起TCP的3次握手
如下图: tcp.png1) Client首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示Client自己的初始序号(seq = 0 就代表这是第0号包),这时候Client进入syn_sent状态,表示客户端等待服务器的回复2) Server监听到连接请求报文后,如同意建立连接,则向Client发送确认。TCP报文首部中的SYN 和 ACK都置1 ,ack = x + 1表示期望收到对方下一个报文段的第一个数据字节序号是x+1,同时表明x为止的所有数据都已正确收到(ack=1其实是ack=0+1,也就是期望客户端的第1个包),seq = y 表示Server 自己的初始序号(seq=0就代表这是服务器这边发出的第0号包)。这时服务器进入syn_rcvd,表示服务器已经收到Client的连接请求,等待client的确认。3) Client收到确认后还需再次发送确认,同时携带要发送给Server的数据。ACK 置1 表示确认号ack= y + 1 有效(代表期望收到服务器的第1个包),Client自己的序号seq= x + 1(表示这就是我的第1个包,相对于第0个包来说的),一旦收到Client的确认之后,这个TCP连接就进入Established状态,就可以发起http请求了。
看抓包截图:
11.png
9 号包 这个就是对应上面的步骤 1)10 号包 这个对应的上面的步骤 2)11 号包 这个对应的上面的步骤 3)
TCP 为什么需要3次握手?
举个例子:
假设一个老外在故宫里面迷路了,看到了小明,于是就有下面的对话:
老外: Excuse me,Can you Speak English?小明: yes 。老外: OK,I want ...
在问路之前,老外先问小明是否会说英语,小明回答是的,这时老外才开始问路
2个计算机通信是靠协议(目前流行的TCP/IP协议)来实现,如果2个计算机使用的协议不一样,那是不能进行通信的,所以这个3次握手就相当于试探一下对方是否遵循TCP/IP协议,协商完成后就可以进行通信了,当然这样理解不是那么准确。
为什么HTTP协议要基于TCP来实现?
目前在Internet中所有的传输都是通过TCP/IP进行的,HTTP协议作为TCP/IP模型中应用层的协议也不例外,TCP是一个端到端的可靠的面向连接的协议,所以HTTP基于传输层TCP协议不用担心数据的传输的各种问题。
三.建立TCP连接后发起http请求
2.png
下面是第12号包的详细内容:
3.png
以上的报文是HTTP请求报文。
那么HTTP请求报文和响应报文会是什么格式呢?
起始行:如 GET / HTTP/1.0 (请求的方法 请求的URL 请求所使用的协议)头部信息:User-Agent Host等成对出现的值主体
不管是请求报文还是响应报文都会遵循以上的格式。
那么起始行中的请求方法有哪些种呢?
GET: 完整请求一个资源 (常用)HEAD: 仅请求响应首部POST:提交表单 (常用)PUT: (webdav) 上传 DELETE:(webdav) 删除 OPTIONS:返回请求的资源所支持的方法的方法 TRACE: 追求一个资源请求中间所经过的代理
4.png
其中
Accept 就是告诉服务器端,我接受那些MIME类型Accept-Encoding 这个看起来是接受那些压缩方式的文件Accept-Lanague 告诉服务器能够发送哪些语言 Connection 告诉服务器支持keep-alive特性Cookie 每次请求时都会携带上Cookie以方便服务器端识别是否是同一个客户端Host 用来标识请求服务器上的那个虚拟主机,比如Nginx里面可以定义很多个虚拟主机 那这里就是用来标识要访问那个虚拟主机。User-Agent 用户代理,一般情况是浏览器,也有其他类型,如:wget curl 搜索引擎的蜘蛛等 条件请求首部:If-Modified-Since 是浏览器向服务器端询问某个资源文件如果自从什么时间修改过,那么重新发给我,这样就保证服务器端资源 文件更新时,浏览器再次去请求,而不是使用缓存中的文件安全请求首部:Authorization: 客户端提供给服务器的认证信息;
四.服务器端响应http请求,浏览器得到html代码
5.png
用Chrome浏览器看到的响应头信息:
Connection 使用keep-alive特性Content-Encoding 使用gzip方式对资源压缩Content-type MIME类型为html类型,字符集是 UTF-8Date 响应的日期Server 使用的WEB服务器Transfer-Encoding:chunked 分块传输编码 是http中的一种数据传输机制,允许HTTP由网页服务器发送给客户端应用(通常是网页浏览器)的数据可以分成多个部分,分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供Vary 这个可以参考()X-Pingback 参考()
这一段配置指明凡是请求的URL中匹配(这里是启用了正则表达式进行匹配) *.php后缀的(后面跟的参数)都交给后端的fastcgi进程进行处理。
2 把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是有编号的),如下图所示:
还有一个要点:目录其实也普通是文件,也需要占用磁盘块,目录不是一个容器。你看默认创建的目录就是4096字节,也就说只需要占用一个磁盘块,但这是不确定的。所以要找到目录也是需要到元数据区里面找到对应的条目,只有找到对应的inode就可找到目录所占用的磁盘块。
那到底目录里面存放着什么,难道不是文件或者其他目录吗?
其实目录存着这么一张表(姑且这么理解),里面放着 目录或者文件的名称和对应的inode号(暂时称之为映射表),如下图:
8.jpg
假设
/ 在数据区占据 1、2号block ,/其实也是一个目录 里面有3个目录 web 111web 占据 5号block 是目录 里面有2个目录 echo dataecho 占据 11号 block 是目录 里面有1个文件 index.phpindex.php 占据 15 16号 block 是文件
其在文件系统中分布如下图所示
9.png
那么内核究竟是怎么找到index.php这个文件的呢?
内核拿到nginx的IO系统调用要获取/web/echo/index.php这个文件请求之后
1 内核读取元数据区 / 的inode,从inode里面读取/所对应的数据块的编号,然后在数据区找到其对应的块(1 2号块),读取1号块上的映射表找到web这个名称在元数据区对应的inode号2 内核读取web对应的inode(3号),从中得知web在数据区对应的块是5号块,于是到数据区找到5号块,从中读取映射表,知道echo对应的inode是5号,于是到元数据区找到5号inode3 内核读取5号inode,得到echo在数据区对应的是11号块,于是到数据区读取11号块得到映射表,得到index.php对应的inode是9号4 内核到元数据区读取9号inode,得到index.php对应的是15和16号数据块,于是就到数据区域找到15 16号块,读取其中的内容,得到index.php的完整内容
五. 浏览器解析html代码,并请求html代码中的资源
10.png详细的浏览器工作原理请看:
六.浏览器对页面进行渲染呈现给用户
最后,浏览器利用自己内部的工作机制,把请求到的静态资源和html代码进行渲染,渲染之后呈现给用户。
自此一次完整的HTTP事务宣告完成.