网络相关知识(Http协议, 请求响应, 服务器知识和Ajax)

网络

  • 什么是服务器

  • 如何访问服务器

  • http协议

    1. 请求消息格式

      • 请求头
      • 请求体
      • GET和POST
    2. 响应消息格式

      • 响应头
      • 响应体
  • 在浏览器中输入一个页面地址, 按下回车以后发生了什么

  • ajax

什么是服务器?

如果所有的程序都是单机的, 会导致什么后果呢?

  1. 数据难以共享

  2. 受计算机配置的影响, 运算速度差异巨大

  3. 个人计算机的安全性堪忧, 可能受到恶意程序的影响

而有了服务器之后这些问题都可以得到解决

服务器在不同的语境中表达了不同的含义

  1. 一台独立的计算机
    比如我们说要去买一台服务器 配置一台服务器, 就是代表的是一台独立的计算器

  2. 一个应用程序

绝大部分开发者, 通常把服务器看做是一个应用程序

无论他是哪个概念, 他都具有以下的特点:

  1. 能够通过网络, 被其他应用程序所访问

  2. 能够提供一些服务

如果一个服务器(应用程序), 它仅仅为一个浏览器提供访问网站服务, 我们将它称之为web服务器

实际上目前的web服务器和游戏服务器界限已经非常模糊, 可以认为, 凡是在互联网中提供服务的服务器都是web服务器

通常我们把访问服务器的程序, 称之为客户端

实际上web服务器不仅仅为浏览器提供服务, 还可以为手机app, 小程序, 小游戏等常见互联网应用提供服务, 而我们今天讨论的是仅考虑为浏览器提供服务的服务器

常见的web服务器有: nginx, apache, iis

开发阶段服务器一般安装在本地计算机, 通常也称之为本地服务器

vscode有一个live Server插件, 他其实就是一个轻量级的服务器

如何访问服务器

服务器可能在本机, 也可能在远程, 他一定运行在一台计算机上, 要在茫茫互联网中访问到服务器程序, 就必须:

  1. 精确的定位到服务器所在的计算机

  2. 精确定位到计算机中的服务器程序

  3. 精确定位到程序中的某个功能

通常我们用url地址来准确的描述上面的三个条件

url(Uniform Resource Locator), 统一资源定位符, 是一个字符串, 他的格式如下

protocal://hostname:port/path?query#hash

  • protocal: 使用的协议, 选择不同的协议会导致和服务器之间消息交互格式, 连接方式的不同, 大部分服务器都支持http和https两种协议, 如果选择了服务器不支持的协议则会导致访问失败

  • hostname: 主机名, 主机名可以是ip或者域名

    • ip: 每台计算机在网络中的唯一编号, 127.0.0.1表示本机
    • 域名: 网络中容易记忆的唯一单词, 通过DNS服务器可以将域名解析成ip, localhost会被解析成127.0.0.1
  • port: 端口, 0 - 65535之间的数字, 相当于服务器计算机上的房号, 使用不同的端口相当于敲不同的房门, 计算机上的程序可以监听一个或者多个端口号, 如果访问的端口号有程序被监听, 则计算机会将到达的网络访问交给对应的程序来处理

    • 端口号可以不写有默认值, http默认为80, https默认为443
  • path: 一个普通的字符串, 该字符串会交给web服务器处理, 主要用于定位服务

    • 如果path为/, 则表示根路径, 例如https://www.baidu.com/的path就是/
  • query: 一种特殊格式的字符串, 该字符串会交给web服务器处理, 主要用于向服务器的某个程序传递一些信息

  • hash: 一个普通的字符串, 在浏览器的地址栏中, 如果url其他位置的信息保持不变, 仅变动hash, 浏览器则不会重新访问服务器因此通常用于不刷新页面的跳转

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-807e20Gi-1581654054428)('...')]

经过这些了解我们可以看出:

  • hostname是用来精准定位计算机的

  • port是用来精准定位服务器的

  • protocal是用于告诉服务器使用哪种协议进行传输数据

  • path是用于精准定位服务器上的服务的

  • query是在使用服务的时候传递的额外的信息, 具体看服务器要求

  • hash也是一些额外信息, 具体要不要也要看服务器要求

注意: url仅支持ASCII字符, 如果包含非ASCII字符, 会被现代浏览器自动进行编码

例如: https://www.baidu.com/s?wd=王思聪

会被编码为: https://www.baidu.com/s?wd=%E7%8E%8B%E6%80%9D%E8%81%AA

同时url地址不能过长, 因为很多浏览器对url地址长度是有限制的, Chrome对url的长度限制为8182个ASSCI字符

http协议

我们可以通过url地址访问服务器, 但是, 服务器和服务器之间的数据到底是怎么交互的, 数据的格式是什么, 这取决于用什么协议

最常见的协议就是http协议

http协议将和服务器的一次交互看做是两段简单的过程组成: 请求request响应response

  • 请求: 客户端通过url地址发送数据到服务器的过程

  • 响应: 服务器收到请求之后反馈结果给客户端的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jWAhmo0t-1581654054429)('...')]

当请求和响应都完成以后, 本次交互结束, 如果需要得到额外的服务器, 则需要重新发送请求

同时 http请求约定了请求的消息格式和响应的消息格式

请求消息格式

请求消息格式由两部分组成, 请求头request headers和请求体request body

请求头

请求头是一个多行文本的字符串

比如我们请求http://www.baidu.com/s?wd=html, 得到的请求头如下:

GET /s?wd=html HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
...

我们可以看出, 该字符串由两部分组成

  1. 请求行: 请求方法path协议

    • 请求方法, 一个普通的字符串, 会被服务器读取到, 常见的请求方法: GET, POST

    • path: 即url中的path + search + hash, 浏览器可能会用到path的信息

    • 协议: 协议即版本号, 目前固定为HTTP/1.1

  2. 键值对: 大量的属性名和属性值组合, 可以自定义

    • url: url地址中的hostname

    • User-Agent: 客户端信息描述

      我们看上方的Chrome的User-Agent在干嘛, 他说自己又是Mozilla, 又是webkit又是Safari, 又是Chrome, 他在干嘛, 他是不是在骗人, 他就是在骗人, 这个为什么要骗人还是跟浏览器大战有关系

    • 其他键值对

请求头描述了请求的元数据信息, 这里的元数据是指跟业务无关的额外信息(跟业务相关的数据如用户账号密码称之为业务数据, 像告诉服务器自己是哪个浏览器 主机名是什么这些都属于元数据)

当我们在浏览器输入一个url按下回车以后, 浏览器会自动构建一个请求, 方法为GET 并向服务器发送请求

请求体

请求体是包含业务数据的字符串

理论上, 请求体可以是任意格式的字符串, 但习惯上, 服务器普遍能识别以下格式

  • application/x-www-form-unlencoded: 属性名=属性值&属性名=属性值

  • application/json: {“属性名”: “属性值”, “属性名”: “属性值”}

  • multipart/form-data: 使用某个随机字符串作为属性之间的分隔符, 通常用于文件上传

由于请求体样式的多样性, 服务器在分析请求体时可能无法知晓具体的格式, 从而不知道如何解析请求体, 因此浏览器通常要在请求头中附带一个属性Content-Type来描述请求体使用的格式

例如:

Content-Type: application/x-www-form-urlencoded
Content-Type: application/json
Content-Type: multipart/form-data

GET和POST

虽然http协议没有规定请求方式必须是什么, 但随意的请求方法服务器可能无法识别, 服务器一般都能识别GET和POST请求, 并作出如下的差异化处理

  • 如果是GET请求, 不读取请求体, 业务数据从path中的search或者hash中读取

  • 如果是POST请求, 读取请求体, 业务数据从请求体中获取, 关于请求体的格式, 不同的服务器, 或者同一个服务器不同的服务对于请求体格式要求都不相同

    在地址栏输入url是不可能产生POST请求的, 但是提交表单可以产生POST请求

    <form action="请求的地址" method="POST">
        <p>
            账号:
            <input type="text" name="loginId">
        </p>
        <p>密码:
            <input type="password" name="password">
        </p>
        <p>
            <button type="submit">提交</button>
        </p>
    </form>
    

由于服务器对GET和POST请求处理上的差异, 导致了GET和POST请求上的差异

  1. GET请求一般不设置请求体, POST有

  2. GET请求的业务数据放在地址中安全性相较于POST较低, POST放在请求体中

  3. GET请求传递的业务数据量是有限的, POST则不做限制(除非服务器做限制)

  4. GET请求利于分享页面结果, POST不行

  5. 在浏览器刷新或者回退页面的时候, 会按照之前的请求方式重新请求数据, 如果是GET请求, 浏览器则会重新发送GET请求, 如果是POST请求 浏览器则会重新构建之前的消息体数据, 通常会弹出提示

响应消息格式

和请求类似, 响应消息也分响应头(response headers)和响应体(response body)

响应头

比如我们请求 https: //www.baidu.com/s?wd=html, 得到的响应头可能如下

HTTP/1.1 200 OK
Bdpagetype: 3
Bdqid: 0xae29d62d0002d4fb
Cache-Control: private
Ckpacknum: 2
Ckrndstr: d0002d4fb
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Fri, 14 Feb 2020 01:22:58 GMT
Server: BWS/1.1
Set-Cookie: delPer=0; path=/; domain=.baidu.com
Set-Cookie: BD_CK_SAM=1;path=/
Set-Cookie: PSINO=3; domain=.baidu.com; path=/
Set-Cookie: BDSVRTM=18; path=/
Set-Cookie: H_PS_PSSID=30744_1468_21085; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 1581643378042092493812549797325406655739
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked

该字符串由两部分组成

  • 响应行: 协议 状态码 状态码文本

    1. 协议: 协议以及协议版本号, 目前固定为HTTP/1.1

    2. 状态码和状态码文本: 一个数字和该数字对应的单词, 来描述服务器的响应状态, 浏览器会根据不同的状态码做不同的处理
      (常用的状态码如下)

      • 200 OK: 一切正常
      • 301 Move Permanently: 资源已经被永久重定向, 你的请求我已经收到了, 但是呢你要的东西已经不在这个地址了, 我已经永久的把它移动到了一个新的地址, 麻烦你请求新的地址, 地址我已经 放到请求头的loacation中了
      • 302 Found: 资源已经被临时重定向, 你的请求我已经收到了, 但是呢你要的东西已经不在这个地址了, 我已经临时的把它移动到了一个新的地址, 麻烦你请求新的地址, 地址我已经 放到请求头的loacation中了(永久的话会直接覆盖浏览器的历史记录, 临时不会)
      • 304 Not Modified: 文档内容未修改, 你的请求我收到了, 但是你要的东西和之前没有任何修改, 我就不给你结果了, 你自己就用以前的吧, 啥? 你没有缓存以前的内容? 管我啥事?
      • 400 Bad Request: 语义有误, 当前请求无法被服务器理解 你给我发的是个啥啊? 听都听不懂
      • 403 Forbidden: 服务器拒绝执行, 你的请求我已经收到了 但是我就是不给你东西
      • 404 Not Found: 资源不存在, 你的请求我已经收到了, 但是我没有你要的东西
      • 500 Interval Server Error: 服务器内部错误, 你的请求我已经收到了, 但是这道题我不会, 解不出来 我先睡了

    通常认为, 0~399之间的状态码都是正常的, 其他都是不正常的

  • 键值对: 大量的属性名和属性值的结合, 可以在服务器响应的时候自定义

    1. Content-Type: 响应体中的数据格式, 常见格式如下

      • text/plain: 普通的纯文本, 浏览器通常会将响应体原封不动的展示在页面上
      • text/html: html文档, 浏览器通常会将响应体作为html页面进行渲染
      • text/javascript: js代码, 浏览器通常会使用js引擎将他解析并且执行
      • image/jpeg: 浏览器会将他视为jpg图片
      • text/css: css代码, 浏览器会将他视为样式
      • attachment: 附件, 浏览器看到这个类型, 通常会直接触发下载功能
      • 其他MIME类型
      面试题: 
      1. 如果访问http://www.xxx.com/1.js 浏览器是否会按照js来执行
      
          不会, 只看响应头中的Content-Type
      
      2. 如果服务器返回的响应体是js, 浏览器是否会按照js执行
      
          不会, 还是只看响应头中的Content-Type
      
    2. Server: web服务器类型

响应体

响应体没什么好说的, 就是响应消息的正文

在浏览器地址栏中输入一个地址, 按下回车键以后都发生了什么?

  1. 浏览器将url地址补充完整: 没有书写协议, 则补上协议

  2. 找到这个url域名的服务器ip, 浏览器会首先从缓存中获取, 如果无则会去系统文件的hosts文件中寻找,如果无则会查询DNS服务器

  3. 浏览器将对url地址进行url编码: 如果url地址中出现了非ASCII码字符, 则浏览器会对其进行编码

  4. 浏览器会构造一个http请求, 将请求头请求体和附带的数据封装在一个tcp包中, 该tcp包会依次经过传输层, 网络层, 数据链路层, 物理层到达服务器

  5. 服务器接收到请求, 解析之后将一个html页面代码放进响应体中, 返回给浏览器

  6. 浏览器拿到服务器的响应以后, 丢弃当前页面, 开始渲染新的html页面, 根据页面结构生成dom树, 根据css生成css规则树, 然后合并成渲染树

  7. 浏览器在渲染页面的过程中, 发现有其他的嵌入资源, 如图片js等, 浏览器会使用不阻塞加载的方式重新向服务器发送请求获取资源

  8. 当上述事件处理完毕以后触发window.onload事件

Ajax

不仅仅是浏览器可以发出请求并且获得响应, 任何具有网络通信能力的程序均可以这样做

过去, 在浏览器中, 只有浏览器本身有发送请求的能力, 直到ajax的出现

ajax是一种技术, 让js语言在浏览器中获得了新的api, 通过该api, js代码拥有了和服务器通信的能力

同时,js代码发出的网络请求浏览器是不会刷新页面的

我们来看一篇已经封装好的ajax的方法, 根据代码和注释相信可以帮助你理解好Ajax是如何请求服务器资源的

// 笔者直接用es5书写了, 避免有些朋友还未接触到es6
function ajax(url, methods, data, callback, flag) {
    var requestTypes = {
        GET: function() {
            var date = new Date().getTime; // 时间戳, 好让服务器区分请求的不同
            var paramsStr = ''; 
            // 因为get请求一般是不设置响应体的, 所以我们要将参数进行遍历拼接进url
            for(var prop in data) {
                paramsStr += (prop + '=' + data[prop] + '&');
            } 
            url = url + '?' + paramsStr + date;  
            xhr.open(methods, url, flag); // send代表配置请求, 这个flag一般代表是否异步, 默认肯定是异步
            xhr.send(); // 代表构建请求发送至服务器 
        }, 

        POST: function() {
            // 如果是POST请求
            xhr.open(method, url, flag);
            xhr.send(data); // post请求的data是要进请求体的
        }
    }

    // 创建发送请求的对象, 因为兼容性问题IE独有为ActiveXObject来创建,所以我们做一下兼容性处理
    var xhr = windows.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft XMLHttp'); 

    // 设置请求的请求头
    xhr.setRequestHeader("Content-Type", "application/json");

    var methods = methods.toUpperCase(); // 调用方法的人可能会使用小写的get post 我们统一转化为大写

    // 当请求状态发生改变的时候运行的函数, 这个方法最好写在send之前, 因为如果写在send之后由于网速原因某些时候可能会监听不到变化 
    xhr.onreadystatechange = function() {
        // xhr.readyState: 一个数字, 用于判断请求到了哪个阶段
        // 0: 刚刚创建好了这个对象, 但是还未配置请求(未调用open方法)
        // 1: open方法已经被调用, 配置请求已经处理好
        // 2: send方法已经被调用, 请求体构建好并发送到服务器
        // 3: 正在接收服务器的响应消息体
        // 4: 服务器响应的所有内容均已经接收完毕
        // 根据这四个状态我们可以想到的是我们要等到状态为4的时候才可以取到完整的服务器响应数据
        if(xhr.readyState === 4) {
            // xhr.status 表示状态码和状态文本
            if(xhr.status === 200) {
                // xhr.responseText: 获取服务器响应的消息体
                // xhr.getResponseHeader("Content-Type") 获取响应消息头中的Content-Type
                // 这个时候我们直接将消息体传进callback, 让回调函数取做他该做的事
                callback(xhr.responseText);
            }
        }
    }
    
    // 根据请求方法进行不同的操作
    requestTypes[methods](); 
}

注释写的相对来说比较详细应该也可以帮助大家更好的理解ajax请求了

至此, 希望对大家可以有所帮助

发布了33 篇原创文章 · 获赞 11 · 访问量 2241

猜你喜欢

转载自blog.csdn.net/weixin_44238796/article/details/104309853