http原理详解
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.OSI和TCP/IP协议。
我们知道在我们计算机中的网络通信有两个模型,一个是ISO组织的OSI模型,另一个是TCP/IP模型。OSI目前来讲是一种参考模型,它没有被真正的使用,因为它是一套理论模型,过于重量级,而且有些功能在设计上有重叠现象,所以并不适用。TCP/IP虽然事先并没有模型,而是在应用中逐步完善修修补补敲敲打打才发展起来的,但是它是一个很好用的,在实战中摸爬滚打起来的军事家。而不是纸上谈兵的军事家。这是他们两者的区别。
![](https://i-blog.csdnimg.cn/blog_migrate/7b0bd36b5448adcd2fc5c81eefc54607.png)
我们把OSI模型的上三个层次叫做资源子网,下面的四个层次叫做通信子网,这也就意味着我们下面的四个层次(通信子网)主要的着眼点在于实现数据通信的,而上面的三个层次(资源子网)主要的着眼点是实现数据共享(资源共享)。而我们把这样的层次细致到每一台主机上的话,以Linux系统为例,通信子网层是在内核中实现的,那也就意味着两台主机的通信建立,实际上就是靠他们本身的内核来完成的,因此内核主要关心的是通信细节。而站在另外的一个角度来想,如果要完成所谓资源共享,它必须要用一个用户进程(也就是某一个应用程序)在两台主机之间建立通信之后完成资源或信息的传输。那也就意味着应用层是需要在用户空间来实现。
二.套接字的分类
Unix Sock:如果两个通信的进程在同一个主机上,客户端进程需要给服务器端发请求时,它可以直接请求向文件系统写一个文件,然后服务端可以去读取这个文件,最终就会实现了客户端和服务端之前的通信。而这一种通信就是基于Unix Sock,说白了它是基于文件系统(filesystem)的。
Socket:如果两个通信进程不在同一个主机上,客户端进程需要往服务端发送求情时,就需要用到Socket模型了,需要写明“host:port”,host一般指的就是一个主机,用IP进行表示,port指的是端口,用来标识一台主机上的唯一的一个服务的。实际上这种通信机制是有BSD这个组织发明的。所以有时候它也被称为BSD的socket成型。
三.IP报文的传输过程
我们知道Socket这种套接字主要是为了让两台主机的进程实现网络通信的,这个时候就不得不利用我们的网络功能封装报文向外发送或接受数据。我们在客户端想服务端发送的数据可能会比较大,比如客户端要想服务端下载一个1.8G的高清无码小电影。服务端需要将这个视频文件进行切割(为什么要切割?我们知道一个IP报文的总长度[65535-守护长度]个字节,是无法一次性发送1.8G的文件哟!)以后才能发送给客户端,而被切割的数据在发送的过程中需要经过互联网的各个路由器交换机等等,而在物理层传输数据时,还会涉及到一个MTU的概念(最大传输单元,而最大传输单元一般通行的情况下是1500或是更小),一旦遇到更小的MTU时,我们的IP报文还会被再一次切割成IP分片。最终数据传输到客户端时,首先先对这些IP分片在内存中进行组合形成IP报文,然后在把IP报文里的数据取出来放在某个存储空间上,把所有接受的包文的数据进行组合形成一个完整的文件。一旦丢失了任何一块数据,都可能导入文件损坏。
四.传输层协议
面向连接的:TCP(TCP套接字)
需要在传输方和接收方建立一个虚连接。互联网上有95%的协议都基于TCP来传输的。
无连接的:UDP(UDP套接字)
即时通讯软件(QQ聊天等),DNS解析,TFTP
其他协议:
再传输层并不意味着只有TCP和UPD协议,如raw套接字。它既不基于UPD也不基于TCP,而是应用层的一个程序自身负责建立一个原始的套接字进行数据通信的。绝大多数都是基于TCP和UDP协议来传输的。
五.网际协议
不管是TCP,UDP还是RAW协议最终报文发送仍然无法绕过IP协议(网际协议)的视线。而网际协议目前来说比较流行的有2个版本,一个是IPV4,另外一个就是IPV6。两个协议组织方式是不一样的哟。
六.设备驱动程序和硬件
事实上对应OSI模型的最下面两层,即数据链路层和物理层。相较于我们的TCP/IP模型来讲通常是对应的是设备驱动程序和硬件(当然我们知道物理层本身就是硬件,你的网卡设备本身以及你的网卡上链接的网线)。说你你的网卡应该做成什么样,接口应该是什么样以及网线应该遵循什么样的规范等等这是物理层协议。在这个协议上予于实现的就是硬件了。数据链路的绝大多数功能(以太网帧通过物理网卡往外发送数据的)都可以做在网卡中或是做在网络设备的驱动程序里面。所以,我们说数据链路层和物理层本身就是由设备驱动(内核负责管理)程序以及硬件组成。
七.应用层协议
通信子网只负责把消息传输给对端,说以我们说他只是负责传输的。至于传输过去的数据对方主机是否能解析就得用应用层协议来实现啦。通信子网是通用的,不管你是什么协议它都能负责传输。而应用层协议就是特定的(ftp客户端和stmp服务端他们之间是没有共同语言的,因为他们的协议都是特定的,也就是说,stmp服务端只能识别stmp客户端,而不能识别ftp的客户端)。目前比较主流的协议有很多,比如:http,https,smtp,pop,imap,itp,ldap等等。
八.什么是http协议
http协议是超文本(ASSIC码)传输协议,全称为hypertext transport protocol。它是遵循HTML(超文本标记语言,hypertext mark language)研发的文本格式。http应用是C/S架构,其中客户端是浏览器(Browser),服务端是http Server。那么问题来了,客户端和服务端是如何进行通信的呢?很简单,我们的Browser有一个位置允许我们去填充路径去获取某种资源。而这个位置就是URL(统一资源定位符),它是URI(统一资源标识符)的一个子集。
URI:全称为Uniform Recource Identifier,统一资源标识符或者叫做统一资源命名符。统一资源用来定义互联网上的每一个资源。不光是web服务的,你的ftp,ldap等任何一种协议但凡要用到唯一标识某一个资源的话,都要遵循这种方式,才能够保证被互联网上被唯一标识。
URL:全称Uniform Recource Locator,它是URI的一个子集,换句话说,URL只实现了URI的一部分。而一个URL必须遵循一种格式:“scheme://host[:port]/path/.../[?query-string][#anchor]”
1>.scheme :指定底层使用的协议(例如:https,http,ldap,ftp,svn)
2>.host : HTTP服务的IP地址或者域名
3>.port :HTTP服务器的默认端口是80,这种情况下端口号可以省略,如何使用了别的端口必须指明,例如:http://node1.yinzhengjie.com:9090/
4>.path :访问资源的路径
5>.query-string :发送给http服务器的数据
6>.anchor :锚,你可以理解是当前网页的某一个位置(类似于你读书看到某一页的某一行的标记而已)。
九.http协议版本
HTTP协议设计就是用来传输文本的,它是被设计出来传输字符的(也就是ASCII码)。或者基于Unicode编码的字节串。它没有办法用来传输图片,如果非要用它来传输图片,它只能讲图片进行切割进行传输,最后用户打开的时候只能得到一堆乱码文件啦~这个时候我们就得了解一下http协议的版本啦。
起初,在1991年左右,http的0.9版本仅用于传输html文档。
MIME(全称Multipurpose Internet Mail Extension)是多用户互联网邮件扩展机制,引用了base64的编码机制,能够实现将二进制数据编码成文本,并且接受方还能将其还原回原来的格式。
一般来讲,MIME是一种标记机制,为了能够实现让双方在交互时能够明确定义所支持的资源类型有哪些,我们需要定义major(主类型)/minor(子类型)这种格式向客户端发送过去的资源类型是什么,但对我们而言,我们需要对这些基本类型要做一些了解,例如HTML的文档的资源类型是text(主类型)/html(子类型);ASCII(纯文本文件)的资源类型是text(主类型)/plain(子类型),如果是JPEG格式的图片的话,image(主类型)/jpeg(子类型);如果是GIF格式的话,则是image(主类型)/gif(子z类型)
如果是流媒体QuickTime的话,其资源类型是video(主类型)/QuickTime(子类型)。当然httpd服务器在默认在“/etc/httpd/conf/magic”这个文件中有定义具体的资源类型。
注意,服务器端在基于response报文将一个结果发往客户端(浏览器)时,客户端会根据这个多媒体类型来判定使用什么工具来打开这个资源。大家都知道浏览器是可以查看图片的,那是不是浏览器本身就是一个查看器呢?答案是否定的,浏览器并不具备查看图片和打开视频的功能,但为什么我们能在浏览器上看图片或者打开视频呢?事实上,浏览器会对接收到的response报文的数据通过MIME机制进行分析,将分析的结果(比如分析的类型是JPEG或是video)通过其自身的调用机制用当前操作系统以及安装的程序来解析它拿到的资源,比如浏览器拿到了一个“rmvb”的类型,那么它就调用当前操作系统能够解析该资源的程序去打开它。浏览器大多数默认都是调用flash插件去打开网页版本的视频的。
http从1.0版本将MIME(Multipurpose Internet Mail Extension,引用了base64的编码机制,能够实现将二进制数据编码成文本,并且接受方还能将其还原回原来的格式。MIME是多媒体类型,)进行完善,从而支持多媒体数据的处理并且引入了一些http的方法使得它也可以和用户进行交互(例如,填表单等功能)。因此,http/1.0已经是一个比较完整的版本了,支持保持连接(keep-alive)的机制,且有缓存功能。
但是1.0当中还是有些缺陷因此就有了1.1版本(该版本支持更多的请求方法,支持更惊喜的缓存机制,以及原生支持持久连接。),目前,比较主流的两个版本是http/1.0和http/1.1版本。
十.HTTP协议的结构
1>.HTTP事务:一次客户端的请求以及与其服务端对应的响应,
2>.HTTP方法:
GET:请求获取一个资源,需要服务器响应(该方法比较安全);
PUT:与GET相反,向服务器写入文档,例如发布系统;
HEAD:跟GET相似,但其不需要服务发送资源而仅传贵响应首部; 只给我发送响应报头信息即可,换句话说,你有没有这个页面跟我说一下就好,不用把内容发给我(该方法比较安全);
POST:支持HTML表单提交,表单中有用户填入的数据,这些数据会发送到服务器端,有服务器存储至某个特定的位置(例如发往处理程序);
DELETE:请求删除URL指向的资源;
OPTIONS:探测服务器对某资源所支持的请求方法;
TRACE:跟踪请求要经过的防火墙,代理或网关等;
扩展方法:LOCK,MKCOL,COPY,MOVE等;
3>.HTTP首部(headers):
A>.通用首部:请求和响应都可以使用的;
Connection,定义C/S之间关于请求/响应的有关选项,对于http/1.0,Connection:keep-alive。
Via:显示了报文经过的中间节点;
Cache-Control:缓存指示(缓存控制);
B>.请求首部:客户端专用首部
Client-IP:连接的源IP地址;
Host:请求的主机名和端口号,虚拟主机环境下用于不同的虚拟主机;
Referer:指明了请求当前资源(目的URL)的原始资源地址(源URL);
User-Agent:用户代理,使用了什么工具发出的请求;
C>.Accept首部:用户标明客户自己更倾向于支持使用的方式;
Accept:指明服务器能发送的媒体类型;
Accept-Charset:支持使用的字符集;
Accept-Encoding:支持使用的编码方式;
Accept-Langage:支持使用语言;
D>.条件请求首部:主要应用于缓存机制的判断;
Expect:客户端告诉服务器端自己期望的行为;
If-Modified-Since:是否在指定的时间以来修改过此资源,如果修改过就发新资源;
If-None-Match:一个也没有匹配的;
E>.跟安全相关的请求首部:
Authorization:客户端提交给服务端的认证数据,如账号和密码;
Cookie:客户端发送给服务端身份标识;
Cookie2:Cookie的另外一个版本;
F>.响应首部:服务器端专用首部
Age:指定相应持续的时间;
Server:向客户端标明服务器程序名称和版本;
G>.协商首部:
Accept-Ranges:对当前资源来讲,服务器所能够接受的范围类型(比如服务器是否支持压缩和不压缩,有的服务器不支持压缩文件。);
Vary:首部列表,服务器会根据列表中的内容挑选处最合适的版本发送给客户端;
H>.跟安全相关的响应首部:
Set-Cookie:服务器端在某个客户端第一次请求时发给令牌;
Set-Cookie2:Set-Cookie的另外一个版本;
WWW-Authentication:质询,即要求客户提供账号和密码;
I>.实体首部:用于指定实体属性;
Location:资源的新位置
Allow:运行
J>.内容首部:用于标明内容格式或是内容类型等等
Content-Encoding:告诉用户么有拍卖行编码格式;
Content-Language:告诉用户内容的语言;
Content-Length:内容长度;
Content-Location:资源所处的实际位置;
Content-Range:类型的范围;
Content-Type:内容类型;
K>.缓存首部:和缓存相关的首部
Etag:实体标签;
Expires:过期期限;
Last-Modified:上一次的修改时间;
L>.扩展首部:非标准首部,可能是有程序开发者创建的,例如X-Forward-For。
4>.标签的说明
<method>:请求方法,希望服务器端执行的动作,如GET,HEAD,POST等
<request-url>:请求的资源,可以是相对路径,也可以是完整的URL。
<version>:协议版本,格式HTTP/<major[主版本号]>.<minor[次版本号]>,如:http/1.0
<status>:状态码分五类(以下举例都是常见的状态码。)
1xx:信息状态码
2xx:成功状态码
200:ok (表示请求资源没问题);
201:Created(比如,用PUT方法上传文件给服务器,成功上传就会返回该状态码)。
3xx:重定向状态码
301:Moved Permanently,在响应报文中使用首部“Location:URL”;
302:Found,在响应报文中使用首部“Location:URL”指定临时资源位置;
304:Not Modified,条件式请求中使用,一般用于缓存,(比如,客户端第二次请求一个资源是否修改了,服务端告诉你没有修改数据,那么就不需要下载数据了,直接用缓存的数据就好,这就是为什么第二次打开一个网站速度回很快,大多数都是缓存在搞鬼);
4xx:客户端类的错误
403:Forbidden,不允许访问,请求被服务器拒绝;
404:Not Found,服务器无法找到请求的URL;
405:Method Not Allowed,不允许使用此方法请求相应的URL;
5xx.:服务器端类错误
500:Internal Server Error,服务器内部错误(例如服务器脚本无权限执行);
502:Bad Gateway,代理服务器从上联服务器收到了一条伪响应(服务器未响应);
503:Service Unavailable,服务器此时无法提供服务,但将来可能可用;
<reason-phrase:>原因短语,明确说明请求失败了,或是成功的方式。比如请求成功会返回200,数字状态易度信息。
<entity-body>:主体部分。
5>.HTTP请求:request。
HTTP的请求报文式:
<method><request-URL><version> #第一行,指定HTTP方法,请求的资源路径,因为常用的版本有1.0和1.1两个版本(两种版本的使用特性是不一样的),因此需要指定版本。
<headers> #第二行,需要加一些请求首部,然后需要紧跟两个空白行。
#第三行和第四行都是空格,需要空出来。
<entity-body> #第五行,请求的主体。上面两行是空行哟~也就是第三行和第四行都是空着的。
1 GET /domains/example/ HTTP/1.1 #请求行:指定请求方法 请求URI HTTP 协议/协议版本 2 Host:www.yinzhengjie.org.cn #服务端的主机名 3 User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) 4 Chrome/22.0.1229.94 Safari/537.4 #浏览器信息 5 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 #客户端能接收的mine 6 Accept-Encoding:gzip,deflate,sdch #是否支持流压缩 7 Accept-Charset:UTF-8,*;q=0.5 #客户端字符编码集 8 #空行,用于分割请求头和消息体 9 #消息体,请求资源参数,例如 POST 传递的参数
5>.HTTP响应:resonse。
HTTP的响应报文:
<version><status><reason-phrase> #第一行,告诉客户端用哪个http版本响应客户端,然后紧跟的是返回客户端的状态码,再跟着是原因短语。
<headers> #第二行,跟着是服务端的首部。
#第三行和第四行都是空格,需要空出来。
<entity-body> #第五行,跟的是响应的主体。
1 HTTP/1.1 200 OK //状态行 2 Server: nginx/1.0.8 //服务器使用的 WEB 软件名及版本 3 Date:Date: Tue, 30 Oct 2012 04:14:25 GMT //发送时间 4 Content-Type: text/html //服务器发送信息的类型 5 Transfer-Encoding: chunked //表示发送HTTP 包是分段发的 6 Connection: keep-alive //保持连接状态 7 Content-Length: 90 //主体内容长度 8 // 空行 用来分割消息头和主体 9 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"... //消息体
十一.HTTP事物延迟
http协议是一种stateless(无状态)协议。无状态就是指服务器端并不知道两次请求(即便是同一个客户端)是来自同一个客户端的,它没法记录上一次连接的身份信息。正是基于这种机制,http是无连接的。它还体现在一次事物结束后,连接即行断开。我们知道HTTP是面向连接的,也就是tcp套接字协议。这也就意味着每次客户端和服务端发生一次事物就会进行一次三次握手和四次断开,如果请求的数据的相对较多的话,频繁的三次握手和四次断开会导致效率很低,用户体验度也是很差的。为了解决这种问题目前有两种比较流行的加速方式:
第一,并行求情,客户端多线程(浏览器第一次请求一个URL交个服务器端,服务器基于相应,浏览器拿到了服务器响应的数据,浏览器会自动进行分析,如果发现服务端相应的内容还有其他的资源的话就会自动开启多线程去下载这些资源到本地。)
第二,持久连接(设置超时时间或是最大资源限制,同一个客户端和服务端进行连接之后,三次握手之后不断开,直到把所有的数据都请求完毕后再断开。这意味着不用频繁的进行三次握手和四次断开了,但是这并不意味着都是好处哟~比如对应非常繁忙的web服务器,最佳接收连接的数是20w,但是目前有100w个连接需要同时连接,那么剩下的80w连接用户都得乖乖等着。所以说,在设置超时时间和最大请求资源限制的优化还得运维仔细琢磨。)
十二.基于TCP客户/服务器程序的套接字函数
![](https://i-blog.csdnimg.cn/blog_migrate/9edd944a98490dfd6fbe8d0ce3424b0a.png)
十三.基于web服务请求步骤
一次Web请求的基本过程大致为:建立连接--->接收请求--->处理请求--->访问资源--->构建响应--->发送响应--->记录日志,大致流程图如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/8ae1585e743c3843b1e514325489fbac.png)
我们在Web请求的第二步(接收请求)开始分析,Web服务器输入/输出结构大致分为三类:
a>.单线程I/O结构;
在这种场景下,一次只能接受用户一个请求进来,其他的用户请求都必须等待(排队),假如web服务器处理一个用户的请求需要0.1秒,那意味着1秒钟只能处理10个请求。这样的效率是非常低效率的。
b>.多线程I/O结构;
在这种情况下,主进程对用户的每一个请求都创建一个子进程处理,等待子进程处理完用户请求之后告诉主进程,主进程会再一次收回该子进程的内存空间。通过这种方式可以实现多用户请求(注意,一个用户不代表只有一个请求,大家都知道浏览器目前都是可以支持多进程请求的。),比第一种效率要高很多,但与此同时也带来了很多问题,比如主进程不断的创建子进程会导致应用程序占用内存空间过大,从而可能影响到服务器自身性能。
c>.复用的I/O结构
在这种情况下,单个进程可用响应多个请求。如果你去过快餐店(比如:田老师)吃过饭的话,你去叫餐,会发现他们有一个比其他饭店独有的特色,没错,就是饭来的特别快。而我们可以清楚的看到有一个人来负责接待客户,然后把票递给厨房的工作人员,之后负责接待客户的就告诉你稍定一下,正在为你取餐,然后又立马对下一位客户说:您好,请问想吃点什么?这就很像我们的说的复用的I/O结构。
d>.复用的多线程I/O结构
多个线程,每个线程多个请求。我们可以理解是把“复用的I/O结构”启动了多个,这样的响应效果明显又会提升不少。