HTTP基础


title: HTTP基础

date: 2019-08-21 14:53:32

tags: HTML

categories:  # 这里写的分类会自动汇集到 categories 页面上,分类可以多级

  • HTML # 一级分类
  • HTTP基础 # 二级分类

ajax

ajax 是一种技术方案,核心依赖浏览器提供的 XMLHttpRequest 对象,是这个对象使得浏览器可以发出 http 请求与接受响应。

如何与后端交互

  1. form 表单提交.缺点会跳转,无后台反馈
  2. ajax
  3. websocket

实现方法

  1. XMLHttpRequest 对象(该对象支持同步和异步请求)
  2. fetch

范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var xhr = new XMLHttpRequst()
//true默认是异步的方式,false是同步的方式
xhr.open("get", "/xxx.com", true)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
//成功
console.log(xhr.responseText)
} else {
console.log("服务器异常")
}
}
//发送请求
xhr.send()

Fetch

fetch 基于 promise

当接受到错误状态码时,conffetch()返回的 promise 不会标记为 reject

默认情况下,fetch 不会从服务端发送或接收任何 cookies.

API

1
2
3
4
5
6
7
8
9
10
11
12
13
1. let xhr = new XMLHttpRequest();
2. xhr.open('GET','/xxx') //初始化xhr
3. xhr.send() //发送请求
4. xhr.onreadystatechange //监听请求状态的变化<readystatechange>是一个事件
5. xhr.readyState === 1 //xhr.open()已经完成
6. xhr.readyState === 2 //xhr.send()已经完成
7. xhr.readyState === 3 //xhr.responseText正在下载
5. xhr.readyState === 4 //响应完成
6. xhr.status //HTTP状态码
7. var string = xhr.responseText //响应的内容
8. var value = JSON.parse(xhr.responseText) //把符合JSON语法的字符串转换成JS,解析响应返回的内容
9. value.node //若value是对象,这就是对象里内容
10. value.node.name //对象里的name的值

post形式需要将数据放入到 send 中

跨域

同源策略

同协议,域名,端口

实现跨域的方法

JSONP

JSON with padding

通过 script 标签加载数据的方式获取数据当做 js 代码来执行。jsonp 需要对应接口的后端配合才可以实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function addScriptTag(src) {
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.src = src;
document.body.appendChild(script);
}

window.onload = function () {
addScriptTag("http://example.com/ip?callback=foo");
};

function foo(data) {
console.log("Your public IP address is: " + data.ip);
}

上面代码通过动态添加<script>元素,向服务器 example.com 发出请求。注意,该请求的查询字符串有一个 callback 参数,用来指定回调函数的名字,这对于 JSONP 是必需的。

服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

1
2
3
foo({
ip: "8.8.8.8",
});

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了 foo 函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用JSON.parse的步骤。

必考面试题:JSONP 为什么不支持 POST 请求

答:因为 JSONP 是通过动态创建 script 的方法进行的,而 script 只能发送 get 请求不能发送 post 请求。

jquery 的写法

1
2
3
4
5
6
7
8
$.ajax({
url:"http://jack.com:8002/pay",
dataType: "jsonp",
success:function (response) {
console.log(response)
}
}
})

axios 写法

1
2
3
4
5
6
7
8
9
this.$http = axios;
this.$http
.jsonp("http://www.domain2.com:8080/login", {
params: {},
jsonp: "handleCallback",
})
.then((res) => {
console.log(res);
});

CORS

跨域资源共享,是一种 ajax 跨域请求资源的方式。

写法就是普通的 ajax 写法.不过在浏览器请求上加点处理.而且是浏览器自动处理的.

当使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给请求加一个请求头:Origin

后台进行处理,如果确定接受请求,则在返回结果中加响应头:Access-Control-Allow-Origin

浏览器判断该响应头中是否包含Origin的值,如果有,浏览器会处理响应,就可以拿到响应数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open("post", "http://www.domain2.com:8080/login", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("user=admin");

xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};

postMessage

  1. A 页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
componentDidMount() {
window.addEventListener('message', this.receiveMessage, false)
const iframeImg = this.iframe.contentWindow
iframeImg.onload = function() {
iframeImg.postMessage('message', 'http://www.blogoog.com:8088')
}
}
receiveMessage = (event) =>{
const origin = event.origin || event.originalEvent.origin
const thisData = JSON.parse(event.data)
const imgUrl = Object.values(thisData)
this.setState({ imgUrl })
setTimeout(() =>{
this.closeIframe();
message.success('上传成功')
},1000)
if (origin !== 'http://www.blogoog.com:80881) {
window.location.hash = '#/exception/404'
}
}
1
2
3
4
5
6
7
8
9
10
11
12
<iframe
className={styles.iframeWrap}
src="http://www.blogoog.com:8088"
name="imgIframe"
frameBorder="0"
scrolling="auto"
ref={(dom) => {
this.iframe = dom;
}}
>
您的浏览器不支持iframe
</iframe>
  1. A 页面使用的语法

window.postMessage()

otherWindow.postMessage(message, targetOrigin, [transfer])

  • otherWindow:其他窗口的一个引用(在这里我使用了 iframe 的 contentWindow 属性)
  • message:将要发送到其他 window 的数据(可以不受限制的将数据对象安全的传送给目标窗口而无需自己序列化,原因是因为采用了结构化克隆算法)
  • targetOrigin:接收信息的 URL(在这里我当然填的 B 页面的 URL)
  • transfer:可选参数

window.addEventListener(‘message’, receiveMessage, false)

target.addEventListener(type, listener, options)

  • type:表示监听事件类型的字符串
  • listener:当所监听的事件类型触发时,会通知的一个对象或者一个函数
  • potions:可选参数(在此我用 false,表示在 listener 被调用之后不会自动移除)

receiveMessage = (event) => {}

  • event.data:从另一个 window 传递过来的对象(包含传递过来的所有信息)
  • event.origin||event.originalEvent.origin:window.postMessage()发送消息的目标 URL
  • event.source:对发送消息的窗口对象的引用

注意点!!!

  • 在页面内嵌入 iframe 页面的情况下,需要等到页面内的 iframe 页面,也就是 B 页面加载完成之后,才能进行 postMessage 跨域通信
  • event.origin 中的 origin 不能保证是该窗口的当前 origin 或者未来 origin,因为 postMessage 被调用后,可能会被导航到不同的位置,所以需要做个异常情况判断处理origin !== 'http://www.blogoog.com:8088'
  1. B 页面
1
2
3
4
5
6
7
8
9
10
11
12
created() {
window.addEventListener{"message", this.receiveMessage, false)
},
receiveMessage(e) {
if (e origin === 'http://www.blogoog.com:8080' || e.origin === 'http://www.blogoog.com:8088') {
this.sendBtnShow = true
this.originSource = e.origin
this.magicBuildData = JSON.parse(e.data)
},
sendMsg() {
top.postMessage(JSON.stringify(data), 'http://www.blogoog.com:8000')
}
  1. B 页面使用到的语法

top.postMessage('data', 'http://www.blogoog.com:8000')

参考上面 A 页面的语法

为什么用 top 而不用 window 下面再讲

window.addEventListener('message', receiveMessage, false)

参考上面 A 页面的语法

receiveMessage = (event) => {}

参考上面 A 页面的语法

window.postMessage()中的 window 到底是啥?

始终是你需要通信的目标窗口

  • A 页面中:A 页面向 B 页面发送跨域信息,window 就是在 A 页面中嵌入的 iframe 指向的 B 页面的 window,即:iframe.contentWindow
  • B 页面中:B 页面想 A 页面发送跨域信息,window 就是 A 页面的 window,在这里因为 B 页面时嵌入到 A 页面中的,对于 B 页面来讲,window 就是 top 或者 parent

需要特别注意的坑

  1. 一定要等 A 页面嵌入的 B 页面加载完成之后,再进行 postMessage 跨域通信
  2. 一定要对 origin 做判断,去掉不是来自我们目标窗口的 origin,防止来自其他 origin 的攻击
  3. 着重注意 window.postMessage()中 window 的用法,明确目标窗口的 window

websocket 协议跨域

WebSocket protocol 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很好的实现。

原生 WebSocket API 使用起来不太方便,我们使用 Socket.io,它很好地封装了 webSocket 接口,提供了更简单、灵活的接口,也对不支持 webSocket 的浏览器提供了向下兼容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div>user input:<input type="text"></div>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');

// 连接成功处理
socket.on('connect', function() {
// 监听服务端消息
socket.on('message', function(msg) {
console.log('data from server: ---> ' + msg);
});

// 监听服务端关闭
socket.on('disconnect', function() {
console.log('Server socket has closed.');
});
});

document.getElementsByTagName('input')[0].onblur = function() {
socket.send(this.value);
};
</script>

后端 node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var http = require("http");
var socket = require("socket.io");

// 启http服务
var server = http.createServer(function (req, res) {
res.writeHead(200, {
"Content-type": "text/html",
});
res.end();
});

server.listen("8080");
console.log("Server is running at port 8080...");

// 监听socket连接
socket.listen(server).on("connection", function (client) {
// 接收信息
client.on("message", function (msg) {
client.send("hello:" + msg);
console.log("data from client: ---> " + msg);
});

// 断开处理
client.on("disconnect", function () {
console.log("Client socket has closed.");
});
});

标签

iframe

HTML 内联框架元素<iframe> 表示嵌套的 browsing context。它能够将另一个 HTML 页面嵌入到当前页面中。

1
2
3
4
5
6
7
8
<iframe
src="https://mdn-samples.mozilla.org/snippets/html/iframe-simple-contents.html"
title="iframe example 1"
width="400"
height="300"
>
<p>Your browser does not support iframes.</p>
</iframe>

canvas

标签定义图形,比如图表和其他图像。该标签基于 JavaScript 的绘图 API

source

定义多媒体资源 <video><audio>

command

定义命令按钮,比如单选按钮、复选框或按钮

figure

规定独立的流内容(图像、图表、照片、代码等等)

导航

section

定义文档中的节(section、区段)

HTTP 报文

HTTP 报文是在 HTTP 应用程序之间发送的数据块。这些数据块以一些文本形式的元信息开头,描述报文的内容及含义,后面跟着可选的数据部分

URI 和 URL

URI:统一资源标识符

URL:统一资源定位符

报文组成

  1. 对报文进行描述的起始行 —— start line
  2. 包含属性的首部块 —— header
  3. 可选的包含数据的主体部分 —— body

报文语法

  1. 请求报文:

向 web 服务器请求一个动作

1
2
3
4
<method><request-URL><version>
<headers>

<entity-body>
  1. 响应报文

把请求结果返回给客户端

1
2
3
4
<version><status><reason-phrase>
<headers>

<entity-body>

状态码

服务器和浏览器之间的约定

分类:

100-199 用于指定客户端应相应的某些动作。

200-299 用于表示请求成功。

300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。

400-499 用于指出客户端的错误。

500-599 用于支持服务器错误。

状态码 状态信息 解释
100 Continue 初始请求已接受,继续发送
101 switching Protocols 服务器遵从客户请求转换到另一种协议
200 OK 请求成功
201 Created 服务器已经创建了文档,Location 头给出了他的 URL
202 Accepted 已接受请求,但处理尚未完成
203 Non-Authoritative Information 文档已正常返回但一些应答头可能不正确,因为使用的是文档的拷贝
204 No Content 服务器接到请求已经处理完毕,但浏览器页面不会刷新
205 Reset Content 没有新内容,但浏览器应该重置它所显示的内容,用来强制浏览器清除表单输入内容.
206 Partial Content 客户发送了一个带有 Range 头的 GET 请求,服务器完成了它
300 Multiple Choices 客户请求的文档可以在多个位置找到,这些位置已经在返回的文档中列出.如果服务器要提出优先选择,则应该在 Location 应答头指明
301 Moved Permanently 永久重定向,请求的网页已经永久移动到新位置
302 Found 临时重定向,POST 方法的重定向在未询问用户的情况下就会变成 GET
303 See Other 类似 301/302,不同之处在于,原来的请求是 POST,现在是 GET
304 Not Modified 自从上次请求后,网页未被修改过
305 Use Proxy 使用代理服务器
307 Temporary Redirect 临时重定向,不同之处,把 POST 转为 GET
400 Bad Request 请求出现语法错误
401 Unauthorized 用户未授权,需要用户验证
403 Forbidden 服务器已经理解请求,但是拒绝执行
404 Not Found 服务器找不到请求的网页
405 Method Not Allowed 请求方法(GET、POST、HEAD、DELETE、PUT、TRACE 等)对指定的资源不适用。
406 Not Acceptable 指定的资源已经找到,但它的 MIME 类型和客户在 Accpet 头中所指定的不兼容
407 Proxy Authentication Required 类似于 401,表示客户必须先经过代理服务器的授权
408 Request Timeout 请求超时
500 Internal Server Error 服务器遇到错误,无法完成请求
503 Service Unavailable 由于临时的服务器维护或过载,暂时无法处理请求
504 Gateway Timeout 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答
505 HTTP Version Not Supported 服务器不支持请求中所指明的 HTTP 版本。

HTTP 请求方法

  1. GET

请求服务器发送某个资源

  1. HEAD

与 GET 方法类似,但是在服务器的响应中没有资源的内容,只有资源的一些基本信息,主要用于

  • 在不获取资源的情况下获取信息(类型,大小等)
  • 通过状态码看资源是否存在
  • 通过查看首部,测试资源是否被修改
  1. PUT

和 GET 从服务器获取资源相反,PUT 用于向服务器写入资源.PUT 的语义就是让服务器用请求的主体部分创建一个请求 URL 命名的文档.如果存在就替换

  1. POST

POST 用于向服务器发送数据,通常用来支持 HTML 的表单(input,select,textarea),表单中的数据会被发送到服务器

  1. TRACE

客户端发送一个请求,这个请求可能会穿过防火墙,代理,网关,和一些其他应用程序,每个中间节点都可以修改 HTTP 请求,TRACE 方法允许客户端在最终请求发往服务器的时候,看看他变成了什么样子

TRACE 请求会在目的服务器端发送一个”闭环”诊断,行程最后一站服务器会弹回一条 TRACE 响应,并在响应主题中携带他收到的原始请求报文

  1. DELETE

用于要求服务器删除请求的 URL,和 PUT 一样,服务器可能不支持

  1. OPTIONS

用于请求服务器告知其支持的各种功能

浏览器缓存

缓存是一种保存资源副本并在下次请求时直接使用该副本的技术.

HTTP 缓存有多种规则,根据是否需要重新向服务器发起请求来分类,分为两大类(强制缓存,协商缓存)

http 缓存机制主要在 http 响应头中设定,响应头中相关字段为Expires、Cache-Control、Last-Modified、Etag

cache-control

cache-control 通常用于优化网页性能,适当使用可减少重复读取相同内容

可以在服务端 setHeader 设置 cache-control

可以设置让该字段存于缓存中,浏览器下次访问时不发起请求,直接从缓存中读取该资源

ETag

ETag 通常用于优化网页性能,适当使用可减少重复读取相同内容

可通过服务端 setHeader 设置 etag 为相应的字段的 md5 值

浏览器访问时,request.headers 会带有 if-none-match 属性(为上述 MD5),若不等于则说明服务器上的

该资源与本地存储的该资源不相同,需要重新从服务器加载;若相等则说明两者资源相同,则直接从本地

缓存获取.后端应设置 statusCode=304.与 cache-control 不同的是:etag 始终都要发请求和收响应,而

cache-control 不发请求.

cookie

存储信息到浏览器,可以用 js 设置,也可以在服务器端通过 set-cookie 让浏览器种下 cookie,存在于响应头里。

每次请求都会带上 cookie,最大容量 4k

用途:记录用户名

设置 cookie 时的参数:

  1. path: cookie 影响到的路径,匹配该路径才发送 cookie
  2. expiresmaxAge:告诉浏览器 cookie 多久过期

不设置就会产生 session cookie,当关闭浏览器,cookie 就被清除 3. secure: 为 true 时,在 http 无效,在 https 生效 4. httpOnly: 浏览器不允许通过 js 修改 cookie

session

用来保存状态。当用户输入用户名密码提交给服务器后,服务器验证通过后创建 session 用于记录用户信息,session 可保存在服务器,也可以保存在数据库

  1. 创建 session 后,会把关联的 session_id 通过是 set-cookie 添加到响应头
  2. 浏览器加载页面发现有 set-cookie 字段,就把这个 cookie 种到指定域名下
  3. 下次刷新页面,发送的请求带上 cookie,服务器接受后根据 session_id 来识别用户

cookie 和 session 的区别:

cookie 是存储在浏览器中的一小段数据,session 是让服务器识别某个用户的机制 session.在实现过程中需要使用 cookie.二者不是同一维度的东西.

localStorage

  1. 本地存储,永不过期,除非手动 js 删除。5M 大小。
  2. 不参与网络传输
  3. 一般用于性能优化

localStorage 的存储,获取,删除

只支持字符串存储.

1
2
3
4
5
6
7
8
9
10
11
//存储
localStorage.setItem("myCat", "Tom");

//读取
localStorage.getItem("Tom");

//移除
localstorage.removeItem("myCat");

//移除所有
localstorage.clear();

sessionstorage

与 localstorage 类似,属于本地存储与上面的 session 无关.但是关闭浏览器失效.

浏览器缓存机制通俗语言版(自我理解)

1
2
Cache-Control: max-age=300;
ETag:W/"e-cbxLFQW5zapn79tQwb/g6Q"

浏览器第一次请求 a.jpg 这张图片,服务器发回一张图片,并且附送一个ETag(相当于一个用于验证的文件).浏览器把这张图片和 ETag 缓存到本地.如果在 300 秒内,浏览器又请求这张图片,那么直接从缓存中读取这张图.如果超过 300 秒,浏览器发现已经过了时间了,就重新向服务器发请求,并且附带之前给的 ETag.服务器拿着当前文件计算后的ETag和浏览器发回的ETag比较,如果一样,说明图片没换,发回一个响应头(不包含图片,304,表示文件没修改,还能用).如果不一样,就把新的文件和新的ETag发回去.

另外还有Last-Modified/If-Modified-Since.和ETag功能类似.因为服务器可能不止一个,文件存储在不同的服务器,那就给请求文件上附带上Last-Modified声明,请求时带上If-Modified-Since,表明服务器上一次修改这个文件的时间,拿来和服务器上记录的最后修改时间对比.如果修改时间比较新,说明被动过,发回一个新文件.如果时间比较旧,说明不用换,发回 304,使用缓存即可.

严谨说法

浏览器缓存控制分为强缓存协商缓存协商缓存必须配合强缓存使用。

首先浏览器第一次跟服务器请求一个资源,服务器在返回这个资源和 response header 的同时,会根据开发者要求或者浏览器默认,在 response 的 header 加上相关字段的 http response header。

一、当浏览器对某个资源的请求命中了强缓存时,利用[Expires]或者[Cache-Control]这两个 http response header 实现

  1. [Expires]:描述的是一个绝对时间,根据的是客户端时间。用 GMT 格式的字符串表示,如:Expires:Thu, 31 Dec 2037 23:55:55 GMT 下次浏览器再次请求同一资源时。先从客户端缓存中寻找,找到这个资源后,拿出它的[Expires]跟当前的请求时间比较。如果请求时间在[Expires]指定的失效时间之前,就能命中缓存,这样就不用再次到服务器上去缓存一遍,节省了资源。但是正因为是绝对时间,如果客户端时间被随意更改下,这个机制就失效了。所以我们需要[Cache-Control]。

  2. [Cache-Control]:描述的是一个相对时间,在进行缓存命中时,都是利用浏览器时间判断。

这两个 header 可以只启用一个,也可以同时启用,当 response header 中,[Expires]和[Cache-Control]同时存在时,[Cache-Control]优先级高于[Expires]。

二、当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中。

如果命中,则还是从客户端缓存中加载。协商缓存利用的是[Last-Modified,If-Modified-Since]和[ETag、If-None-Match]这两对 Header 来管理的。

  1. [Last-Modified]:原理和上面的[expires]相同,区别是它是根据服务器时间返回的 header 来判断缓存是否存在。但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况(这种问题也不容易被定位),这时候我们需要[ETag、If-None-Match]。

  2. [ETag、If-None-Match]:原理与上相同,区别是浏览器跟服务器请求一个资源,服务器在返回这个资源的同时,在 respone 的 header 加上 ETag 的 header,这个 header 是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同。

  3. [ETag、If-None-Match]这么厉害我们为什么还需要[Last-Modified、If-Modified-Since]呢?有一个例子就是分布式系统尽量关闭掉 ETag(每台机器生成的 ETag 都会不一样)

[Last-Modified,If-Modified-Since]和[ETag、If-None-Match]一般都是同时启用。

HTTPS 原理

HTTP+SSL/TLS, 即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL,用于安全的 HTTP 数据传输。

加密算法:

  1. 对称加密

有流式、分组两种,加密和解密都是使用的同一个密钥。 2. 非对称加密

加密使用的密钥和解密使用的密钥是不相同的,分别称为:公钥、私钥,公钥和算法都是公开的,私钥是保密的。非对称加密算法性能较低,但是安全性超强,由于其加密特性,非对称加密算法能加密的数据长度也是有限的 3. 哈希算法

将任意长度的信息转换为较短的固定长度的值,通常其长度要比信息小得多,且算法不可逆。 4. 数字签名

签名就是在信息的后面再加上一段内容(信息经过 hash 后的值),可以证明信息没有被修改过。hash 值一般都会加密后(也就是签名)再和信息一起发送,以保证这个 hash 值不被修改。

简单通信流程

概括来说,整个简化的加密通信的流程就是:

  1. 小明访问网站,网站将自己的证书给到小明(其实是给到浏览器,小明不会有感知)
  2. 浏览器从证书中拿到网站的公钥 A
  3. 浏览器生成一个只有自己知道的对称密钥 B,用公钥 A 加密,并传给网站(其实是有协商的过程,这里为了便于理解先简化)
  4. 网站通过私钥解密,拿到对称密钥 B
  5. 浏览器、网站 之后的数据通信,都用密钥 B 进行加密

存在的问题

证书可能存在的问题

证书是伪造的:压根不是 CA 颁发的

证书被篡改过:比如将 XX 网站的公钥给替换了

解决方法: 数字签名和摘要

“摘要”就是对传输的内容,通过 hash 算法计算出一段固定长度的串。然后,在通过 CA 的私钥对这段摘要进行加密,加密后得到的结果就是“数字签名.这段数字签名只有 CA 的公钥才能够解密。

一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?(流程说的越详细越好)

详细版:

1、浏览器会开启一个线程来处理这个请求,对 URL 分析判断如果是 http 协议就按照 Web 方式来处理;

2、调用浏览器内核中的对应方法,比如 WebView 中的 loadUrl 方法;

3、通过 DNS 解析获取网址的 IP 地址,设置 UA 等信息发出第二个 GET 请求;

4、进行 HTTP 协议会话,客户端发送报头(请求报头);

5、进入到 web 服务器上的 Web Server,如 Apache、Tomcat、Node.JS 等服务器;

6、进入部署好的后端应用,如 PHP、Java、JavaScript、Python 等,找到对应的请求处理;

7、处理结束回馈报头,此处如果浏览器访问过,缓存上有对应资源,会与服务器最后修改时间对比,一致则返回 304;

8、浏览器开始下载 html 文档(响应报头,状态码 200),同时使用缓存;

9、文档树建立,根据标记请求所需指定 MIME 类型的文件(比如 css、js),同时设置了 cookie;

10、页面开始渲染 DOM,JS 根据 DOM API 操作 DOM,执行事件绑定等,页面显示完成。

简洁版:

浏览器根据请求的 URL 交给 DNS 域名解析,找到真实 IP,向服务器发起请求;

服务器交给后台处理完成后返回数据,浏览器接收文件(HTML、JS、CSS、图象等);

浏览器对加载到的资源(HTML、JS、CSS 等)进行语法解析,建立相应的内部数据结构(如 HTML 的 DOM);

载入解析到的资源文件,渲染页面,完成。

WebSocket 协议

1.为什么需要 WebSocket

HTTP 协议只能由客户端发起,WebSocket 可以实现双向对话,服务端和客户端都可以发送信息.

2.WebSocket 特点

  1. 建立在 TCP 协议之上,服务端实现容易.
  2. 与 HTTP 协议有着良好的兼容性,并且握手阶段采用 HTTP 协议,因此握手阶段不容易屏蔽,可通过 HTTP 代理服务器.
  3. 数据格式轻量,性能开销小,通信高效.
  4. 可以发送文本和二进制数据.
  5. 没有同源限制.
  6. 协议标识符是 ws(加密是 wss),服务器网址是 URL.
1
ws://example.com:80/some/path

客户端示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function (evt) {
console.log("Connection open ...");
ws.send("Hello WebSockets!");
};

ws.onmessage = function (evt) {
console.log("Received Message: " + evt.data);
ws.close();
};

ws.onclose = function (evt) {
console.log("Connection closed.");
};

WebSocket 客户端 API

WebSocket 构造函数

1
ws = new WebSocket("ws://localhost:8080");

执行上面语句之后,客户端就会与服务器进行连接。

WebSocket.readyState

readyState 属性返回实例对象的当前状态,共用四种.

  • CONNECTING: 值为 0,表示正在连接.
  • OPEN: 值为 1,表示连接成功,可以通信.
  • CLOSEING: 值为 2,表示正在关闭.
  • CLOSED: 值为 3,表示连接已经关闭,或者打开失败.

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
switch (ws.readyState) {
case WebSocket.CONNECTING:
// do something
break;
case WebSocket.OPEN:
// do something
break;
case WebSocket.CLOSING:
// do something
break;
case WebSocket.CLOSED:
// do something
break;
default:
// this never happens
break;
}

WebSocket.onopen

实例对象的onopen属性,用于指定连接成功后的回调函数.

1
2
3
ws.onopen = function () {
ws.send("hello");
};

如果指定多个函数,可以使用addEventListener方法.

1
2
3
ws.addEventListener("open", function (event) {
ws.send("hello");
});

WebSocket.onclose

实例对象的onclose属性,用于指定连接关闭后的回调函数。