官术网_书友最值得收藏!

2.3 網絡協議

網絡協議是連接客戶端與服務器端的橋梁。學習與網絡協議相關的知識,能讓我們對網絡通信過程以及通信所需條件有一個全面的認識,可以幫助我們提升爬蟲技術或者設計出更有效的反爬蟲方案。

常見的網絡協議有HTTP協議、WebSocket協議、FTP協議、SSH協議和View-Source協議等。在本節中,我們主要學習在Web應用中使用最多的HTTP協議和WebSocket協議。

2.3.1 認識HTTP

當我們在瀏覽器的地址欄中輸入http://www.huawei.com并按下回車鍵時,我們就通過瀏覽器向服務器發起了一次HTTP請求,服務器會根據本次請求的信息將服務器上的資源響應給瀏覽器,瀏覽器按照規則進行渲染后,得到如圖2-18所示的網頁。

圖2-18 瀏覽器渲染后的網頁

超文本傳輸協議(Hyper Text Transfer Protocol,簡稱HTTP)是互聯網中應用最為廣泛的一種應用層協議,所有的超媒體文檔都必須遵守這個標準。HTTP協議是為Web瀏覽器和Web服務器之間的通信設計的,但是也可以用于其他目的。HTTP協議是無狀態協議,這意味著服務器不會在兩個請求之間保留任何的數據或者狀態。雖然它通常基于TCP/IP層,但是它可以在任何可靠的傳輸層上使用。

HTTP協議有多個版本,如HTTP/1.0、HTTP/1.1和HTTP/2.0,其中應用最廣泛的是HTTP/1.1。本書介紹的HTTP協議相關知識基于HTTP/1.1版本,RFC文檔編號為2616(詳見https://tools.ietf.org/html/rfc2616)。

如圖2-19所示,HTTP協議遵循傳統的客戶端?服務端器模型,客戶端打開連接以發出請求,然后等待服務器端響應。

圖2-19 客戶端-服務器端模型

HTTP協議可以減少網絡傳輸次數,使瀏覽器更加高效,它不僅保證了計算機正確快速地傳輸超文本文檔,還能確定傳輸文檔中的哪一部分或優先顯示哪一部分(如文本先于圖形)等。

2.3.2 資源與資源標識符

HTTP請求的目標稱為資源,它可以是文檔、照片、視頻或其他任何東西。資源由統一資源標識符(URI)進行標識,在Web頁面中,資源的標識和位置主要由單個URL(統一資源定位符)給出。URL通常被稱為Web網址,是最常見和使用最多的URI。當我們在瀏覽器的地址欄中輸入URL或者單擊超鏈接時,就確定了要獲取的資源的地址,比如我們在瀏覽器的地址欄中輸入https://developer.mozilla.org,就是告知瀏覽器加載目標服務器上的對應資源。

URL由不同的部分組成,一些是強制性的,另一些是可選的。RFC2616給出的HTTP URL格式如下:

http_URL = "http:" "http://" host [ ":" port ] [ abs_path [ "?" query ]]

URL由協議類型名稱、域名、端口號、資源路徑和查詢參數組成,其中端口號的默認值為80。在實際場景中,我們看到的URL會更復雜,如http://www.exp.com:80/path/to/file.html?key=v&name=abc#python,它由協議類型名稱、域名、端口號、資源路徑、查詢參數和錨點組成,對應的URL結構如圖2-20所示。

圖2-20 URL結構

每個組成部分的作用如下。

? 協議類型名稱:表示瀏覽器必須使用的協議,通常是HTTP協議或其安全版本HTTPS。

? 域名:表示正在請求哪個Web服務器,也可以直接使用主機地址。

? 端口號:用于表示Web服務器上的資源入口,如Web服務器使用HTTP協議的標準端口號(HTTP為80,HTTPS為443)來授予對其資源的訪問權限。

? 資源路徑:Web服務器上資源的路徑,在Web的早期,這樣的路徑代表了Web服務器上的物理文件位置,如今它主要是由Web服務器處理的抽象標識。

? 查詢參數:用&符號分隔的鍵值對。在將資源返回給用戶之前,Web服務器可以使用這些參數來執行額外的操作。每個Web服務器都有自己的參數規則,了解特定Web服務器如何處理參數的唯一可靠方法是詢問Web服務器的所有者或應用程序的開發者。

? 錨點:資源內部的一種“書簽”,瀏覽器會根據錨點將對應的內容呈現給用戶,而不需要用戶滑動頁面來尋找內容。例如,在HTML文檔中,瀏覽器將滾動到定義錨點的位置;在視頻或音頻文檔中,瀏覽器將嘗試轉到錨點所代表的時間。值得注意的是,#之后的部分(也稱片段標識符)永遠不會隨請求一起發送到服務器。

除了HTTP協議和HTTPS協議之外,服務器和瀏覽器也能夠處理其他協議,如表2-13所示。

表2-13 協議名稱及描述

URN是使用較少的URI,叫作統一資源名稱,它用于在特定名稱空間中按名稱標識資源,比如urn:ietf:rfc:7230標識了IETF規范7230。要注意的是,URN僅僅用于標識資源,并不能像URL那樣定位資源。

2.3.3 HTTP請求與響應

HTTP請求是指從客戶端向服務器端發起的請求消息。以瀏覽器為例,它向Web服務器發出了一條請求其實就是向服務器傳遞了一個數據塊,這個數據塊也稱為請求信息。HTTP請求信息由請求行、請求頭和請求正文組成。

請求行以請求方法開頭并以空格分隔,后面跟著請求URI和協議的版本號,最后以CRLF作為結尾,且在結尾處不允許出現單獨的CR或LF字符。請求行的格式如下:

Method Request-URI HTTP-Version CRLF

示例:GET http://www.exp.com/user.html HTTP/1.1 (CRLF)。

除了GET之外,常見的請求方法還有POST、PUT、DELETE、OPTIONS和HEAD等。

請求頭可以聲明瀏覽器所支持的語言、瀏覽器版本以及希望接受的數據類型等。例如,我們使用瀏覽器訪問http://www.example.com時,請求頭的內容為:

Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Host: www.example.com
If-Modified-Since: Fri, 09 Aug 2013 23:54:35 GMT
If-None-Match: "1541025663"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/72.0.3626.119 Safari/537.36

下面簡要說明一些常用頭域。

? Accept:客戶端希望接受的數據類型,比如Accept: text/html代表客戶端希望接受的數據類型是HTML類型。

? Accept-*:指定客戶端可接受的內容,比如Accept-Encoding用于指定可接受的編碼, Accept-Language用于指定可接受的語言類型。

? Content-Type:互聯網媒體類型(簡稱MIME類型),代表具體請求的媒體類型信息(比如text/html代表HTML格式,image/gif代表GIF圖片,application/json代表JSON類型)。

? Host:指定請求資源的域名(或IP)和端口號,內容為請求URL的原始服務器或網關的位置。

? Cookie:可以理解為在HTTP協議下,服務器或其他腳本語言維護客戶端信息的一種方式,是保存在客戶端(比如瀏覽器)的文本文件。Cookie中往往包含客戶端或者用戶的相關信息。

? Referer:記錄上一次訪問的頁面地址,也可以理解為標識此次請求的來源URL。

請求頭和請求正文之間是一個空行,這個空行表示請求頭已經結束,接下來的內容就是請求正文。請求正文包含本次請求提交的查詢參數。GET請求的請求正文示例如下:

keyword=德瑪西亞&time=1542398562015

上面的請求正文只有一行內容,包含keyword頭域和time頭域。很多開發者將頭域稱為字段,這兩種叫法都是可以的,在本書中我們使用“頭域”這一稱呼。實際上,HTTP請求正文還可以包含更多內容,比如使用網頁版有道翻譯時,請求正文如下:

i: 德瑪西亞之翼
from: AUTO
to: AUTO
smartresult: dict
client: fanyideskweb
salt: 15597905976630
sign: b054ae008ed4bd3499606a3cacc55140
ts: 1559790597663
bv: 81eda17dc48cc8bed5d01154f3dd0136
doctype: json
version: 2.1
keyfrom: fanyi.web
action: FY_BY_REALTlME

HTTP響應是指服務器端根據客戶端的請求返回的信息。HTTP響應由狀態碼、響應頭和響應正文組成。狀態碼是一個3位數字(如200),它的第一位代表了不同的響應狀態。響應狀態共有5種,含義如下。

? 1代表信息響應類,表示接收到請求并且繼續處理,這類響應是臨時響應。

? 2代表處理成功響應類,表示動作被成功接收、理解和接受。

? 3代表重定向響應類,為了完成指定的動作,必須接受進一步處理。

? 4代表客戶端錯誤,表示客戶請求包含語法錯誤或者是不能正確執行的請求。

? 5代表服務器端錯誤,服務器不能正確執行一個正確的請求。

爬蟲工程師通常根據響應狀態碼判斷本次請求的狀態,并決定后續的處理邏輯。表2-14列出了常見的狀態碼及其含義。

表2-14 常見的狀態碼及其含義

要求通信雙方都支持對響應頭域的擴展,如果存在不支持的響應頭域,一般會作為實體頭域處理。表2-15列出了響應頭域及其含義。

表2-15 響應頭域及其含義

響應正文才是請求的最終目的,比如訪問網頁時響應正文為網頁的HTML代碼。爬蟲爬取的網頁數據其實就是服務器端解析請求URL后返回的文本資源,如訪問華為官網的首頁后,得到的響應正文如圖2-21所示。

圖2-21 華為官網響應正文部分截圖

2.3.4 Cookie

Cookie可以理解為在HTTP協議下,服務器或其他腳本語言維護客戶端信息的一種方式,是保存在客戶端(比如瀏覽器)的文本文件,Cookie中往往包含客戶端或者用戶的相關信息。

1. 背景

在客戶端與服務器能夠進行動態交互的Web應用程序出現之前,Cookie還沒有被熟知,是HTTP的無狀態特性促進了Cookie的產生。HTTP的無狀態指的是無狀態協議,HTTP協議對于事務處理沒有記憶能力,缺少狀態意味著如果服務器需要與客戶端進行多次交互,那么客戶端必須在每一次交互時都主動提交身份信息。

面對這個問題,人們想出了兩種能夠保持HTTP連接狀態的技術:Cookie和Session。Cookie是通過客戶端來保持狀態的解決方案。從定義上來說,Cookie就是由服務器發給客戶端的特殊信息,這些信息以文本文件的形式存放在客戶端??蛻舳嗣看蜗蚍掌靼l送請求的時候,都會攜帶這些特殊的信息。在登錄網站的時候,經常能夠看到登錄框處有類似“記住我”的選項,如果勾選了它,那么在重新打開網站甚至第二天訪問該網站時,就不需要進行煩瑣的登錄操作了。因為勾選了這個選項之后,網站會將用戶的身份信息記錄在Cookie中,在瀏覽器下一次向服務器發起請求的時候,會自動攜帶Cookie信息,從而實現自動登錄的功能。

Cookie通過在客戶端存儲身份信息的方式與服務器保持狀態,而Session通過服務器來保持狀態。Session對象會存儲特定用戶會話所需的屬性及配置信息。當用戶在應用程序的Web頁面之間跳轉時,存儲在Session對象中的變量不會丟失,會在整個用戶會話中一直存在下去。如果用戶在訪問Web網頁的時候還沒有Session,則Web服務器將自動創建一個Session對象,當會話過期或被放棄后,服務器也會終止會話。

2. 生存周期與持久性

Cookie在生成時會被指定一個Expire值,該值就是Cookie的生存周期。Cookie在這個周期內是有效的,但是超出周期后就會被清除。如果將Cookie的生存周期設置為0或者負數,那么在你關閉瀏覽器的時候,Cookie就會被瀏覽器清除,下一次打開網頁時再生成新的Cookie值。這個功能常用于金融類網站,因為需要在瀏覽器關閉時立即清除用戶的身份信息,更好地保證信息安全。像這樣即用即清除的Cookie通常稱為會話Cookie,而剛才提到的類似“記住我”或“一周內免登錄”選項,是將Cookie值保存起來,在生存周期內還可以繼續使用,長時間保持用戶狀態的Cookie稱為持久Cookie。

3. 屬性和結構

Cookie由變量名稱和值組成,其中變量既有標準的變量,也有自定義格式的變量,具體格式如下:

NAME=VALUE;Expires=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE

? NAME和VALUE是Cookie的名稱與值。

? Expires通常是一個日期變量,格式為DD-MM-YY HH:MM:SS GMT。正如剛才所說,Expires決定了Cookie的生存周期,值為空就代表這是一個會話Cookie,即Cookie文件將隨著瀏覽器的關閉而自動消失。

? Path屬性規定了在Web服務器上,哪些路徑下的頁面可以獲取服務器設置的Cookie。一般情況下,如果用戶輸入的URL的路徑部分從第一個字符開始包含Path屬性所定義的字符串,瀏覽器就認為通過檢查。如果Path屬性的值為“/”,則Web服務器上所有的WWW資源均可讀取該Cookie。該項設置是可選的,如果省略,則Path的屬性值為Web服務器傳給瀏覽器的資源路徑名。

? Domain指定了可以訪問Cookie信息的域名并且它是一個只寫變量。它規定了哪些Internet域中的Web服務器可以讀取瀏覽器所存的Cookie,即只有來自這個域的頁面才可以使用Cookie中的信息。這項設置是可選的,如果省略它,Cookie的屬性值為該Web服務器的域名。比如將Domain的值設為taobao.com,那么所有類似m.taobao.com或者taobao.com的頁面就可以訪問Cookie中的信息。

? SECURE:如果在Cookie中標記該變量,表明只有當瀏覽器和Web服務器之間的通信協議為加密認證協議時,瀏覽器才向服務器提交相應的Cookie。當前這樣的協議只有一種,即HTTPS。

小技巧Cookie的屬性可以搭配使用,比如借助Domain和Path這兩個屬性,就可有效地控制Cookie文件被訪問的范圍。

4. 產生過程

在正常情況下,Cookie值可以由服務器端或JavaScript代碼設定。在客戶端第一次向服務器端發起請求之前,客戶端是不會有Cookie值的。當客戶端的請求到達服務器端后,服務器端可以將Cookie值寫在響應頭中并返回給客戶端,或者客戶端工具(如瀏覽器)在渲染頁面時,由頁面中的JavaScript代碼生成Cookie值。雙端交互及Cookie的產生過程如圖2-22所示。

圖2-22 雙端交互及Cookie的產生過程

服務器生成Cookie值并將其添加到響應頭中返回給瀏覽器,瀏覽器檢測到響應頭中的Set-Cookie頭域后將對應的Cookie值保存起來,而后每一次請求都會自動攜帶對應的Cookie,除非Cookie過期或者被清除。

除了服務端在響應頭中使用Set-Cookie頭域向客戶端發送Cookie這種方式外,還可以通過JavaScript代碼設置Cookie,比如:

document.cookie = 'async=569cls8fs2';

5. 應用場景

在Web應用中,Cookie常常被用來記錄和驗證用戶的身份信息,有些應用還會將Cookie作為過濾“垃圾流量”的驗證條件。圖2-23是一個登錄框,當用戶輸入賬號和密碼并點擊“登錄”按鈕時,用戶身份信息就會被發送到服務器端進行校驗。

圖2-23 登錄框

當用戶身份通過校驗后,服務器端會根據用戶身份生成Cookie值,并存放在響應頭的Set-Cookie頭域返回給客戶端??蛻舳嗽诤罄m請求網頁時無須重新登錄,只需要攜帶這個Cookie值。

2.3.5 了解HTTPS

由于HTTP協議以明文方式發送請求正文,并且不提供任何方式的數據加密,所以攻擊者可以截取Web瀏覽器和網站服務器之間的傳輸報文,從而讀取傳輸的信息。因此,HTTP協議不適合傳輸敏感信息,如身份證號、登錄密碼等。

超文本傳輸安全協議(Hypertext Transfer Protocol Secure,簡稱HTTPS或HTTPS協議)正是為了解決HTTP協議的缺陷而生的。它在HTTP協議的基礎上加入了安全套接層(Secure Sockets Layer,簡稱SSL協議)和傳輸層安全(Transport Layer Security,簡稱TLS)協議,SSL協議依靠證書來驗證服務器的身份,并為瀏覽器和服務器之間的通信加密。HTTPS協議是以安全為目標的HTTP通道,被廣泛應用于萬維網上的安全敏感通信場景,例如資金交易和網絡支付場景。HTTPS協議和HTTP協議的區別主要為以下幾點。

? HTTP協議不需要證書,而HTTPS協議需要到CA申請證書,大部分證書不是免費的。

? HTTP是超文本傳輸協議,信息以明文方式傳輸,HTTPS則是具有安全性的SSL加密傳輸協議。

? HTTP協議和HTTPS協議使用的是完全不同的連接方式,所用端口也不一樣,前者的默認端口為80,后者的默認端口為443。

? HTTP協議的連接很簡單,并且是無狀態的;HTTPS協議是由SSL協議和HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,也是無狀態的。

SSL協議及其繼任者TLS協議都是為網絡通信提供安全服務的安全協議。TLS協議與SSL協議在傳輸層對網絡連接進行加密。SSL協議提供的服務主要有以下幾種。

? 認證客戶端和服務器端,確保數據發送到正確的地方。

? 加密數據,防止數據中途被竊取。

? 維護數據的完整性,確保數據在傳輸過程中不被改變。

2.3.6 認識WebSocket

WebSocket是一種在單個TCP連接上進行全雙工通信的協議,被廣泛應用于對數據實時性要求較高的場景,如體育賽事播報、股票走勢分析、在線聊天等。WebSocket通信協議于2011年被IETF定為標準RFC6455(詳見https://tools.ietf.org/html/rfc6455),并由RFC7936補充規范。RFC6455共有14個部分,包括WebSocket協議背景與介紹、握手、設計理念、術語約定、雙端要求、掩碼以及連接關閉等內容。

WebSocket協議使客戶端和服務器端之間的數據交換變得更加簡單,它允許交互雙方創建持久連接,同時支持服務器端主動向客戶端推送數據。

在WebSocket協議出現之前,如果Web應用想要實現消息推送與實時數據展示功能,那么需要使用輪詢的手段。輪詢指的是客戶端以特定的時間間隔向服務器端發出HTTP請求,服務器端返回最新的數據給客戶端的過程。這種傳統模式的缺點很明顯,客戶端需要不斷地向服務器端發出請求,而HTTP請求可能包含較長的頭部,但其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬資源。

在這種情況下,HTML5定義了WebSocket協議。WebSocket協議在實現相同功能的情況下可以更好地節省服務器資源,并且能夠讓雙端進行實時通信。在數據傳輸方面,它對二進制的支持和數據壓縮也比HTTP協議更好。

2.3.7 WebSocket握手

與HTTP協議不同的是,WebSocket協議只需要發送一次連接請求。連接請求的完整過程被稱為握手,即客戶端為了創建WebSocket連接而向服務器端發送特定的HTTP請求并聲明升級協議。WebSocket是獨立的、創建在TCP上的協議,雙端通過HTTP/1.1協議進行握手,握手成功后才會轉為WebSocket協議。

使用WebSocket協議進行通信時,客戶端與服務器端的交互順序如下。

(1) 客戶端發起握手請求。

(2) 服務器端收到請求后驗證并返回握手結果。

(3) 連接建立成功,可以互相發送消息。

關于握手的標準,在協議中有相關的說明:

The opening handshake is intended to be compatible with HTTP-based server-side software and intermediaries, so that a single port can be used by both HTTP clients talking to that server and WebSocket clients talking to that server. To this end, the WebSocket client’s handshake is an HTTP Upgrade request :

? GET /chat HTTP/1.1.

? Host: server.example.com.

? Upgrade: websocket.

? Connection: Upgrade.

? Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==.

? Origin: http://example.com.

? Sec-WebSocket-Protocol: chat, superchat.

? Sec-WebSocket-Version : 13.

In compliance with [RFC2616], header fields in the handshake may be sent by the client in any order, so the order in which different header fields are received is not significant.

上方協議內容約定,握手由客戶端發起,握手時使用的協議是HTTP協議而非WebSocket協議。握手時發出的請求叫作升級請求,客戶端在握手階段通過Connection和Upgrade這兩個頭域告知服務器端,要求將通信的協議升級為WebSocket,對應頭域為:

Connection: Upgrade
Upgrade: websocket

Sec-WebSocket-Version和Sec-WebSocket-Protocol這兩個頭域表明通信版本和協議約定, Sec-WebSocket-Key則作為一個防止無端連接的保障(其實在實際應用中并沒有什么保障作用,因為key的值完全由客戶端控制,服務器端并無驗證機制),其他的頭域與HTTP協議中頭域的作用一致。

假設客戶端發出一個符合握手約定的HTTP請求,那么服務器端需要先對信息進行驗證,并將握手結果回復給客戶端。服務器端返回的握手結果包含狀態碼和當前所用的協議,返回信息的含義如下。

? Status Code代表本次握手結果,狀態碼中的101表示連接成功。

? Connection和Upgrade表明當前所用協議。

? Sec-WebSocket-Accept是經過服務器確認并加密過后的Sec-WebSocket-Key。

如果客戶端收到如下響應,那么說明握手成功:

Status Code: 101 Web Socket Protocol Handshake
Sec-WebSocket-Accept: T5ar3gbl3rZJcRmEmBT8vxKjdDo=
Upgrade: websocket
Connection: Upgrade

2.3.8 數據傳輸與數據幀

雙方握手成功并確認協議后,就可以互相發送信息了。WebSocket協議對傳輸規范也作了對應的約定(其中…代表省略部分):

In the WebSocket Protocol, data is transmitted using a sequence of frames. To avoid confusing network intermediaries (such as intercepting proxies) and for security reasons

...

which together define the "Payload data".Certain bits and opcodes are reserved for future expansion of the protocol.

上方協議內容約定,在WebSocket協議中使用幀傳輸數據時,出于安全原因,客戶端發送的消息必須進行掩碼,如果服務器收到未掩碼的幀,則關閉連接。服務器發送給客戶端的消息則不進行掩碼,如果客戶端收到掩碼的幀,就關閉連接。

幀協議用操作碼定義幀類型、有效載荷長度、指定位置的擴展數據和應用程序數據,它們共同定義“有效載荷數據”,還保留了一些位和操作碼,用于將來的擴展協議。幀協議允許將每個消息分成一幀或多幀,在發送到客戶端后由客戶端將信息拼接完整。數據幀的格式如圖2-24所示。

圖2-24 數據幀格式

數據幀由FIN、RSV1、RSV2、RSV3、opcode、MASK、Payload len、Masking-key和Payload Data等部分組成,含義如下。

? FIN:占1位(bit)。

■ 0:不是消息的最后一個分片。

■ 1:是消息的最后一個分片。

? RSV1、RSV2和RSV3:各占1位。

在一般情況下全為0。當客戶端和服務器端協商采用WebSocket協議擴展時,這3個標志位可以不為0,且值的含義由擴展部分進行定義。如果出現非零的值,且并沒有采用WebSocket協議擴展,則連接出錯。

? opcode:占4位。

■ %x0:一個延續幀。當opcode為0時,表示本次數據傳輸采用了數據分片,當前收到的數據幀為其中一個數據分片。

■ %x1:這是一個文本幀(text frame)。

■ %x2:這是一個二進制幀(binary frame)。

■ %x3~%x7:保留的操作代碼,用于后續定義的非控制幀。

■ %x8:連接斷開。

■ %x9:這是一個心跳請求(ping)。

■ %xA:這是一個心跳響應(pong)。

■ %xB~%xF:保留的操作代碼,用于后續定義的控制幀。

? MASK:占1位。

■ 0:不對數據載荷進行掩碼異或操作。

■ 1:對數據載荷進行掩碼異或操作。

? Payload len:占7位或(7 + 16)位或(7 + 64)位。

■ 0~126:數據載荷的長度等于該值。

■ 126:后續2字節代表一個16位的無符號整數,該無符號整數的值為數據的長度。

■ 127:后續8字節代表一個64位的無符號整數(最高位為0),該無符號整數的值為數據的長度。

? Masking-key:占0字節(Byte)或4字節。

■ 當MASK為1時,攜帶了4字節的Masking-key。

■ 當MASK為0時,沒有Masking-key。

掩碼算法:按位進行循環異或運算,先對該位的索引取模來獲得Masking-key中對應的值x,然后將該位與x進行異或運算,而得到真實的數據。注意,掩碼的作用并不是防止數據泄露,而是防止早期版本的協議中存在代理緩存污染攻擊(proxy cache poisoning attacks)等問題。

? Payload Data:載荷數據。

雙端接收到數據幀之后,就可以根據數據幀各個位置的值進行處理或信息提取。

服務器端與客戶端都應該使用WebSocket協議規范中規定的格式,將數據轉化為幀后再發送,而接收方在收到數據幀后需要按照規定的格式進行解包和數據提取。

2.3.9 WebSocket連接

WebSocket協議的數據傳輸是通過網絡進行的,所以我們可以在Chrome瀏覽器的開發者工具中找到Network面板,然后查看WebSocket的連接信息及傳輸的數據。

ECHO TEST是基于WebSocket應用程序和服務器的測試平臺,我們可以通過觀察測試平臺中的網絡信息來加深對WebSocket的理解。測試平臺的網址為http://www.websocket.org/echo.html,打開網址后喚起Chrome開發者工具并切換到Network面板,然后點擊頁面中的Connect按鈕,頁面會主動向測試平臺的WebSocket服務器發起握手,此時可以看到網絡請求記錄中有一條狀態碼為101的記錄,如圖2-25所示。

圖2-25 ECHO TEST頁面及Network面板

接著點擊Send按鈕,此時客戶端向服務器端發送一條信息,服務器端會立即返回內容相同的信息。我們可以在網絡請求記錄中選中此條請求,然后在右側的面板中選擇Frames,就可以看到WebSocket雙端傳輸的數據內容了,如圖2-26所示。

圖2-26 雙端傳輸的數據

圖中箭頭向上的數據是客戶端發送給服務器端的,箭頭向下的是服務器端推送給客戶端的。除了可以使用瀏覽器連接以外,還可以使用其他的客戶端連接,只要握手時發送的請求頭符合要求即可。我們可以使用aiowebsocket這個第三方庫來連接測試平臺的WebSocket服務器。aiowebsocket是一個用Python代碼編寫的開源庫,它非常輕量并且很靈活,GitHub網址為https://github.com/asyncins/aiowebsocket。使用前,我們可以通過Python包管理工具pip安裝它,命令如下:

$ pip install aiowebsocket

然后新建一個名為wsocket.py的文件,并將以下代碼寫入文件:

import asyncio
import logging
from datetime import datetime
from aiowebsocket.converses import AioWebSocket

async def startup(uri):
    async with AioWebSocket(uri) as aws:
        # 初始化 aiowebsocket 庫的連接類
        converse = aws.manipulator
        # 設定需要向服務器發送的信息
        message = b'AioWebSocket - Async WebSocket Client'
        while True:
            # 不斷地向服務器發送信息,并打印輸出信息的內容和時間
            await converse.send(message)
            print('{time}-Client send: {message}'.format(time=datetime.now()
                  .strftime('%Y-%m-%d %H:%M:%S'), message=message))
            # 不斷地讀取服務器推送給客戶端的信息,并打印輸出信息的內容和時間
            mes = await converse.receive()
            print('{time}-Client receive: {rec}'.format(time=datetime.now()
                  .strftime('%Y-%m-%d %H:%M:%S'), rec=mes))

if __name__ == '__main__':
    # 設定遠程服務器地址
    remote = 'wss://echo.websocket.org'
    try:
        # 開啟事件循環,調用指定的方法
        asyncio.get_event_loop().run_until_complete(startup(remote))
            except KeyboardInterrupt as exc:
            logging.info('Quit.')

保存后可以使用以下命令運行wsocket.py文件:

$pyhton wsocket.py

終端會輸出如圖2-27所示的雙端互推消息。

圖2-27 雙端互推消息

在運行結果中,send代表客戶端發送給服務器端的消息,而receive代表服務器端推送給客戶端的消息。

2.3.10 連接保持

WebSocket協議可以保持雙端持久連接,那么一旦連接就不會斷開了嗎?

服務器端開發者如果不希望所有連接都長期打開,可以定時給客戶端發送一個Ping幀,客戶端收到Ping幀后必須回復一個Pong幀。如果客戶端不響應,那么服務器端就可以主動斷開連接。

除此之外,還可以使用其他的方法,比如客戶端連接后,需要先向服務器端發送驗證信息,如果能夠通過服務器端驗證,則保持連接,否則立即關閉。

2.3.11 小結

WebSocket協議可以讓服務器端與客戶端雙向通信,并且保持長久連接。握手時使用的是HTTP協議,握手成功后才升級為WebSocket協議。要注意的是,WebSocket協議規范只作為參考,并不是強制實現,所以服務器端和客戶端的連接條件和消息格式通常由服務器端開發者決定。這意味著服務器端可以在握手時對客戶端進行身份校驗,在消息傳遞或數據幀方面也可以設計一些用于反爬蟲的方法。服務器端可以以任何理由關閉連接,開發者常常利用這些特點限制爬蟲連接或者獲取數據。

主站蜘蛛池模板: 郧西县| 北安市| 卢湾区| 永登县| 潮安县| 云安县| 金华市| 西昌市| 阿拉善右旗| 吉木萨尔县| 西乌珠穆沁旗| 洪雅县| 陈巴尔虎旗| 龙游县| 十堰市| 仁怀市| 布尔津县| 临泽县| 建昌县| 铁力市| 广州市| 黎川县| 阜新市| 双江| 彭阳县| 策勒县| 盐池县| 连州市| 肥西县| 正阳县| 灵丘县| 云龙县| 监利县| 宁阳县| 临安市| 务川| 甘孜县| 鸡西市| 盐源县| 交城县| 慈利县|