重識TCP/IP協議族與HTTP基礎

jasonhww發表於2019-01-26

不忘初心 砥礪前行, Tomorrow Is Another Day !

本文概要:

  1. TCP/IP協議族的網路分層
    • TCP三次握手四次揮手
  2. Http簡介
  3. 報文結構
  4. Http的請求方法及狀態碼
  5. 常用的Header

前言:

宣告:掌握好HTTP非常重要,具體理由本文最後再揭曉!

一. TCP/IP協議族的網路分層

在瞭解HTTP協議之前,先來快速瞭解TCP/IP的網路分層以及三次握手四次揮手的流程.

1.1 網路分層

由於網路的不穩定性,所以將網路分層多個網路模型.

  • 應用層: HTTP FTP DNS
  • 傳輸層: TCP UDP
  • 網路層: IP
  • 鏈路層: 乙太網 WIFI

大致流程:

  1. 應用層將內容打包好交給傳輸層.
  2. 傳輸層負責將內容資訊進行切割分包,依次交給網路層.並確認資訊是否傳送成功.
    • 如果不成功,將資料包繼續交給網路層傳送.
  3. 網路層只負責將資訊的傳送到目標地址,不負責資訊全部準確傳送到目標地址.
    • 確認資訊是否傳送成功的是傳輸層.
  4. 最後實際傳送的是通過我們的鏈路層也就是網線和閘道器路由等.

1.2 TCP三次握手四次揮手

這裡用最簡單粗暴的方式快速瞭解其過程,不帶任何複雜的專業術語.讓你看圖就明白.

  • 三次握手
重識TCP/IP協議族與HTTP基礎
TCP三次握手
  • 四次揮手
重識TCP/IP協議族與HTTP基礎
TCP四次揮手

可以發現在四次揮手中,第2步與第3步會分開傳送,之所以這樣是因為此時伺服器可能還有訊息沒有傳送完畢.所以就沒有像握手流程時一起傳送訊息.

二. Http簡介

HyperText Transfer Protocol 超文字傳輸協議

  • URL 格式 : 協議型別 + 主機名 + 路徑

示例

api.github.com/users?key1=…

  • http : 協議型別
  • api.github.com : 主機名
  • /users?key1=value1&key2=value2 : 路徑

三. 報文結構

  • 請求報文結構
重識TCP/IP協議族與HTTP基礎
HTTP請求報文結構

示例

    GET /users HTTP/1.1
    Host: api.github.com
    Content-Type: text/html
    Content-Length: 110

    bodybodybody假裝我是一個Body
    bodybodybodybodybodybody
複製程式碼
  • 響應報文結構
    響應報文和請求報文大同小異,只是將請求行換成了狀態行.
重識TCP/IP協議族與HTTP基礎
響應報文狀態行結構

示例

    HTTP/1.1 200 OK
    content-type: application/json; charset=utf-8
    cache-control: public, max-age=60, s-maxage=60
    content-encoding: gzip
    
    bodybodybody假裝我是一個Body
    bodybodybodybodybodybody
複製程式碼

四. 請求方式及狀態碼

4.1 狀態碼
status code 類別 原因短語 示例
1XX Informational(臨時資訊) 接收的請求正在處理 100(繼續傳送)
2XX Success 請求正常處理完畢 200(OK)
3XX Redirection(重定向) 需要進行附加操作以完成請求 301(永久移動)、304(內容未改變)
4XX Client Error 伺服器無法處理請求 400(客戶端請求錯誤)、404(內容找不到)
5XX Server Error 伺服器處理請求出錯 500(伺服器內部錯誤)

更多資訊,可以檢視HTTP狀態碼詳解

4.2 請求方式
Method description
GET 1. 獲取資源 2. 不傳送Body
POST 1. 增加或者修改資源 2. 傳送Body
PUT 1.修改資源 2. 傳送Body
DELETE 1.刪除資源 2. 不傳送Body
HEAD 與GET基本一致 , 區別在於返回響應無Body

五. 常見的Header

HTTP的後設資料,傳遞一些附加資訊.

格式: 鍵: 值,注意冒號後面有一個空格

由於Header過多,這裡只列舉了個人覺得比較重要且常用的Header

5.1 通用的Header
欄位名 內容說明
Content-Type 內容型別(下一節詳細講解)
Content-Length 內容長度,單位位元組
Content-Encoding 壓縮編碼格式,如gzip
Last-Modified 資源的最後修改日期時間
Cache-Control 控制快取的行為,取值為一般為no-cache或max-age=XX
5.2 請求Header
欄位名 內容說明
Host 請求的主機和埠號
User-Agent 使用者代理,僅用於找到目標主機後確認主機域名和埠
Accept 接受的媒體型別,可以多個值,用,(半形逗號)分開.如text/html,
Accept-Charset 接受的字符集,如UTF-8
Accept-Encoding 接受的壓縮編碼型別,如gzip
--- ------------------------------------------
Last-Modified 值,用於確認某個資源是否被更改過,沒有更改過(304)就從快取中讀取
If-Modified-Since 比較資源的更新時間;值為上一次伺服器返回的
If-Match 比較實體標記(ETag)
If-None-Match 比較實體標記(與 If-Match 相反);值為上一次伺服器返回的 ETag 值,一般會和If-Modified-Since一起出現
--- ------------------------------------------
Cookie 已有的Cookie
Authorization 用於設定身份認證資訊
5.3 響應Header
欄位名 內容說明
Location 令客戶端重定向至指定URI
Transfer-Encoding 分塊傳輸,如Transfer-Encoding: chunked
Accept-Range 如Accept-Range: bytes,表示伺服器支援按位元組獲取資料
Content-Range 如Content-Range:-/total,表示傳送的是哪段資料
----------- ------------------------------
ETag 資源的匹配標識,和Last-Modified、If-None-Match、If-Modified-Since配合,用於快取控制
Set-Cookie 設定Cookie

最後著重學習下Content-Type,這是實際開發中最經常使用的.

  • Content-Type
    指定Body的型別
  1. text/html 一般用於響應時,返回html頁面.

示例

    HTTP/1.1 200 OK
    content-type: text/html; charset=utf-8
    Content-Length: 666
    
    <!DOCTYPE html>
    <html>
    <head>
        ......
    </head>
    <body>
        ......
    <body>    
    </html>

複製程式碼
  1. application/x-www-form-urlencoded
    普通表單,提交純文字.

示例

POST /users HTTP/1.1
Host: api.github.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 33

userName=jasonhww&password=123456
複製程式碼

對應Retrofit程式碼

@FormUrlEncoded
@POST("/users")
Observable<User> login(@Field("userName") String userName, @Field("password") String
password);
複製程式碼
  1. multipart/form-data; boundary={boundary} 帶檔案的表單

將body分成多個部分,每部分都被boundary分成單獨的段;

  • 每段以 -- 加 boundary開頭,
  • 然後是該段的conten-disposition,
  • 空行,
  • 傳入的value,
  • 最後請求結束的標識為boundary後面加--

示例

           POST /users/photo HTTP/1.1
           Host: api.github.com
           Content-Length: 3698
請求頭      Content-Type: multipart/form-data;boundary={boundary的值}
空行
請求體part1 --{boundary的值}
            conten-disposition: form-data; name="userName"
            \r\n
            jasonhww
請求體part2 --{boundary的值}
            conten-disposition: form-data; pwd="password"
            \r\n
            123456
請求體part3 --{boundary的值}
            conten-disposition: form-data; name="photo"; filename="檔名.txt";
            Content-Type: image/jpeg
            \r\n
            sdsdsdfqfvfvsvadvavdavakvkakvadvanvnav
            afdfafaiewfre482ekdsji21dnefrgoonwncvewnfefeowkkr假設我是一個檔案ddsrrogkrn2n3934
請求體結束  ---boundary的值--

複製程式碼

其中Content-Disposition中的filename是區分是否當成檔案;因為檔案有不同的型別,所以還要使用Content-Type指示檔案的型別;如果不知道是什麼型別取值可以為application/octet-stream表示該檔案是個二進位制檔案.

對應Retrofit程式碼

@Multipart
@POST("/users/photo")
Observable<User> uploadPhoto(@Part("userName") RequestBody userName, @Part("password") RequestBody password, @Part("photo")
RequestBody photo);


RequestBody namePart = RequestBody.create(MediaType.parse("text/plain"),
nameStr);
RequestBody pwdPart = RequestBody.create(MediaType.parse("text/plain"),
nameStr);
RequestBody avatarPart = RequestBody.create(MediaType.parse("image/jpeg"),
avatarFile);
api.addUser(namePart, avatarPart);

複製程式碼
  1. application/json , image/jpeg
    單內容,實際開發使用較少.

示例-提交json

POST /users HTTP/1.1
Host: api.github.com
Content-Type: application/json
Content-Length: 41

{"userName":"jasonhww","password":123456}
複製程式碼

這樣請求body中直接為json字串了

對應Retrofit程式碼

@POST("/users")
Observable<User> addUser(@Body("user") User user);

複製程式碼

示例-提交檔案

POST /users/photo HTTP/1.1
Host: api.github.com
Content-Type: image/jpeg
Content-Length: 6666

dsfdsfncwowncnowncodwcw...假設是個檔案資料
複製程式碼

這樣請求body中直接為檔案資料了

對應Retrofit程式碼

@POST("/users/photo")
Observable<User> updatePhoto(@Body RequestBody avatar);
複製程式碼

最後順便提下restFul,其實 RestFul風格就是指規範的使用HTTP,但是國內大多數公司一般都不遵從.比如請求方式,實際就只會用到GET與POST.

另外:前面提到掌握好HTTP非常重要,現在揭曉理由,理由就是除錯介面時,與後臺互懟的強力保障,再也不要擔心他們忽悠客戶端了.O(∩_∩)O
不過新時代社會主義還是和諧為主,儘量少互懟,合作共贏才是正道!

由於本人技術有限,如有錯誤的地方,麻煩大家給我提出來,本人不勝感激,大家一起學習進步.

參考連結:

相關文章