JS基础总结(6)—— HTTP请求

1. Ajax

Ajax: Asynchronous JavaScript + XML
能够向服务器请求额外数据而无须卸载页面

1.1 XMLHttpRequest

原生进行http请求方法如下:

const request = new XMLHttpRequest()
// 以下是两种状态监听方案
// 1. 使用onreadystatechange监听XMLHttpRequest对象的状态
request.onreadystatechange = function () {
    // readyState: 0: 未调用open(), 1: 调用open()未调用send()
    // 2. 调用send()未收到响应, 3. 收到部分响应数据 4. 收到全部数据
    if (request.readyState === 4) {
        console.log('请求完成了')
    } else if (request.readyState === 3) {
        console.log('收到数据了')
    }
}
// 2. 使用ProgressEvent对象监听XMLHttpRequest状态
// ProgressEvent对象: 测量如 HTTP 请求等底层流程进度的事件
request.onload = function (event) { // 相当于readyState为4
    console.log('收到数据了:' + event.currentTarget.responseText)
}
request.onprogress = function (event) { // 相当于readyState为3
    if (event.lengthComputable && event.totalSize) {
        console.log('当前加载进度为' + 
        (event.position / event.totalSize).toFixed(2) + '%')
    }
}
request.onerror = function (err) {
    console.log(err)
}
request.timeout = 10000
request.ontimeout = function () {
	console.log('time out')
}
request.open('GET', url, true )
request.send()

1.2 content-type

用于定义数据类型以及编码,常用的三种格式如下:

  1. application/json: 使用JSON数据
  2. application/x-www-form-urlencodedform表单数据被编码为key/value格式发送到服务器
  3. multipart/form-data: 文件传输

2. 跨域

满足以下三种条件之一,浏览器则会认为请求跨域,从而阻止该HTTP请求

  1. 协议不同
  2. 域名不同(域名与解析后的IP不被认为是同一域名)
  3. 端口不同

2.1 Preflighted Request

在处理复杂跨域请求时,浏览器会在发送请求之前,使用OPTIONS方法向服务器发送一个Preflight请求(预请求),如果服务器响应结果为拒绝性质,则不再发送真正的请求。
进行Preflighted Request的条件:

  1. 使用自定义请求头
  2. 使用非GETPOST方法
  3. 使用不同类型的content-type

2.2 解决方案

2.2.1 图像Ping

由于img标签的加载不存在跨域,可以用来进行浏览器-服务端的单项通信
缺点:

  1. 只能进行get请求
  2. 无法对响应结果进行处理
const img = new Image()
img.onload = function () {
    console.log('返回了')
}
// 发送数据{ name: xxx, password: xxx}
img.src = 'http://xxx.com/xxx?name=xxx&password=xxx'
2.2.2 JSONP

由于script标签加载不存在跨域,可以用来进行浏览器-服务端的双向通信
缺点:

  1. 只能进行get请求
  2. 不安全
// 1. 动态添加script标签
const script = document.createElement('script')
// 2. 设置回调
script.src = 'http://www.xxx.com/xxx?callback=handleResponse'
document.body.appendChild(script)
// 3. 注入回调
function handleResponse(response) {
    console.log('响应数据是:', response)
}

// 服务器响应文本
handleResponse({ name: 'soraka', age: 18})
// 服务器返回结果本质上是一串js代码,script标签加载完毕后会自动执行
// 代码的内容是执行handleResponse这个函数,传参则是服务器处理后拼接上去的字符串
// 所以在JSONP成功返回后会自动执行注入的回调并将结果传进来
2.2.3 跨域资源共享(CORS)

服务端设置Access-Control-Allow-Origin为指定域名即可

2.2.4 代理

跨域是浏览器行为,不是HTTP协议的部分,因此只存在客户端-服务器间的跨域,不存在服务器-服务器间的跨域。
webpacknginx代理原理类似,解决客户端-代理服务器间的跨域,由代理服务器转发请求至目标服务器,然后将结果返回完成请求

2.2.5 Web Socket

不再局限于客户端主动请求服务器,建立连接后,客户端可以向服务器发送消息,服务器也可以主动向客户端推送消息

// 服务器
const app = express()
var expressWs = require('express-ws')(app);

app.ws('/', function(ws, req) {
    let index = 0
    ws.on('message', function(msg) {
        console.log(msg); // message
    });
    setInterval(() => {
        ws.send(index++)
    },1000)
});
// 客户端
const url = 'ws://192.168.x.x:5000'
const Socket = new WebSocket(url)
Socket.onmessage = function(){
    console.log(event.data) // 0,1,2,3,4...
};
function send() {
    Socket.send('message')
}
2.2.6 iframe类

以下方案适用于嵌套iframe在不同域名下页面间的通信,不涉及后端
(书以及网上的理论,本人实践1,3貌似不行,可能是使用ip + port不是域名的缘故。由于在postMessage面前,它们都是弟弟,所以也就不太在乎可行性)

  1. document.domain:
    iframe和子iframe同时设置document.domain = 'domain.com',即可强制将两者设置为同一域名
  2. location.hash:
    通过hash传值触发onhashchange
    实现AB通信,设置一个与A同域的子域CAhashBB收到后将处理结果传hashCC收到后调用A中的回调将结果传给A
  3. window.name:
    window.name在不同页面加载后值仍然存在,可以通过iframe加载其他域下的页面后将值取出给当前页面使用
2.2.7 postMessage

基本使用:

  1. 获取接收消息的窗口的引用
  2. 在引用上调用postMessage()方法
// 示例一
// 发送方
<body>
	<button onclick="send()">postMassage</button>
	<iframe id="iframe" src="http://192.168.x.x:5000/public/index.html"></iframe>
	<script>
	    const iframe = document.getElementById('iframe')
	    function send() {
	        iframe.contentWindow.postMessage('postMassage', '*')
	    }
	</script>
</body>
// 接收方
<body>
	<div id="box"></div>
	<script>
	    window.addEventListener('message', function (event) {
	        const box = document.getElementById('box')
	        box.innerText = event.data
	    })
	</script>
</body>

// 示例二
// 发送方
<body>
    <button onclick="send()">postMassage</button>
    <button onclick="openWindow()">打开</button>
    <script>
        let targetWindow
        function openWindow() {
            targetWindow = window.open('http://192.168.x.x:5000/public/index.html', 'hehe')
        }
        function send() {
            targetWindow.postMessage('postMassage', '*')
        }
    </script>
</body>
// 接收方
<body>
	<div id="box"></div>
	<script>
	    window.addEventListener('message', function (event) {
	        const box = document.getElementById('box')
	        box.innerText = event.data
	    })
	</script>
</body>

3. HTTP

网络七层协议:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
上三层注重数据的处理,下三层注重数据的传输,中间层对接上三层和下三层

3.1 TCP

传输层协议,HTTP请求可以简单理解为TCP的连接,HTTP只是对数据进行了处理,本质还是TCP

3.1.1 三次握手

  1. 客户端:哥,我要跟你建立关系,这是我的SYN请求,序列号Seq是x
  2. 服务器:弟,我收到请求了,这是Ack: x + 1。我也要跟你建立关系,这是我的SYN请求,序列号Seq是y
  3. 客户端:哥,我收到请求了,这是Ack: y + 1

为什么三次:少一次不可靠,多一次浪费性能

3.1.2 四次挥手

  1. 服务器:弟,我传完了,拜拜,这是FIN请求,序列号是x
  2. 客户端:哥,我收到请求了,这是Ack: x + 1
  3. 客户端:哥,我也传完了,拜拜,这是FIN请求,序列号是y
  4. 服务器:弟,我收到请求了,这是Ack: y + 1

为什么四次:全双工通信,需要保证双方传输完毕

3.1.3 滑动窗口

假设一次发送三个包A,B,C
接受方: 只有在顺序收到包后才会滑动。如收到A,未收到B,收到C,窗口滑动至B处
发送方: 滑动至收到的最大确认序列包处。如未收到A、B的确认包,收到了C的确认包,窗口滑动至D处(认为A和B都收到了,只是确认包丢失了)

3.1.4 拥塞机制

慢启动: 在到达慢启动门限之前,拥塞窗口在每个RTT之后呈指数增长
拥塞避免: 在到达慢启动门限之后,拥塞窗口在每个RTT之后呈线性增长
快重传: 在连续三次收到一个数据包的丢失ACK后,立即重传这个数据包
快启动: 在连续三次收到一个数据包的丢失ACK后,将慢启动门限设置为拥塞窗口的一半,启动拥塞避免

3.2 HTTPS

HTTPSHTTP下加入SSL层,SSLHTTP的安全基础

加密算法:

  • 对称加密: 加解密使用同一个密钥,加解密速度快
  • 非对称加密:公私钥一对,公钥加密只有私钥能解,私钥加密只有公钥能解,加解密速度慢

实现过程:

  1. 客户端向服务器发起连接请求
  2. 服务器返回证书和非对称加密公钥
  3. 客户端验证证书,使用公钥加密验证数据验证服务器
  4. 服务器使用私钥解密,将相同数据再加密后返回完成验证
  5. 客户端产生对称加密密钥,使用公钥加密后传给服务器
  6. 服务器解密获得密钥,此后使用对称加密进行通信

这个加密逻辑个人认为无解,第三方唯一能入手的则是在第一步就伪装成服务器,否则后续即使截获双方通信的所有内容,也无法得到有用的信息
但是验证证书又将伪装成服务器的可能扼杀,因此HTTPS相对来说还是挺安全的

3.3 HTTP发展

  1. http1.0:每次请求建立一次TCP连接,请求完毕后断开连接
  2. http1.1:建立一次TCP长连接,同步的进行多次请求,前面的请求会拥塞后面的请求
  3. http2.0:建立一次TCP连接,并发的进行多次请求
发布了6 篇原创文章 · 获赞 0 · 访问量 79

猜你喜欢

转载自blog.csdn.net/weixin_44844528/article/details/105393487