地址解析, 如用客户端浏览器请求这个页面:http://localhost.com:8080/index.htm 从中分解出协议名、主机名、端口、对象路径等部分,对于我们的这个地址,解析得到的结果如下: 协议名:http 主机名:localhost.com 端口:8080 对象路径:/index.htm 在这一步,需要域名系统DNS解析域名localhost.com,得主机的IP地址。
封装HTTP请求数据包 把以上部分结合本机自己的信息,封装成一个HTTP请求数据包
封装成TCP包,建立TCP连接(TCP的三次握手) 在HTTP工作开始之前,客户机(Web浏览器)首先要通过网络与服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能,才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。这里是8080端口
客户机发送请求命令 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可内容。
服务器响应 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。 实体消息是服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据
服务器关闭TCP连接 一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码Connection:keep-alive TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
请求 URI 定位资源 我们在浏览器中输入一个地址,浏览器是如何根据地址去找到服务器对应的资源并做返回的?以及这个地址包含了哪些有价值的信息呢? 这就需要我们了解 URL (Uniform Resource Locator),统一资源定位符 ,用于描述一个网络上的资源,具体格式是 http://www.baidu.com:80/java/index.html?name=mic#head schema://host[:port#]/path/…/?[url-params]#[ query-string] scheme 指定应用层使用的协议(例如:http, https, ftp) host HTTP 服务器的 IP 地址或者域名 port# HTTP 服务器的默认端口是 80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/ path 访问资源的路径 query-string 查询字符串片段标识符(使用片段标识符通常可标记出已获取资源中的子资源(文档内的某个位置)) 通过这个 url 地址,我们就可以读到,当前用户要使用 http 协议访问指定服务器上对应进程中的资源,并且携带了请求参数。
MIME Type 服务器根据用户请求的资源找到对应的文件以后,会返回一个资源给到客户端浏览器,浏览器会对这个资源解析并且渲染。但是服务器上的资源类型有很多,比如图片类型、视频类型、Js、Css、文本等。浏览器如何识别当前类型做不同的渲染呢? MIME Type:是描述消息内容类型的因特网标准,常见的几种类型 文本文件:text/html,text/plain,text/css,application/xhtml+xml,application/xml 图片文件:image/jpeg,image/gif,image/png. 视频文件:video/mpeg,video/quicktime 我们可以通过两种方式来设置文件的渲染类型,第一种是 Accept,第二种是 Content-Type Accept: 表示客户端希望接受的数据类型,即告诉服务器我需要什么媒体类型的数据,此时服务器应该根据 Accept 请求头生产指定媒体类型的数据 Content-Type: 表示发送端发送的实体数据类型 , 比如我们应该写过类似的 :resposne.setContentType(“application/json;charset=utf-8”)的代码,表示服务端返回的数据格式是 json。 如果 Accept 和 Content-Type 不一致,假如说 Accept 要接收的类型是 image/gif,但是服务端返回的数据是 text/html,那么浏览器将会无法解析。
状态码 状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值: 1xx:指示信息–表示请求已接收,继续处理 2xx:成功–表示请求已被成功接收、理解、接受 3xx:重定向–要完成请求必须进行更进一步的操作 4xx:客户端错误–请求有语法错误或请求无法实现 5xx:服务器端错误–服务器未能实现合法的请求 常见状态代码、状态描述、说明: 200 OK //客户端请求成功 400 Bad Request //客户端请求有语法错误,不能被服务器所理解 401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用 403 Forbidden //服务器收到请求,但是拒绝提供服务 404 Not Found //请求资源不存在,eg:输入了错误的URL 500 Internal Server Error //服务器发生不可预期的错误 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常
告诉服务器端当前请求的意图 有了 url,mimetype、状态码, 能够基本满足用户的需求,但是,很多时候一个网站不单纯只是不断从服务端获取资源并做渲染,可能还需要做一些数据的提交、删除等功能。所以浏览器定义了 8 种方法来表示对于不同请求的操作方式,当然最常用的还是 Get 和 Post,我觉得,要不是 Get 方法不支持大数据的传输,估计很多同学 Post 都不会去使用。 GET:一般是用于客户端发送一个 URI 地址去获取服务端的资源(一般用于查询操作),Get不支持的传输数据有限制,具体限制由浏览器决定 POST:一般用户客户端传输一个实体给到服务端,让服务端去保存(一般用于创建操作) PUT:向服务器发送数据,一般用于更新数据的操作 DELETE:客户端发起一个 Delete 请求要求服务端把某个数据删除(一般用于删除操作) HEAD:获得报文首部 OPTIONS:询问支持的方法 TRACE:追踪路径 CONNECT:用隧道协议连接代理
在 REST 架构风格中,有严格规定对于不同的请求类型要设置合适的请求方法。也是避免出 现因为乱用导致混乱的问题。这里提到了 REST 架构,现在很多同学都在写 REST,有没有人 能够明白为什么要定义 REST 这个架构风格?
我个人认为是这样
随着服务化架构的普及,http 协议的使用频率越来越高很多人在错误的使用 http 协议定义接口,比如各种各样的命名,什么 getUserInfoById,deleteById 之类的、有状态和无状态请求混用。对于 http 协议本身提供的规则并没有很好的利用 所以,为了更好的解决这些问题,干脆就定义一套规则,这套规则并没有引入新的东西,无非就是对 http 协议本身的使用做了一些约束,比如说REST 是面向资源,每一个 URI 代表一个资源强调无状态化,服务器端不能存储来自某个客户的某个请求中的信息,并在该客户的其他请求中使用强调 URL 暴露资源时,不要在 URI 中出现动词合理的利用 http 状态码、请求方法。请求报文
响应报文
由于 HTTP 协议在通信过程中,是基于明文通信,并且底层是基于 TCP/IP 协议进行通信,那么按照 TCP/IP 协议族的工作机制,通信内容在所有的通信线路上都有可能遭到拦截和窃取。窃取这个过程其实很简单,通过抓包工具 Wireshark 就可以截获请求和响应的内容。
由于 HTTP 协议通信的不安全性,所以人们为了防止信息在传输过程中遭到泄漏或者篡改,就想出来对传输通道进行加密的方式 https。https 是一种加密的超文本传输协议,它与 HTTP 在协议差异在于对数据传输的过程中,https对数据做了完全加密。由于 http 协议或者 https 协议都是处于 TCP 传输层之上,同时网络协议又是一个分层的结构,所以在 tcp 协议层之上增加了一层 SSL(Secure Socket Layer,安全层)或者 TLS(Transport Layer Security) 安全层传输协议组合使用用于构造加密通道;
从第一个消息开始 客户端 A 向服务端 B 发送一条消息,这个消息可能会被拦截以及篡改,我们如何做到 A 发送给 B 的数据包,及时被拦截了,也没办法得知消息内容并且也不能查看呢?
利用对称加密 要做到消息不能被第三方查看以及篡改,那么第一想法就是对内容进行加密,同时,该消息还需要能被服务端进行解密。所以我们可以使用对称加密算法来实现,密钥 S 扮演着加密和解密的角色。在密钥 S 不公开的情况下,就可以保证安全性?
协商过程又是不安全的 在互联网世界,通信不会这么简单,也许是这样。
会存在多个客户端和服务端产生连接,而这个客户端也许是一个潜伏者,如果他也有对称密钥 S,那相当于上面的方案是不可行的?如果服务端和每个客户端通信的时候使用不同的加密算法呢? 似乎能够完美解决问题,然后?密钥如何分配呢?也就是服务端怎么告诉客户端该使用那种对称加密算法呢?解决办法似乎只能通过建立会话以后进行协商了?
非对称加密出马 非对称加密算法的特点是:私钥加密后的密文,只要有公钥,都能解密,但是公钥加密后的密文,只有私钥可以解密。私钥只有一个人有,而公钥可以发给所有人这样就可以保证 A/B 向服务器端方向发送的消息是安全的。似乎我们通过非对称加密算法解决了密钥的协商的问题?
公钥怎么拿? 使用非对称加密算法,那么如何让 A、B 客户端安全地持有公钥? 那么我们逐步思考,有两种我们能想到的方案: 服务器端将公钥发送给每一个客户端服务器端将公钥放到一个远程服务器,客户端可以请求到 (多了一次请求,还得解决公钥放置问题) 方案一似乎不可行,因为,传输过程又是不安全的?公钥可能会被调包引入第三方机构 到上面这一步,最关键的问题是,客户端如何知道给我公钥的是黄蓉还是小龙女?只能找本人去证实?或者有一个第三者来帮你证实,并且第三者是绝对公正的。所以,引入一个可信任的第三者是一个好的方案服务端把需要传递给客户端的公钥,通过第三方机构提供的私钥对公钥内容进行加密后,再传递给客户端? 通过第三方机构私钥对服务端公钥加密以后的内容,就是一个简陋版本的“数字证书”。这个帧数中包含【服务器公钥】 客户端拿到这个证书以后,因为证书是第三方机构使用私钥加密的。客户端必须要有第三方机构提供的公钥才能解密证书。这块又涉及到第三方机构的公钥怎么传输?(假设是先内置在系统中)以及还有一个问题,第三方机构颁发的证书是面向所有用户,不会只针对一家发放。如果不法分子也去申请一个证书呢?
如果不法分子也拿到证书? 如果不法分子也申请了证书,那它可以对证书进行调包。客户端在这种情况下是无法分辨出收到的是你的证书,还是中间人的。因为不论是中间人的、还是你的证书都能使用第三方机构的公钥进行解密。
验证证书的有效性 事情发展到现在,问题演变成了,客户端如何识别证书的真伪?在现实生活中,要验证一个东西的真伪,绝大部分都是基于编号去验证(比如大学毕业证书,比如买的数码产品是否是山寨),我之前讲过,计算机领域的解决方案都是人为去实现的,所以在这里,解决方案也是一样,如果给这个数字证书添加一个证书编号?是不是就能达到目的呢?证书上写了如何根据证书的内容生成证书编号。客户端拿到证书后根据证书上的方法自己生成一个证书编号,如果生成的证书编号与证书上的证书编号相同,那么说明这个证书是真实的。这块有点类似于 md5 的验证,我们下载一个软件包,都会提供一个 md5 的值,我们可以拿到这个软件包以后通过一个第三方软件去生成一个 md5 值去做比较,是不是一样如果一样表示这个软件包没被篡改过
第三方机构的公钥证书存哪里? 浏览器和操作系统都会维护一个权威的第三方机构列表(包括他们的公钥)因为客户端接收到的证书中会些颁发机构,客户端就根据这个办法机构的值在本地找到响应的公钥说到这里,我想大家一定知道,证书就是HTTPS 中的数字证书,证书编号就是数字签名,而第三方机构就是数字证书的签发机构(CA)