HTTP、HTTPS、WebSocket

一 、HTTP

image.png

1.1 HTTP發展史

1.1.1 什麼是HTTP

超文本傳輸協議,是一個基於請求與響應,無狀態的,應用層的協議,常基於TCP/IP協議傳輸數據,互聯網上應用最爲普遍的一種網絡協議,全部的WWW文件都必須遵照這個標準。設計HTTP的初衷是爲了提供一種發佈和接收HTML頁面的方法html

1.1.2 發展歷史

image.png

1.2 HTTP特色

  1. 無狀態:協議對客戶端沒有狀態存儲,對事物處理沒有「記憶」能力,好比訪問一個網站須要反覆進行登陸操做
  2. 無鏈接:HTTP/1.1以前,因爲無狀態特色,每次請求須要經過TCP三次握手四次揮手,和服務器從新創建鏈接。好比某個客戶機在短期屢次請求同一個資源,服務器並不能區別是否已經響應過用戶的請求,因此每次須要從新響應請求,須要耗費沒必要要的時間和流量。
  3. 基於請求和響應:基本的特性,由客戶端發起請求,服務端響應簡單快速、靈活
  4. 通訊使用明文、請求和響應不會對通訊方進行確認、沒法保護數據的完整性

1.3 HTTP消息結構

一個HTTP請求報文由請求行(request line)、請求頭(header)、空行和請求數據4個部分組成,下圖給出了請求報文的通常格式python

image.png

1.3.1 請求行

請求行由請求方法字段、URL字段和HTTP協議版本字段3個字段組成,它們用空格分隔。例如,GET/index.htmlHTTP/1.1web

1.3.2 請求頭

請求頭部由關鍵字/值對組成,每行一對,關鍵字和值用英文冒號「:」分隔。請求頭部通知服務器有關於客戶端請求的信息,典型的請求頭有常見的請求頭屬性ajax

  • Accept :指定客戶端可以接收的內容類型 | Accept: text/plain, text/html
  • Cache-Control: 指定請求和響應遵循的緩存機制 | Cache-Control: no-cache
  • Cookie :HTTP請求發送時,會把保存在該請求域名下的全部cookie值一塊兒發送給web服務器 | Cookie:$Version=1; Skin=new;
  • Content-Type:請求的與實體對應的MIME信息 | Content-Type: application/x-www-form-urlencoded
  • Host: 指定請求的服務器的域名和端口號
  • Referer: 先前網頁的地址,當前請求網頁緊隨其後,即來路
  • User-Agent : 發起請求的用戶的身份標識

1.3.3 空行

最後一個請求頭以後是一個空行,發送回車符和換行符,通知服務器如下再也不有請求頭。算法

1.4 HTTP響應

HTTP狀態碼的英文爲HTTP Status Code。狀態代碼由三位數字組成,第一個數字定義了響應的類別,且有五種可能取值。瀏覽器

狀態響應頭緩存

  • 1xx:指示信息–表示請求已接收,繼續處理。
  • 2xx:成功–表示請求已被成功接收、理解、接受。
  • 3xx:重定向–要完成請求必須進行更進一步的操做。
  • 4xx:客戶端錯誤–請求有語法錯誤或請求沒法實現。
  • 5xx:服務器端錯誤–服務器未能實現合法的請求。

常見的狀態碼安全

  • 200 OK:客戶端請求成功,通常用於GET和POST請求
  • 400 Bad Request:客戶端請求有語法錯誤,不能被服務器所理解。
  • 301 Moved Permanently:永久移動,請求的資源已被永久移動到新url,返回信息會包含新的url,瀏覽器會自動定向到新url
  • 401 Unauthorized:請求未經受權,這個狀態代碼必須和WWW-Authenticate報頭域一塊兒使用。
  • 403 Forbidden:服務器收到請求,可是拒絕提供服務。
  • 404 Not Found:請求資源不存在,舉個例子:輸入了錯誤的URL。
  • 500 Internal Server Error:服務器發生不可預期的錯誤。
  • 502 Bad Gateway: 充當網關或代理的服務器,從遠端接收到一個無效的請求
  • 503 Server Unavailable:服務器當前不能處理客戶端的請求,一段時間後可能恢復正常,舉個例子 : HTTP/1.1 200 OK(CRLF)

1.5 關於HTTP請求GET和POST的區別

1.5.1 兩種提交方式

  1. GET提交 : 請求的數據會附在URL以後(就是把數據放置在HTTP協議頭<request-line>中),以?分割URL和傳 輸數據,多個參數用&鏈接;例如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0 %E5%A5%BD。若是數據是英文字母/數字,原樣發送,若是是空格,轉換爲+,若是是中文/其餘字符,則直接 把字符串用BASE64加密,得出如: %E4%BD%A0%E5%A5%BD,其中%XX中的XX爲該符號以16進製表示的 ASCII。
  2. POST提交:把提交的數據放置在是HTTP包的包體<request-body>中。

1.5.2 傳輸數據的大小

首先聲明,HTTP協議沒有對傳輸的數據大小進行限制,HTTP協議規範也沒有對URL長度進行限制。 而在實際開發中存在的限制主要有:服務器

  1. GET : 特定瀏覽器和服務器對URL長度有限制,例如IE對URL長度的限制是2083字節(2K+35)。對於其餘瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決於操做系統的支持。所以對於GET提交時,傳輸數據就會受到URL長度的限制
  2. POST : 因爲不是經過URL傳值,理論上數據不受限。但實際各個WEB服務器會規定對post提交數據大小進行限制,Apache、IIS6都有各自的配置。

1.5.3 安全性

POST的安全性要比GET的安全性高。websocket

注意:這裏所說的安全性和上面GET提到的「安全」不是同個概念。上面「安全」的含義僅僅是不做數據修改,而這裏安全的含義是真正的Security的含義,好比:經過GET提交數據,用戶名和密碼將明文出如今URL上,由於(1)登陸頁面有可能被瀏覽器緩存, (2)其餘人查看瀏覽器的歷史紀錄,那麼別人就能夠拿到你的帳號和密碼了

二 、實現原理

HTTPS是一種經過計算機網絡進行安全通訊的傳輸協議,經由HTTP進行通訊,利用SSL/TLS創建全信道,加密數據包。
HTTPS使用的主要目的是提供對網站服務器的身份認證,同時保護交換數據的隱私與完整性。
PS:TLS是傳輸層加密協議,前身是SSL協議,由網景公司1995年發佈,有時候二者不區分

2.1 對稱祕鑰加密

客戶端本身封裝一種加密算法,將給服務端發送的數據進行加密,而且將數據加密的方式即祕鑰發送給密文,服務端收到祕鑰和數據,用祕鑰進行解密

2.2 非對稱祕鑰加密

服務器端爲客戶端統一建立一個加密方式,由服務器端指定建立,稱爲公鑰,服務器端所建立的加密方式統一的分發給即將要進行服務器鏈接的客戶端,客戶端只須要將密文發送給服務端,服務端經過公鑰加密,可是的創建兩次鏈接,先傳送公鑰,也存在隱患,模擬某些服務器的公鑰,發送的數據被第三方截取。

2.3 證書祕鑰加密

用到三方機構,數字證書,服務器端和客戶端足以信任的機構,服務器端將公鑰發送給三方機構,在三方機構 作一個防僞標識,數字簽名,公鑰攜帶三方機構的證書發送給客戶端,客戶端使用公鑰對數據進行加密。

image.png

三 、http通訊原理

1572246937380.png

客戶端輸入URL回車,DNS解析域名獲得服務器的IP地址,服務器在80端口監聽客戶端請求,端口經過TCP/IP協議(能夠經過Socket實現)創建鏈接。HTTP屬於TCP/IP模型中的運用層協議,因此通訊的過程實際上是對應數據的入棧和出棧

1572246985902.png

1572247007054.png

報文從運用層傳送到運輸層,運輸層經過TCP三次握手和服務器創建鏈接,四次揮手釋放鏈接。

四 、WebSocket

4.1 WebSocket與HTTP

  • WebSocket 的最大特色就是,服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息,是全雙工通訊。
  • TTP 有 1.1 和 1.0 之說,也就是所謂的 keep-alive,把多個 HTTP 請求合併爲一個,可是 Websocket 實際上是一個新協議,跟 HTTP 協議基本沒有關係,只是爲了兼容現有瀏覽器,因此在握手階段使用了HTTP

1572247145127.png

4.2 WebSocket - 握手

WebSocket是基於HTTP協議的,或者說借用了HTTP協議來完成一部分握手

GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13  # 版本
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
  • Sec-WebSocket-Key是一個Base64 encode的值,這個是瀏覽器隨機生成的,發送給服務器

  • 服務端從請求(HTTP的請求頭)信息中提取Sec-WebSocket-Key,利用magic_string和Sec-WebSocket-Key進行hmac1加密,再進行base64加密

  • 將加密結果響應給客戶端,服務器會返回下列東西,表示已經接受到請求, 成功創建WebSocket

headers = get_headers(data)  # 提取請求頭信息
# 對請求頭中的sec-websocket-key進行加密
response_tpl =  "HTTP/1.1 101 Switching Protocols\r\n" \
                "Upgrade:websocket\r\n" \
                "Connection: Upgrade\r\n" \
                "Sec-WebSocket-Accept: %s\r\n" \
                "WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"

value = headers['Sec-WebSocket-Key'] + magic_string
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'))
# 響應【握手】信息
conn.send(response_str.encode("utf8"))
  • 依然是固定的,經過Upgrade告訴客戶端即將升級的是WebSocket協議,而不是mozillasocket,urnarsocket** 或者shitsocket。

  • 而後,Sec-WebSocket-Accept這個則是通過服務器確認,而且加密事後的Sec-WebSocket-Key。服務器:好啦好啦,知道啦,給你看個人ID CARD來證實行了吧。

  • Sec-WebSocket-Protocol則是表示最終使用的協議。

4.3 WebSocket - 解密

hashstr = b'\x81\x83\xceH\xb6\x85\xffz\x85'
# b'\x81  \x83  \xceH\xb6\x85\xffz\x85'
# 將第二個字節也就是 \x83 第9-16位 進行與127進行位運算
payload = hashstr[1] & 127
print(payload)
if payload == 127:
    extend_payload_len = hashstr[2:10]
    mask = hashstr[10:14]
    decoded = hashstr[14:]

# 當位運算結果等於127時,則第3-10個字節爲數據長度
# 第11-14字節爲mask 解密所需字符串
# 則數據爲第15字節至結尾

if payload == 126:
    extend_payload_len = hashstr[2:4]
    mask = hashstr[4:8]
    decoded = hashstr[8:]
# 當位運算結果等於126時,則第3-4個字節爲數據長度
# 第5-8字節爲mask 解密所需字符串
# 則數據爲第9字節至結尾

if payload <= 125:
    extend_payload_len = None
    mask = hashstr[2:6]
    decoded = hashstr[6:]
# 當位運算結果小於等於125時,則這個數字就是數據的長度
# 第3-6字節爲mask 解密所需字符串
# 則數據爲第7字節至結尾

str_byte = bytearray()
for i in range(len(decoded)):
    byte = decoded[i] ^ mask[i % 4]
    str_byte.append(byte)

print(str_byte.decode("utf8"))

4.4 WebSocket - 加密

import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)

if length < 126:
    token += struct.pack("B", length)

elif length == 126:
    token += struct.pack("!BH", 126, length)

else:
    token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
print(msg)

五 、WebSocket - 做用

5.1 ajax輪詢和long poll的原理。

  1. ajax輪詢的原理很是簡單,讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息

  2. long poll 其實原理跟 ajax輪詢差很少,都是採用輪詢的方式,不過採起的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起請求後,若是沒消息,就一直不返回 Response 給客戶端。直到有消息才返回,返回完以後,客戶端再次創建鏈接,周而復始。

5.2 WebSocket

  1. 這兩種方式都不是最好的方式,須要不少資源,一種須要更快的速度,一種須要更多的’電話。這兩種都會致使’電話’的需求愈來愈高。

  2. 忘記說了 HTTP 仍是一個無狀態協議。通俗的說就是,服務器由於天天要接待太多客戶了,是個健忘鬼,你一掛電話,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴服務器一遍。

  3. 只須要通過一次 HTTP 請求,就能夠作到源源不斷的信息傳送了,當服務器完成協議升級後(HTTP --—> Websocket),服務端就能夠主動推送信息給客戶端啦。

相關文章
相關標籤/搜索