HTTP、HTTPS、WebSocket


一 、HTTP

image.png

1.1 HTTP發展史

1.1.1 什么是HTTP

超文本傳輸協議,是一個基於請求與響應,無狀態的,應用層的協議,常基於TCP/IP協議傳輸數據,互聯網上應用最為廣泛的一種網絡協議,所有的WWW文件都必須遵守這個標准。設計HTTP的初衷是為了提供一種發布和接收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個部分組成,下圖給出了請求報文的一般格式

image.png

1.3.1 請求行

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

1.3.2 請求頭

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

  • 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的安全性高。

注意:這里所說的安全性和上面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),服務端就可以主動推送信息給客戶端啦。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM