博客 / 詳情

返回

網絡知識

瀏覽器如何定位一台服務器?

使用url統一資源定位符:protocal://hostname:port/path?query#hash

  • protocal: 協議,服務器根據不同的協議確定數據交換的格式和方式,常用的有httphttps
  • hostname: 主機名,可以是ip,也可以是域名;

    • ip:網絡中每個終端的唯一編號;
    • 域名:和ip一一對應,方便記憶;
    • 域名和ip的關係:計算機是通過ip在網絡上查找的另一台計算機,域名是方便人閲讀和記憶。輸入域名的時候,計算機會先去通過域名解析服務器(DNS)將域名轉化成ip,再去網絡中根據這個ip查找計算機。
  • port:端口號,服務器中有很多其它的應用,端口是用來定位使用服務器中的哪一個應用;

    • http: 默認端口80
    • https:默認端口443
  • path:路徑,用於區分使用web服務器的哪個服務。
  • query:查詢參數,用於向web服務器傳遞信息。

    url只支持ASCII字符,輸入中文會被轉義
  • hash:錨點,定位頁面中的內容,唯一一個改變後瀏覽器不會重新訪問服務器的值(不會刷新頁面),可以用來做路由。

瀏覽器和服務器傳輸數據的格式是什麼?

http是其中一種用來規定瀏覽器和服務器傳輸數據格式的協議。
http將瀏覽器和服務器之間的一次交互分為兩個部分:

  • 請求request:客户端發生數據給服務器的過程
  • 響應response:服務器收到請求返回一個數據給客户端的過程

其實一個請求可以看做是一個規定了格式的特殊字符串,下面我們看看這個字符串是有什麼組成的。

以百度的網址為例,

請求request由請求頭和請求體兩個部分組成:

1. 請求頭(request headers)

規定請求的請求方式,就是告訴服務器自己給什麼格式的數據,以什麼方式給,希望返回什麼數據等。

GET /s?wd=abc 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
Sec-Fetch-User: ?1
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
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: PSTM=1563287690;

請求頭又包括請求行和其它屬性:

  • 請求行:請求頭的第一行

    • 請求方法:GET、POST等
    • path:url中的path + search + has
    • 協議及版本號:HTTP/1.1
  • 其它屬性:後面的數據以鍵值對的形式存儲,可以自定義

    • Host: 主機名
    • Connection: 連接方式

2. 請求體(request body)

  1. 請求體就是瀏覽器要發送給服務器的數據,一般是服務器需要用到的業務數據。
  2. 請求頭中也可以傳遞不同格式的數據,一般由請求頭中的Content-Type屬性來描述請求體中使用的格式。

    • 常見的格式:

      • application/x-www.form-urlencoded對應的數據格式:屬性名=屬性值&屬性名=屬性值
      • application/json對應的數據格式:{"屬性名":"屬性值", "屬性名":"屬性值"}
      • multipart/form-data對應的數據格式:使用某個隨機字符串作為屬性之間的分隔符,通常用於文件上傳。
      ------WebKitFormBoundaryD1o3TzulmXAIABD2
      Content-Disposition: form-data; name="image"; filename="xxx.png"
      Content-Type: image/png
      ------WebKitFormBoundaryD1o3TzulmXAIABD2--

響應response由響應頭和響應體兩個部分組成:

1. 響應頭(response headers)

服務器根據請求頭和請求體構建出來的數據,告訴客户端響應的狀態,響應數據的格式等信息。

HTTP/1.1 200 OK
Content-Length: 24
Content-Type: text/plain; charset=UTF-8
Date: Mon, 03 Feb 2020 10:21:37 GMT

響應頭又包括響應行和其它屬性:

  • 請求行:請求頭的第一行

    • 協議及版本號:HTTP/1.1
    • 狀態碼:對應不同的響應狀態
    • 狀態文本:用於描述狀態碼的文本
常見的狀態碼:
200: 請求成功。
301:資源永久重定向,告訴瀏覽器這個資源已經不在當前地址了,去其它地方取,其它地方的地址一般放在Location屬性中。
302:資源臨時重定向,告訴瀏覽器這個資源沒有變化,自己去緩存中拿。
400:請求有問題,當前請求服務器無法識別。
403:服務器拒絕執行,可能是權限不夠造成的,服務器不返回數據。
404:頁面找不到,告訴瀏覽器這個地址不對,我這裏找不到對應的資源。
500:服務器內部錯誤,表示是服務器處理程序出錯。
502:網關錯誤,瀏覽器請求的地址可能還會進行一層網關的過濾,用來分發到不同的服務器,網關錯誤表示代理網關無法從上游服務器獲取數據。
503:服務器過載,表示當前服務器處理不了那麼多請求,拒絕當前瀏覽器發出的請求,一般會在響應頭添加一個Retry-After屬性。
504:網關超時,網關請求上游服務器一般會設置的一個時間限制,在規定時間內沒有從上游服務器獲取數據就會產生該錯誤信息。
  • 其它屬性:後面的數據以鍵值對的形式存儲,可以自定義

    • Server: web服務器類型,Apache、IIS等
    • Content-Type: 描述響應體中的數據格式

2. 響應體(response body)

服務器返回給客户端的業務數據

常見的數據格式有:

  • text/plain:普通的存文本。瀏覽器會原封不動的顯示在頁面上。
  • text/html:html文檔,瀏覽器會將返回的數據解析成頁面。
  • text/css:css代碼,瀏覽器會根據返回數據渲染html頁面。
  • text/javascript:js代碼,瀏覽器會啓動js引擎進行解析。
  • attachment:附件、瀏覽器看到該屬性會自動下載服務器返回的數據。
  • 其它MIME類型

POST請求和GET請求的區別?

就協議的角度來講是沒有什麼實質的區別,不管是那種請求都是由請求頭和請求體組成的,只不過服務器會根據請求的方式不同去不同的地方讀取業務數據。

服務器對於不同的請求方法做出的處理(不是絕對的,開發者可以自己處理)

  • GET請求:服務器遇到GET請求一般會去path中讀取數據。
  • POST請求:服務器遇到POST請求一般會去請求體中讀取數據。

瀏覽器對於不同請求做出的處理

  • GET請求會限制url大小,因此如果業務數據過大要使用POST請求。
  • POST請求只能通過表單的形式提交,在瀏覽器地址欄輸入的url都是以GET的方式向服務器發送請求。

其它差異:

  1. GET請求會保留請求數據,利於用户做分享。
  2. GET請求的業務數據是放在url中的,一般帶有用户信息的數據都不應該使用GET請求,防止被其它人偷窺,或者被用户自己分享出去。

什麼是Ajax?

AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)。

是瀏覽器提供給js的請求網絡數據的API。

請求實例:

var xhr = new XMLHttpRequest(); //創建請求實例
// 監聽請求變化的函數  
xhr.onreadystatechange = function (response) {
    if (xhr.readyState === 4 && xhr.status === 200) { //表示所有內容接受完畢,並且服務器返回的狀態是請求成功
        console.log(xhr.responseText);
    }
}
xhr.open("GET", "https://api.binstd.com/shouji/query"); //配置請求
xhr.send(); //構建請求體,發送請求體
現在大部分的請求都使用fetch替代ajax。(fetch是es6提供的請求網絡數據的新API,用promise封裝的網絡請求API。)

什麼是跨域?

  1. 協議,域名,端口三者有一個不同就會造成跨域無法獲取數據的問題;
  2. 跨域是瀏覽器為了提高安全性做的限制。只有在瀏覽器中才會發生的現象。服務器之間互相訪問是不會出現跨域問題的;
  3. linkimgscript等含有src屬性的標籤也不會出現跨域問題;
  4. 解決跨域問題,有jsonp,後台設置運行跨域,iframe等方法;但是副作用最少,無需後台配合的就是自己寫代理服務器。

代理服務器

服務器代理解決跨域問題思路:

  1. 出現跨域問題一般是請求不同域下的資源,請求相同域中的資源不會出現跨域問題。
  2. 自己編寫一個服務器,將要發請求的頁面放在該服務器下,再通過自己的服務器請求真實接口,將返回的數據回傳給頁面就可以解決跨域問題。

代理服務器的作用:你請求有跨域問題,我請求不會,我幫你請求再把數據返回給你。

舉個例子:

環境:node v10.16.0,yarn 1.15.2

目錄結構:

- demo.html
- proxy.js
  1. 在項目根目錄打開cmd,運行命令:

    yarn init
    yarn add express
    yarn add request
  2. 新建demo.html文件,代碼如下:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>測試跨域請求</title>
    </head>
    
    <body>
    <script>
        var xhr = new XMLHttpRequest(); //創建請求實例
        // 監聽請求變化的函數  
        xhr.onreadystatechange = function (response) {
            if (xhr.readyState === 4 && xhr.status === 200) { //表示所有內容接受完畢,並且服務器返回的狀態是請求成功
                console.log(JSON.parse(xhr.responseText));
            }
        }
        xhr.open("GET", "http://localhost:2020/api"); //配置請求
        xhr.send(); //構建請求體,發送請求體
    </script>
    </body>
    
    </html>
  3. 新建proxy.js文件,代碼如下:

    var express = require('express'); //導入express模塊
    var request = require('request'); //導入request模塊
    
    var app = new express(); //創建express實例
    app.use(express.static('./')); //監聽文件夾,獲取靜態資源
    app.listen(2020); //監聽2020端口
    
    //監聽api接口
    app.get('/api', function (req, res) {
        // 請求真實接口
        request('https://api.binstd.com/shouji/query', function(error,response,body){
            res.header('Content-Type', 'application/json;charset=utf-8');
            res.send(body); //返回數據
        })
    })
  4. 啓動服務器,在cmd輸入node proxy.js
  5. 打開瀏覽器,輸入網址:http://localhost:2020/demo.html,查看控制枱,現在就不會有跨域問題產生。

什麼是三次握手?

客户端與服務器建立TCP/IP連接的準備階段進行的三次交互被稱為三次握手。

http協議是基於TCP/IP協議建立的數據傳輸協議,http定義的是數據收發雙發的格式,TCP/IP協議定義的是數據接收時連接怎麼建立。

過程:

  1. 客户端向服務器發送請求;
  2. 服務器接收請求,向客户端發送迴應;
  3. 客户端接收回應;

為什麼要進行三次握手?

為了建立一個可靠的連接,確保客户端和服務器都具備收發能力。

分析

假設只進行步驟1,客户端向服務器發送請求,客户端無法知道服務器真的能接到消息;

假設進行了步驟1,2,客户端知道服務器具備收數據的功能,服務器也知道客户端具備發數據的功能,但是服務器還不能確定客户端是否能收到數據;

所以要執行步驟3,這就是為什麼一定要三次握手。

什麼是四次揮手?

四次揮手是TCP/IP連接斷開時候客户端和服務器進行的四次交互過程。

過程:

  1. 客户端發送中斷請求,不再發送數據;
  2. 服務器接收中斷請求,數據沒發送完成,還可以發送數據;
  3. 服務器發送關閉連接請求 ,不可發送數據;
  4. 客户端確定關閉連接。
發完了,知道發完了,收完了,知道收完了

為什麼要進行四次揮手?

為了確保在斷開連接之前客户端和服務器的交互數據已經收發完畢。

分析:

  1. 客户端告訴服務器我要斷開連接,不再發送數據;
  2. 服務器知道了客户端要斷開請求,但是還有數據要發個客户端,告訴客户端先等我的數據發完再斷開連接;
  3. 過了一會服務器數據發送完畢,服務器告訴客户端數據發完了,可以關閉連接了(此時服務端不再接收客户端消息);
  4. 客户端收到通知,關閉連接,不再接受服務端消息。完成整個斷開連接過程。
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.