XMLHttpRequest,深入理解异步请求、TCP的三次握手与四次挥手

面试时回答

TCP协议是什么

TCP协议是一种面向连接的、可靠的、基于字节流运输层通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。连接时要经过三次握手断开时要经过四次挥手的过程。

  • TCP的三次握手
  1. 第一次握手:客户端发送一段TCP报文,服务端收到报文。
    这次握手,服务端可以确认自己可以接受到客户端发送的报文
  2. 第二次握手:服务器端返回一段TCP报文,客户端收到返回报文。
    这次握手,客户端可以确认服务端收到了自己发送的报文段,并且可以确认自己可以接受 服务器发送的报文段
  3. 第三次握手:客户端将返回的报文返回给服务端
    这次握手,服务端可以确认客户端收到了自己发送的报文

客户端和服务端都需要确认各自可收、发,因此需要三次握手。

  • TCP的四次挥手
  1. 第一次:浏览器发送FIN码给服务器表示我要断开链接

  2. 第二次:服务器接收到后,并发送ACK码给浏览器表示已经收到你的断开请求

  3. 第三次:之后,服务器不会立刻就断开,会去确认所有要发送的数据是否已经发送完毕。 之后服务器继续发送FIN+ACK码告诉浏览器我的数据发送完毕

  4. 第四次:浏览器接收到后,确认下所有数据都接收完毕,之后会发送ACK码给服务器表示我已接收到你的信息了,我断开了,你也可以断开连接了。
    在这里插入图片描述

为什么是建立连接是三次握手,而断开却是四次挥手

三次握手,主要是为了确认双方可以收到对方所传递过来的数据

四次挥手,断开时,其中**多了一步**就是确认自己要发送的数据已经发送完毕,对方确定自己要接收的数据也已经接收完毕,最终才会断开,也就是多了第三次挥手的过程。

HTTPS与HTTP
  • https=http+ssl,之所以能保证安全,主要原理是利用了非对称加密算法,平常用的对称加密算法之所以不安全,是因为双方是用到统一的密钥进行加密解密的,只要双方有任何一方泄漏了密钥,那么其他人就可以利用密钥解密数据。
  • 客户端如何验证证书的合法性
    1、校验证书的颁发机构是否受客户端信任
    2、通过 CRL 或 OCSP 的方式校验证书是否被吊销
    3、证书是否在有效期内
    4、判断网站域名是否与证书颁发的域名一致

当前证书是自签的,还是权威机构颁发的

什么是对称加密

对称加密,就是加密(encryption)与解密(decryption)用的是同样的密钥

缺点
在发送密钥的过程中,密钥有很大的风险会被黑客们拦截,容易泄露。

解决办法
对密钥进行非对称加密,然后传送给需要它的人。

什么是非对称加密?

非对称加密,它使用了一对密钥,公钥 和 私钥
私钥只能由一方安全保管,不能外泄,而公钥则可以发给任何请求它的人。通过使用公钥加密,私钥解密。

比如,你向银行请求公钥,银行将公钥发给你,你使用公钥对消息加密,那么只有私钥的持有人–银行才能对你的消息解密。与对称加密不同的是,银行不需要将私钥通过网络发送出去,因此安全性大大提高。

TCP和UDP的区别

1、TCP 通讯双方建立连接的状态,才开始传递数据,一旦断开,则不再传输。适用于对安全性要求比较高的数据!
2、UDP 一方开始推送数据,不管另一方是否在接收,只管推送。适用于对安全性要求不高的数据。

HTTP协议是什么

HTTP 就是超文本传输协议, 是一个专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范

一个http请求分为以下7个步骤

1,建立TCP连接
2,浏览器向服务器发送请求命令
3,浏览器向服务器发送请求头信息
4,服务器响应
5,服务器向浏览器发送响应头信息
6,服务器向浏览器发送数据
7,服务器关闭TCP连接

一个http请求一般由4个部分组成

1,http请求的方式,常见的有get和post
2,请求的url
3,请求头信息,包括一些浏览器信息和用户身份信息等
4,请求正文,包括一些用户的查询信息,表单信息等。请求头和请求体有一个空行来分开。

请求头信息包括
  • origin: 从哪个地址发出的请求,但是仅仅包括协议和域名。
    可以看到response有对应的header:Access-Control-Allow-Origin
  • refer:从哪个地址发出的请求
  • cookies:如果是同源的话,浏览器会自动携带cookie
  • Authorization:授权信息
  • user-agent:用户使用的客户端版本等信息
  • accept 指定客户端可以接收的数据类型有哪些
  • Content-Type: 表示客户端发送的数据类型
    application/x-www-form-urlencoded、
    application/form-data、
    application/json、
    application/octet-stream(二进制流数据)、
    application/xml
响应码分五种类型,由它们的第一位数字表示:

1xx:请求已经接收到,需要进一步处理才能完成

2xx:成功处理请求

  • 200 OK:成功返回响应

  • 202 Accepted:服务器接受并开始处理请求,但请求未处理完成

3xx:重定向,就是通过各种方法将各种网络请求转到其它位置

  • 301 ------ 请求的资源被永久的移动了位置
  • 302------- 请求的资源被临时的移动了位置
  • 304------- 针对GET 请求的资源,自上次访问以来并没有改变,则服务器应当返回304,表示通过浏览器缓存读取该资源即可
  • 305------ 所请求的资源必须通过代理访问

4xx:客户端错误,请求包含语法错误或者请求无法实现

  • 400 Bad Request:服务器认为客户端出现了错误,但不明确,一般是 HTTP 请求格式错误
  • 401 Unauthorized:用户认证信息缺失或者不正确,比方说没有携带token/ authorization等标识用户身份的东西。
  • 403 Forbidden:禁止执行,一般是没有权限引起的。
  • 404 Not Found:服务器没有找到对应的资源
  • 405 Method Not Allowed ------ 请求方法不正确,比如说应该是post,而你用的是get
  • 412 ------ 请求头字段的错误
  • 413 ------ 请求的资源大于服务器允许的大小
  • 414 ------ 请求地址URL大于服务器允许的长度

5xx:服务器错误

  • 500 Internal Server Error ------ 服务器内部错误
  • 501 ------ 服务器不支持请求的功能,无法完成请求
  • 502 Bad Gateway ------ 网关或者代理服务器无响应
  • 505 HTTP Version Not Supported ------ 请求使用的 HTTP版本不支持

HTTP与TCP的区别和联系

TCP对应于传输层,HTTP对应于应用层,从本质上来说,二者没有可比性。

Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求。Http会通过TCP建立起一个到服务器的连接通道,当本次请求需要的数据完毕后,Http会立即将TCP连接断开,这个过程是很短的。所以Http连接是一种短连接,是一种无状态的连接。

TCP是底层协议,定义的是数据传输和连接方式的规范。
HTTP是应用层协议,定义的是传输数据的内容的规范。

结论:
HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP就一定支持TCP。

XMLHTTPRequest对象

常用方法
  • open(请求方法:“get/post”, 请求url, 是否异步(默认true))
  • send(请求体发送数据,无则传入null)
  • abort():收到响应之前取消异步请求
  • setRequestHeader(‘MyHeader’, ‘MyValue’)
  • getResponseHeader(‘MyHeader’)|getAllResponseHeader()
属性
  • responseText
  • responseXML
  • status(响应HTTP状态)
  • statusText(响应HTTP状态描述)
  • readyState(响应状态,请求/响应过程的哪个阶段):0未初始化|1已打开|2已发送|3接收中|4完成,从一个值变为一个值,会触发readystatechange事件,
  • readystatechange事件处理程序应该在调用open()之前赋值
  • timeout超时时间,对应超时事件ontimeout
  • 进度事件loadstart、progress事件
示例
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://domain/service');

// request state change event
xhr.onreadystatechange = function () {
    // request completed?
    if (xhr.readyState !== 4) return;

    if (xhr.status === 200) {
        // request successful - show response
        console.log(xhr.responseText);
    } else {
        // request error
        console.log('HTTP error', xhr.status, xhr.statusText);
    }
};

xhr.timeout = 3000; // 3 seconds
xhr.ontimeout = () => console.log('timeout', xhr.responseURL);

// progress事件可以报告长时间运行的文件上传
xhr.upload.onprogress = p => {
     console.log(Math.round((p.loaded / p.total) * 100) + '%');
}

// start request
xhr.send(null);
fetch
  • 方法:fetch(url,{}init对象),返回Promise对象,只支持异步。

  • 响应通过response对象获取:fetch().then((response)=>{}).catch(()=>{})response对象混入了body,提供了5个方法,将ReadableStream转存到缓冲区的内存里,将缓冲区转换为js对象,通过Promise返回。

    • response.text() //转为text
    • response.json() //转为json
    • response.formData()
    • response.arrayBuffer()
    • response.blob()
  • 错误不会reject:当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject

  • 不支持超时设置

  • 需要借用AbortController终止fetch

  • fetch示例

    fetch(
            'http://domain/service', {
                method: 'GET'
            }
        )
        .then(response => response.json())
        .then(json => console.log(json))
        .catch(error => console.error('error:', error));
    
    //credentials:omit不发送cookie|same-origin同源发送cookie(默认)|include都发送cookie
    fetch(
        'http://domain/service', {
            method: 'GET',
            credentials: 'same-origin'
        }
    )
    
    // 错误不会reject
    // HTTP错误(例如404 Page Not Found 或 500 Internal Server Error)不会导致Fetch返回的Promise标记为reject;.catch()也不会被执行。
    // 想要精确的判断 fetch是否成功,需要包含 promise resolved 的情况,此时再判断 response.ok是不是为 true
    fetch(
            'http://domain/service', {
                method: 'GET'
            }
        )
        .then(response => {
            if (response.ok) {
                return response.json();
            }
            throw new Error('Network response was not ok.');
        })
        .then(json => console.log(json))
        .catch(error => console.error('error:', error));
    
    // 不支持直接设置超时, 可以用promise
    function fetchTimeout(url, init, timeout = 3000) {
        return new Promise((resolve, reject) => {
            fetch(url, init)
                .then(resolve)
                .catch(reject);
            setTimeout(reject, timeout);
        })
    }
    
    // 中止fetch
    // signal用于支持AbortController中断请求
    const controller = new AbortController(); 
    //AbortController接口表示一个控制器对象,允许你根据需要中止一个或多个 Web请求。
    fetch(
            'http://domain/service', {
                method: 'GET',
                signal: controller.signal
            })
        .then(response => response.json())
        .then(json => console.log(json))
        .catch(error => console.error('Error:', error));
    controller.abort();
    
请求体的数据格式
  • form-data
    就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开既可以上传键值对,也可以上传文件

    当上传的字段是文件时,会有Content-Type来说明文件类型;
    content-disposition,用来说明字段的一些信息;
    由于有boundary隔离,所以multipart/form-data既可以上传文件,也可以上传键值对,它采用了键值对的方式,所以可以上传多个文件。

  • x-www-form-urlencoded:就是application/x-www-from-urlencoded,会将表单内的数据转换为键值对,比如,name=java&age = 23

  • json: 也就是Content-Type:application/json,上传JSON格式的数据;

  • octet-stream:也就是Content-Type:application/octet-stream,从字面意思得知,只可以上传二进制数据,通常用来上传文件,由于没有键值,所以,一次只能上传一个文件。

  • raw:可以上传任意格式的文本,可以上传text、json、xml、html

XHR请求过程

1、XMLHTTPRequest对象

var xhr = new XMLHttpRequest()

2、请求信息配制(请求方式,请求地址,是否异步,请求头信息、监听请求)

// 请求方式、地址、是否异步
xhr.open(method,url,asncy)
// 请求头信息
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
// 请求成功回调函数
xhr.onload = e => {
    console.log('request success');
};
// 请求结束
xhr.onloadend = e => {
    console.log('request loadend');
};
// 请求出错
xhr.onerror = e => {
    console.log('request error');
};
// 请求超时
xhr.ontimeout = e => {
    console.log('request timeout');
};

3、发送请求

xhr.send(params)

具体详解

最简单的http请求

let xhr = new XMLHttpRequest();
xhr.open('GET', '/url', true);
xhr.send();

封装XMLHttpRequest

const http = {
  /**
   * js封装ajax请求
   * >>使用new XMLHttpRequest 创建请求对象,所以不考虑低端IE浏览器(IE6及以下不支持XMLHttpRequest)
   * >>使用es6语法,如果需要在正式环境使用,则可以用babel转换为es5语法 https://babeljs.cn/docs/setup/#installation
   *  @param settings 请求参数模仿jQuery ajax
   *  调用该方法,data参数需要和请求头Content-Type对应
   *  Content-Type                        data                                     描述
   *  application/x-www-form-urlencoded   'name=哈哈&age=12'或{name:'哈哈',age:12}  查询字符串,用&分割
   *  application/json                     name=哈哈&age=12'                        json字符串
   *  multipart/form-data                  new FormData()                           FormData对象,当为FormData类型,不要手动设置Content-Type
   *  注意:请求参数如果包含日期类型.是否能请求成功需要后台接口配合
   */
  ajax: (settings = {}) => {
    // 初始化请求参数
    let _s = Object.assign({
      url: '', // string
      type: 'GET', // string 'GET' 'POST' 'DELETE'
      dataType: 'json', // string 期望的返回数据类型:'json' 'text' 'document' ...
      async: true, //  boolean true:异步请求 false:同步请求 required
      data: null, // any 请求参数,data需要和请求头Content-Type对应
      headers: {}, // object 请求头
      timeout: 1000, // string 超时时间:0表示不设置超时
      beforeSend: (xhr) => {
      },
      success: (result, status, xhr) => {
      },
      error: (xhr, status, error) => {
      },
      complete: (xhr, status) => {
      }
    }, settings);
    // 参数验证
    if (!_s.url || !_s.type || !_s.dataType || !_s.async) {
      alert('参数有误');
      return;
    }
    // 创建XMLHttpRequest请求对象
    let xhr = new XMLHttpRequest();
    // 请求开始回调函数
    xhr.addEventListener('loadstart', e => {
      _s.beforeSend(xhr);
    });
    // 请求成功回调函数
    xhr.addEventListener('load', e => {
      const status = xhr.status;
      if ((status >= 200 && status < 300) || status === 304) {
        let result;
        if (xhr.responseType === 'text') {
          result = xhr.responseText;
        } else if (xhr.responseType === 'document') {
          result = xhr.responseXML;
        } else {
          result = xhr.response;
        }
        // 注意:状态码200表示请求发送/接受成功,不表示业务处理成功
        _s.success(result, status, xhr);
      } else {
        _s.error(xhr, status, e);
      }
    });
    // 请求结束
    xhr.addEventListener('loadend', e => {
      _s.complete(xhr, xhr.status);
    });
    // 请求出错
    xhr.addEventListener('error', e => {
      _s.error(xhr, xhr.status, e);
    });
    // 请求超时
    xhr.addEventListener('timeout', e => {
      _s.error(xhr, 408, e);
    });
    let useUrlParam = false;
    let sType = _s.type.toUpperCase();
    // 如果是"简单"请求,则把data参数组装在url上
    if (sType === 'GET' || sType === 'DELETE') {
      useUrlParam = true;
      _s.url += http.getUrlParam(_s.url, _s.data);
    }
    // 初始化请求
    xhr.open(_s.type, _s.url, _s.async);
    // 设置期望的返回数据类型
    xhr.responseType = _s.dataType;
    // 设置请求头
    for (const key of Object.keys(_s.headers)) {
      xhr.setRequestHeader(key, _s.headers[key]);
    }
    // 设置超时时间
    if (_s.async && _s.timeout) {
      xhr.timeout = _s.timeout;
    }
    // 发送请求.如果是简单请求,请求参数应为null.否则,请求参数类型需要和请求头Content-Type对应
    xhr.send(useUrlParam ? null : http.getQueryData(_s.data));
  },
  // 把参数data转为url查询参数
  getUrlParam: (url, data) => {
    if (!data) {
      return '';
    }
    let paramsStr = data instanceof Object ? http.getQueryString(data) : data;
    return (url.indexOf('?') !== -1) ? paramsStr : '?' + paramsStr;
  },
  // 获取ajax请求参数
  getQueryData: (data) => {
    if (!data) {
      return null;
    }
    if (typeof data === 'string') {
      return data;
    }
    if (data instanceof FormData) {
      return data;
    }
    return http.getQueryString(data);
  },
  // 把对象转为查询字符串
  getQueryString: (data) => {
    let paramsArr = [];
    if (data instanceof Object) {
      Object.keys(data).forEach(key => {
        let val = data[key];
        // todo 参数Date类型需要根据后台api酌情处理
        if (val instanceof Date) {
          // val = dateFormat(val, 'yyyy-MM-dd hh:mm:ss');
        }
        paramsArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(val));
      });
    }
    return paramsArr.join('&');
  }
}

说一说你对 DNS 的理解

  • DNS (Domain Name System) 负责对域名的解析工作,由根DNS服务器顶级DNS 服务器权威 DNS 服务器组成。

  • 解析顺序是首先从浏览器缓存操作系统缓存以及本地 DNS 缓存 (/etc/hosts) 逐级查找,然后从本地 DNS 服务器根 DNS顶级 DNS 以及权威 DNS层层递归查询。

说一说你对 CDN 的理解

  • CDN(Content Delivery Network)就是内容分发网络

  • 为了突破现实生活中的传输距离等物理限制,CDN 投入了大量资金,在全球范围内各大枢纽城市建立机房,部署大量高存储高带宽的节点,构建跨运营商、跨地域的专用高速传输网络

  • 在用户接入网络后,首先通过全局负载均衡 (Global Sever Load Balance),简称 GSLB 算法负责调度,找到离用户最合适的节点。然后通过 HTTP 缓存代理技术进行缓存,缓存命中就返回给用户,否则就回源站去取。CDN 擅长缓存静态资源(图片、音频等),当然也支持动态内容的缓存。

参考地址 https://www.jianshu.com/p/918c63045bc3

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值