HTTP是一个应用层协议,主要用于Web开发,通常由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如"HTTP/1.1 200 OK",以及返回的内容,如请求的文件、错误消息、或者其它信息。
HTTP是一个无状态
的协议,也就是说服务器不会去维护与客户交互的相关信息,因此它对于事务处理没有记忆能力。举个例子来讲,你通过服务器认证后成功请求了一个资源,紧接着再次请求这一资源时,服务器仍旧会要求你表明身份。
无状态不代表HTTP不能保持TCP连接,更不能代表HTTP使用的是UDP协议(无连接)。HTTP协议中,并没有规定它支持的层。事实上,HTTP可以在任何互联网协议上,或其他网络上实现。HTTP假定其下层协议提供可靠的传输,因此,任何能够提供这种保证的协议都可以被其使用,在TCP/IP协议族使用TCP作为其传输层,其在TCP/IP四层网络模型中的位置如下图所示:
HTTP 协议基础
HTTP 请求
http请求由三部分组成,分别是:请求行、消息报头、请求正文
。
请求行
请求行的格式如下:
Method SP Request-URI SP HTTP-Version CRLF
请求行以一个方法符号开头,后面跟着请求的URI和协议的版本,中间以空格隔开。其中
- Method指出在由Request-URI标识的资源上所执行的方法,方法是大小写敏感的;
- Request-URI是一个统一资源标识符(通过简单的格式化字符串,通过名称、位置、或其他任何特性标识某个资源)。
- HTTP-Version 表示请求的HTTP协议版本;
- CRLF表示回车和换行(除了作为结尾的 CRLF 外,不允许出现单独的 CR 或 LF 字符)
常用的请求方法如下:
方法名称 | 含义 |
---|---|
GET | 获取由Request-URI标识的任何信息(以实体的形式),如果Request-URI引用某个数据处理过程,则应该以它产生的数据作为在响应中的实体,而不是该过程的源代码文本,除非该过程碰巧输出该文本。 |
POST | 用来请求原始服务器接受请求中封装的实体作为请求行中的Request-URI标识的副属。POST主要用于向数据处理过程提供数据块,如递交表单或者是通过追加操作来扩展数据库。 |
PUT | 以提供的Request-URI存储封装的实体。 |
DELETE | 请求原始服务器删除Request-URI标识的资源。 |
HEAD | 除了服务器不能在响应中返回消息体,HEAD方法与GET相同。用来获取暗示实体的元信息,而不需要传输实体本身。常用于测试超文本链接的有效性、可用性和最近的修改。 |
简答例子如下:
GET /index.html HTTP/1.1
POST http://192.168.2.217:8080/index.jsp HTTP/1.1
消息报头
报头域是由名字+“:”+空格+值组成,消息报头域的名字是大小写无关的。请求消息报头包含了普通报头、请求报头、实体报头
。
普通报头
用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息。比如:
- Cache-Control:用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制);
- Date:表示消息产生的日期和时间;
- Connection:允许发送指定连接的选项,例如指定连接是连续,或者指定“close”选项,通知服务器在响应完成后关闭连接。
请求报头
允许客户端向服务器端传递请求的附加信息以及客户端自身的信息。常用的请求报头如下:
- Host:指定被请求资源的 Internet 主机和端口号,它通常是从HTTP URL中提取出来的;
- User-Agent:允许客户端将它的操作系统、浏览器和其它属性告诉服务器;
- Accept:指定客户端接受哪些类型的信息,eg:Accept:image/gif,表明客户端希望接受GIF图象格式的资源;
- Accept-Charset:指定客户端接受的字符集,缺省是任何字符集都可以接受;
- Accept-Encoding:指定可接受的内容编码,缺省是各种内容编码都可以接受;
- Authorization:证明客户端有权查看某个资源,当浏览器访问一个页面,如果收到服务器的响应代码为401(未授权),可以发送一个包含Authorization请求报头域的请求,要求服务器对其进行验证。
实体报头
定义了关于实体正文(eg:有无实体正文)和请求所标识的资源的元信息。常用的实体报头如下:
- Allow:GET,POST
- Content-Encoding:文档的编码(Encode)方法,例如:gzip;
- Content-Language:内容的语言类型,例如:zh-cn;
- Content-Length:表示内容长度,eg:80
请求正文
//TODO
HTTP 响应
在接收和解释请求消息后,服务器返回一个 HTTP 响应消息。HTTP 响应也是由三个部分组成,分别是:状态行、消息报头、响应正文
。
状态行
所有HTTP响应的第一行都是状态行,依次是当前HTTP版本号,3位数字组成的状态代码,以及描述状态的短语,彼此由空格分隔。
HTTP-Version Status-Code Reason-Phrase CRLF
HTTP状态码的作用:Web服务器用来告诉客户端,发生了什么事。状态代码的第一个数字代表当前响应的类型:
- 1xx消息——请求已被服务器接收,继续处理
- 2xx成功——请求已成功被服务器接收、理解、接受
- 3xx重定向——需要后续操作才能完成这一请求
- 4xx客户端错误——请求含有词法错误或者无法被执行
- 5xx服务器错误——服务器在处理某个正确请求时发生错误
虽然 RFC 2616 中已经推荐了描述状态的短语,例如"200 OK","404 Not Found"(“状态消息”更便于人理解)。但是WEB开发者仍然能够自行决定采用何种短语,用以显示本地化的状态描述或者自定义信息。常见的状态码有如下:
- 200 OK 服务器成功处理了请求;
- 206 Partial Content(部分内容)代表服务器已经成功处理了部分GET请求(只有发送GET 方法的request, web服务器才可能返回206)
- 301 Moved Permanently(永久重定向)请求的URL已移走。Response中应该包含一个Location URL, 说明资源现在所处的位置
- 302 Moved Temporarily(临时重定向)
- 304 Not Modified(未修改)客户的缓存资源是最新的,要客户端使用缓存
- 400 Bad Request(坏请求)告诉客户端,它发送了一个错误的请求。
- 401 Unauthorized(未授权)需要客户端对自己认证
- 404 Not Found 未找到资源
- 500 Internal Server Error 服务器遇到一个错误,使其无法对请求提供服务
消息报头
响应消息报头包含了普通报头、响应报头、实体报头,普通报头和实体报头和请求消息报头中的普通报头、实体报头相同。
响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和 对 Request-URI 所标识的资源进行下一步访问的信息。常用的响应报头如下:
- Location:用于重定向接受者到一个新的位置,Location响应报头域常用在更换域名的时候;
- Server:包含了服务器用来处理请求的软件信息,与User-Agent请求报头域是相对应的;
- WWW-Authenticate:必须被包含在401(未授权的)响应消息中。
响应正文
消息正文类似HTTP请求的消息正文。
HTTP协议之Get和Post
Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET, POST, PUT, DELETE。一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查,改,增,删4个操作。我们最常见的就是GET和POST了。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息,主要区别如下:
- GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditPosts.aspx?name=test1&id=123456。POST方法是把提交的数据放在HTTP包的Body中。
- GET提交的数据大小有限制(因为
浏览器对URL的长度有限制
,实际上HTTP协议规范没有对URL长度进行限制),而POST方法提交的数据没有限制。 - GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值,也就是说Get是通过地址栏来传值,而Post是通过提交表单来传值。
- 对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
- GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码。
- GET在浏览器回退时是无害的,而POST会再次提交请求。GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
Http不同版本区别
HTTP/1.0版本,1.1版本主要区别如下:
-
带宽优化
HTTP/1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了。HTTP/1.1中在请求消息中引入了range头域,它允许只请求资源的某个部分。
另外一种情况是请求消息中如果包含比较大的实体内容,但不确定服务器是否能够接收该请求(如是否有权限),此时若贸然发出带实体的请求,如果被拒绝也会浪费带宽。HTTP/1.1加入了一个新的状态码100(Continue),客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码401(Unauthorized);如果服务器接收此请求就回送响应码100,客户端就可以继续发送带实体的完整请求了。
-
长连接
HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。由于大多数网页的流量都比较小,一次TCP连接很少能通过slow-start区,不利于提高带宽利用率。
HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。
HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间。
-
缓存
在HTTP/1.0 中,使用
Expire头域
来判断资源的fresh或stale,并使用条件请求来判断资源是否仍有效。例如,cache通过If-Modified-Since头域向服务器验证资源的Last-Modefied头域是否有更新,源服务器可能返回304(Not Modified),则表明该对象仍有效;也可能返回200(OK)替换请求的Cache对象。HTTP/1.1在1.0的基础上加入了一些cache的新特性,当缓存对象的Age超过Expire时变为stale对象,cache不需要直接抛弃stale对象,而是与源服务器进行重新激活(revalidation)。
-
Host头域
在 HTTP1.0 中认为每台服务器都绑定一个唯一的IP地址,因此请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都支持Host头域,请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
-
错误提示
HTTP/1.0 中只定义了16个状态响应码,对错误或警告的提示不够具体。HTTP/1.1 引入了一个Warning头域,增加对错误或警告信息的描述。
此外,在HTTP/1.1中新增了24个状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
HTTP 高级内容
Cookie与Session
Cookie 和 Session 都为了用来保存状态信息,都是保存客户端状态的机制,它们都是为了解决HTTP无状态的问题而所做的努力。
Cookie 机制
简单地说,cookie 就是浏览器储存在用户电脑上的一小段文本文件。cookie 是纯文本格式,不包含任何可执行的代码。一个 Web 页面或服务器告知浏览器按照一定规范来储存这些信息,并在随后的请求中将这些信息发送至服务器,Web 服务器就可以使用这些信息来识别不同的用户。大多数需要登录的网站在用户验证成功之后都会设置一个 cookie,只要这个 cookie 存在并可以,用户就可以自由浏览这个网站的任意页面。
cookie 会被浏览器自动删除,通常存在以下几种原因:
- 会话 cooke (Session cookie) 在会话结束时(浏览器关闭)会被删除
- 持久化 cookie(Persistent cookie)在到达失效日期时会被删除
- 如果浏览器中的 cookie 数量达到限制,那么 cookie 会被删除以为新建的 cookie 创建空间。
大多数浏览器支持最大为 4096 字节的 Cookie。由于这限制了 Cookie 的大小,最好用 Cookie 来存储少量数据,或者存储用户 ID 之类的标识符。用户 ID 随后便可用于标识用户,以及从数据库或其他数据源中读取用户信息。 浏览器还限制站点可以在用户计算机上存储的 Cookie 的数量。大多数浏览器只允许每个站点存储 20 个 Cookie;如果试图存储更多 Cookie,则最旧的 Cookie 便会被丢弃。有些浏览器还会对它们将接受的来自所有站点的 Cookie 总数作出绝对限制,通常为 300 个。
使用 Cookie 的缺点:
- 不良站点用 Cookie 收集用户隐私信息;
- Cookie窃取:黑客以可以通过窃取用户的cookie来模拟用户的请求行为。(跨站脚本攻击XSS)
Session 机制
Session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识(session id):
- 如果已包含一个session id 则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session 检索出来使用(如果检索不到,可能会新建一个)。
- 如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。
具体实现方式:
Cookie方式
:服务器给每个Session分配一个唯一的JSESSIONID,并通过Cookie发送给客户端。当客户端发起新的请求的时候,将在Cookie头中携带这个JSESSIONID,这样服务器能够找到这个客户端对应的Session。URL回写
:服务器在发送给浏览器页面的所有链接中都携带JSESSIONID的参数,这样客户端点击任何一个链接都会把JSESSIONID带回服务器。如果直接在浏览器输入服务端资源的url来请求该资源,那么Session是匹配不到的。
Web 缓存
WEB缓存(cache)位于Web服务器和客户端之间,缓存机制会根据请求保存输出内容的副本,例如html页面,图片,文件,当下一个请求来到的时候:如果是相同的URL,缓存直接使用副本响应访问请求,而不是向源服务器再次发送请求。
有缓存的 Get 请求过程如下:
主要分三种情况:
-
未找到缓存(黑色线):当没有找到缓存时,说明本地并没有这些数据,这种情况一般发生在我们首次访问网站,或者以前访问过,但是清除过缓存后。浏览器就会先访问服务器,然后把服务器上的内容取回来,内容取回来以后,就要根据情况来决定是否要保留到缓存中了。
-
缓存未过期(蓝色线):缓存未过期,指的是本地缓存没有过期,不需要访问服务器了,直接就可以拿本地的缓存作为响应在本地使用了。这样节省了不少网络成本,提高了用户体验过。
-
缓存已过期(红色线):当满足过期的条件时,会向服务器发送请求,发送的请求一般都会进行一个验证,目的是虽然缓存文档过期了,但是文档内容不一定会有什么改变,所以服务器返回的也许是一个新的文档,这时候的HTTP状态码是200,或者返回的只是一个最新的时间戳和304状态码。
缓存过期后,有两种方法来判定服务端的文件有没有更新。第一种在上一次服务端告诉客户端约定的有效期的同时,告诉客户端该文件最后修改的时间,当再次试图从服务端下载该文件的时候,check下该文件有没有更新(对比最后修改时间),如果没有,则读取缓存;第二种方式是在上一次服务端告诉客户端约定有效期的同时,同时告诉客户端该文件的版本号,当服务端文件更新的时候,改变版本号,再次发送请求的时候check一下版本号是否一致就行了,如一致,则可直接读取缓存。
浏览器是依靠请求和响应中的的头信息来控制缓存的,如下:
-
Expires与Cache-Control:服务端用来约定和客户端的有效时间的。Expires规定了缓存失效时间(Date为当前时间),而Cache-Control的max-age规定了缓存有效时间(2552s)。Expires是HTTP1.0的东西,而Cache-Control是HTTP1.1的,规定如果max-age和Expires同时存在,前者优先级高于后者。
-
Last-Modified/If-Modified-Since:缓存过期后,check服务端文件是否更新的第一种方式。
-
ETag/If-None-Match:缓存过期时check服务端文件是否更新的第二种方式。实际上ETag并不是文件的版本号,而是一串可以代表该文件唯一的字符串,当客户端发现和服务器约定的直接读取缓存的时间过了,就在请求中发送If-None-Match选项,值即为上次请求后响应头的ETag值,该值在服务端和服务端代表该文件唯一的字符串对比(如果服务端该文件改变了,该值就会变),如果相同,则相应HTTP304,客户端直接读取缓存,如果不相同,HTTP200,下载正确的数据,更新ETag值。
当然并不是所有请求都能被缓存。无法被浏览器缓存的请求:
- HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache(HTTP1.0),或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
- 需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
- POST请求无法被缓存
浏览器缓存过程还和用户行为有关。譬如先打开一个主页有个jquery的请求(假设访问后会缓存下来)。接着如果直接在地址栏输入 jquery 地址,然后回车,响应HTTP200(from cache),因为有效期还没过直接读取的缓存;如果ctrl+r进行刷新,则会相应HTTP304(Not Modified),虽然还是读取的本地缓存,但是多了一次服务端的请求;而如果是ctrl+shift+r强刷,则会直接从服务器下载新的文件,响应HTTP200。
HTTP 代理
Web代理(proxy)服务器是网络的中间实体。代理位于Web客户端和Web服务器之间,扮演“中间人”的角色。HTTP的代理服务器即是Web服务器又是Web客户端。(Fiddler 是以代理web服务器的形式工作的,它使用代理地址:127.0.0.1, 端口:8888. 当Fiddler退出的时候它会自动注销代理,这样就不会影响别的程序。)
代理服务器有许多用处:
- 跨过网络障碍。翻墙技术:局域网不能上网,只能通过局域网内的一台代理服务器上网。
- 匿名访问。HTTP代理服务器通过删除HTTP报文中的身份特性(比如客户端的IP地址,或cookie,或URI的会话ID),从而对远端服务器隐藏原始用户的IP地址以及其他细节。同时HTTP代理服务器上也不会记录原始用户访问记录的log。
- 通过代理缓存,加快上网速度。大部分代理服务器都具有缓存的功能,不断将新取得数据存储到它本地的存储器上,如果浏览器所请求的数据在它本机的存储器上已经存在而且是最新的,那么直接将存储器上的数据传给用户,这样就能显著提高浏览速度。
- 过滤指定内容。比如儿童过滤器,很多教育机构,会利用代理来阻止学生访问成人内容。
代理服务器和抓包工具(比如Fiddler)都能看到http request中的数据。如果我们发送的request中有敏感数据,比如用户名,密码,信用卡号码,就会被代理服务器看到。所以我们一般都是用HTTPS来加密Http request。