国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

再也不學AJAX了!(三)跨域獲取資源 ③ - WebSocket & postMessag

liangzai_cool / 2691人閱讀

摘要:一基于維基百科的定義,是一種在單個連接上進行全雙工通訊的協議。讓我們看看這個模型的具體實現下面是客戶端告知服務端要升級為協議的報頭下面是服務端向客戶端返回的響應報頭想知道這些報頭中的字段中代表什么可以參考維基百科下的說明。

讓我們先簡單回顧一下之前談到的內容,AJAX是一種無頁面刷新的獲取服務器資源的混合技術。而基于瀏覽器的“同源策略”,不同“域”之間不可以發送AJAX請求。但是在某些情境下,我們需要“跨域獲取資源”,為了滿足這一需求,我們可以使用“JSONP”與“CORS”兩種技術。

現在,我們將要簡要了解“跨域共享資源”的另外兩種方式:WebSocket 和 postMessage。讓我們先大概看看他們是什么,以及究竟是基于怎樣的原理,滿足了我們的需求 - “跨域獲取資源”。

一、WebSocket

基于維基百科的定義,WebSocket是一種在單個TCP連接上進行全雙工通訊的協議。在這里我并不打算解釋“TCP連接”和“全雙工通訊”這兩個專業術語(這樣做會讓這篇文章變得很長,而且也偏離了我們的主題),讓我們聚焦這段定義的最后兩個字協議

說到協議,你是否聯想到“HTTP協議”?沒錯,HTML5標準之所以提出了一種新的互聯網通信協議 - WebSocket,就是為了彌補在某些情景下使用HTTP協議通信的一些不足。但是注意,這并不意味WebSocket協議就可以完全取代HTTP協議了,其實兩者的關系更像是兩兄弟,各自有著各自擅長的領域,而且時不時還一同協作解決難題。

那么上面提到的某些情景具體是指什么呢?答案是“服務端與客戶端的雙向通信”。我們知道,當我們使用HTTP協議時,客戶端與服務端的通信模式始終是由客戶端向服務端發送請求,服務端只負責驗證請求并返回響應。

我們可以這樣想象,在HTTP協議下,服務端扮演著“守門人”的角色,而客戶端則是一個郵局,它每發送一個請求就像是委托一個信使攜帶一封信(信里注明自己的身份和需要獲取資源的名稱)到服務端,當信使到達時,“守門人”會拆開信封,檢查里面的身份信息,如果身份合法則打開資源寶庫的大門,將相應的資源交給信使,令其返回給客戶端。

在這個故事里,服務端的角色有些枯燥呆板對吧?不僅如此,故事中服務端扮演的“守門人”角色還患有嚴重的臉盲癥,在工作中他只“認信不認人”,也就是說客戶端發送的每一個請求,對于服務而言都是全新的,守門人不會因為信使上次來過,或是收到兩次相同的信而覺得眼熟,對信使有額外的寒暄。這也就是為什么我們說HTTP協議是“無狀態的”。乍看起來,這似乎有些不合理,但是這種設計卻使服務器的工作變得簡單可控,提升了服務器的工作效率。

但是這樣的設計仍然存在兩個問題:

每一個請求都需要身份驗證,這對于用戶而言意味著需要在每一次發送請求時輸入身份信息;

當客戶端所請求的資源是動態生成的時,客戶端無法在資源生成時得到通知(還記得吧,服務器只是一個原地不動的“守門人”);

如何解決這兩個問題呢?對于前者,答案是使用“Cookie”,而對于后者,則輪到我們今天的主角“WebSocket”大顯身手。

在討論WebSocket之前,讓我們先稍微繞點路,談談“Cookie”是如何解決“每一個請求都需要身份驗證”的問題的。

(一)為HTTP協議添加狀態 - Cookie

我們之前提到,HTTP協議下,客戶端與服務端的通信是“無狀態”的,也就是說,如果服務器中的某部分資源是由某個客戶專屬的,那么每當這個客戶想要獲取資源時,都需要首先在瀏覽器中輸入賬號密碼,然后再發送請求,并在被服務器識別身份信息成功后獲取請求的資源。我們當然不想每次發送一個請求都要輸入一遍賬號密碼,因此我們需要Cookie,這個既可以存儲在瀏覽器,又會被瀏覽器發送HTTP請求時默認發送至服務端,并且還受瀏覽器“同源策略”保護的東西幫助我們提高發起一次請求的效率。

在有了Cookie之后,我們可以在一次會話中(從用戶登錄到瀏覽器關閉)只輸入一次賬號密碼,然后將其保存在Cookie中,在整個會話期間,Cookie都會伴隨著HTTP請求的發送被服務器識別,從而避免了我們重復的輸入身份信息。

不僅如此,基于Cookie的特性:可以保存在瀏覽器內,還會在瀏覽器發送HTTP請求時默認攜帶,服務端也可以操作Cookie。Cookie還可以幫助我們節省網絡請求的發起數量。例如,當我們在制作一個購物網站時,我們當然不希望用戶在每添加一個商品到購物車就向服務器發送一個請求(請求數量越少,服務器壓力就越小),此時,我們就可以將添加商品所導致的數據變動存儲在Cookie內,然后等待下次發送請求時,一并發送給服務器處理。

現在我們可以說,Cookie的出現,為無狀態的HTTP協議通信添加了狀態。

最后需要注意,Cookie大多數情況下,都保存著用戶的身份信息,因此各種惡意攻擊者對于Cookie的攻擊便花樣百出,層出不窮。其本質上就是想要獲得用戶的Cookie,再利用其中的身份信息偽裝成用戶獲取相應資源,而瀏覽器的“同源策略”本質上就是保護用戶的Cookie信息不會泄露。

(二)讓服務器也動起來 - WebSocket

繞了一個小彎,現在可以回過頭來繼續談談我們的主角WebSocket了。再讓我們回憶一下WebSocket要解決的問題:

客戶端無法獲知請求的動態資源何時到位“,讓我們描述的更詳細一點,有時候客戶端想要請求的資源,服務器需要一定時間后才能返回(比如該資源依賴于其他服務器的計算返回結果),由于在HTTP協議下,網絡通信是單向的,因此服務器并不具備當資源準備就緒時,通知瀏覽器的功能(因為我們要保障服務器的工作效率)。因此,基于HTTP協議通常的做法是,設置一個定時器,每隔一定時間由瀏覽器向服務器發送一次請求以探測資源是否到位。

這種做法顯然浪費了很多請求,換句話說,浪費了很多帶寬(我們每個請求都要攜帶Cookie和報頭,這些都會占用帶寬傳輸),不僅低效率,而且也不夠優雅。

理所當然的,在這種情況下,我們希望當服務器資源到位時,能夠主動通知瀏覽器并返回相應資源。而為了實現這一點,HTML5標準推出了WebSocket協議,使瀏覽器和服務器實現了雙向通信,更妙的是,除了IE9及以下的IE瀏覽器,所有的瀏覽器都支持WebSocket協議。

讓我們也同樣構建一個基于WebSocket協議的心智模型,在這個心智模型中,服務端扮演的角色發生了一些改變,服務端不再只是一個“守門人”,同時它也運營著一個和客戶端一樣的“郵局”,也就是說,他也擁有了可以向客戶端發送數據的能力。至此一個完整的基于WebSocket協議的通信流程為:

客戶端派發一個信使向服務器送信,服務器扮演的“守門人”檢查信件,發現信件中寫到“讓我們用更加潮流的WebSocket方式交流吧”,服務器在在信件末尾添加上一句“沒問題,瀏覽器伙計”,讓信使原路返回告知瀏覽器。當瀏覽器再次向服務器告知收到消息時(第三次握手),服務器就開始運轉“郵局”,向客戶端派發信使與瀏覽器互發信息,轉發資源。

讓我們看看這個模型的具體實現:

下面是客戶端告知服務端要升級為WebSocket協議的報頭:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

下面是服務端向客戶端返回的響應報頭:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

想知道這些報頭中的字段中代表什么?可以參考維基百科下的說明。

(三)客戶端發起WebSocket請求

既然我們已經為了解釋“什么是WebSocket”,“WebSocket的意義”花了那么多篇幅,那么不妨添加上最后一個環節,讓這個主題變得更加完整,接下來我們將要簡單講解一下客戶端如何發起一個WebSocket請求。

像發起AJAX請求一樣,發起WebSocket請求需要借助瀏覽器提供的WebSocket對象,該對象提供了用于創建和管理WebSocket連接,以及通過該連接收發數據的API。所有的瀏覽器都默認提供了WebSocket對象。讓我們看看該對象的用法:

和使用XHRHttpRequest對象一樣,我們首先要實例化一個WebSocket對象:

var ws = new WebSocket("wss://echo.websocket.org")

傳入的參數為響應WebSocket請求的地址。

同樣類似AJAX的是,WebSocket對象也有一個readyState屬性,用來表示對象實例當前所處的鏈接狀態,有四個值:

0:表示正在連接中(CONNECTING);

1:表示連接成功,可以通信(OPEN);

2:表示連接正在關閉(CLOSING);

3:表示連接已經關閉或打開連接失敗(CLOSED);

我們可以通過判斷這個值來執行我們相應的代碼。

除此之外,WebSocket對象還提供給我們一系列事件屬性,使我們控制連接過程中的通信行為:

onopen:用于指定連接成功后的回調函數;

onclose:用于指定連接關閉后的回調函數;

onmessage:用于指定收到服務器數據后的回調函數;

onerror:用于指定報錯時的回調函數;

通過.send()方法,我們擁有了向服務器發送數據的能力(WebSocket還允許我們發送二進制數據):

ws.send("Hi, server!")

如何知道何時我們的數據發送完畢呢?我們需要使用WebSocket對象的bufferedAmount屬性,該屬性的返回值表示了還有多少字節的二進制數據沒有發送出去,所以我們可以通過判斷該值是否為0而確定數據是否發送結束。

var data = new ArrayBuffer(1000000)
ws.send(data)

if (socket.bufferedAmount === 0) {
    // 發送完畢
} else {
    // 還在發送
}

OK,目前為止我們花了大量篇幅解釋了WebSocket協議是什么,它能夠幫助我們做什么,以及客戶端發送WebSocket請求的方式。但是目前為止,我們還是沒有談論一丁點關于WebSocket是如何幫助我們繞過瀏覽器的“同源策略”讓我們實現“跨域資源共享”,你是否已經有點等的不耐煩了?

但是別急,當你清楚的了解到WebSocket是什么之后,答案就呼之欲出了,那就是當客戶端與服務端創建WebSocket連接后,本身就可以天然的實現跨域資源共享,WebSocket協議本身就不受瀏覽器“同源策略”的限制(還記得吧,同源策略只是限制了跨域的AJAX請求?),所以問題本身就不成立(有點賴皮是吧?)。

但是你可能又會問,如果沒有瀏覽器“同源策略”的限制,那么用戶的Cookie安全又由誰來保護呢?問得好,看來你有認真閱讀上面的文字,為了解答這個問題,讓我們換一種角度思考,我們說過Cookie的存在就是為了給無狀態的HTTP協議通訊添加狀態,因為Cookie是明文傳輸的,且通常包含用戶的身份信息,所以非常受到網絡攻擊者的“關注”。但是想想WebSocket協議下的通訊機制,客戶端和服務端一旦建立連接,就可以順暢的互發數據,因此WebSocket協議本身就是“有狀態的”,不需要Cookie的幫忙,既然沒有Cookie,自然也不需要“同源策略”去保護,因此其實這個問題也不成立。

至此,已經將關于WebSocket的所有內容都大致講述了一遍,真沒想到是如此巨大的工作量。看來本篇文章不應該叫做“再也不學AJAX了”,而是“再也不學AJAX,JSONP,CORS,WebSocket..”。

真是了不起。

二、postMessage

回頭一看,我們已經在“跨域”這個主題上整整停留了三篇文章,涉及的技術包括JSONP,CORS與WebSocket。需要注意的是,以上這些跨域技術都只適用于客戶端請求異域服務端資源的情景。而除此之外,有時候我們還需要在異域的兩個客戶端之間共享數據,例如頁面與內嵌iframe窗口通訊,頁面與新打開異域頁面通訊。

這就是使用HTML5提供的新API -- postMessage的時候了。

使用postMessage技術實現跨域的原理非常簡單,一方面,主窗口通過postMessageAPI向異域的窗口發送數據,另一方面我們在異域的頁面腳本中始終監聽message事件,當獲取主窗口數據時處理數據或者以同樣的方式返回數據從而實現跨窗口的異域通訊。

讓我們用具體的業務場景與代碼進一步說明,假如我們的頁面現在有兩個窗口,窗口1命名為“window_1”, 窗口2命名為“window_2”,當然,窗口1與窗口2的“域”是不同的,我們的需求是由窗口1向窗口2發送數據,而當窗口2接收到數據時,將數據再返回給窗口1。先讓我們看看窗口1script標簽內的代碼:

// window_1 域名為 http://winodow1.com:8080
window.postMessage("Hi, How are you!", "http://window2.com:8080")

可以看到,postMessage函數接收兩個參數,第一個為要發送的信息(可以是任何JavaScript類型數據,但部分瀏覽器只支持字符串格式),第二個為信息發送的目標地址。讓我們再看看窗口2script標簽內的代碼:

// window_2 域名為 http://window2.com:8080
window.addEventListener("message", receiveMessage, false)

function receiveMessage(event) {
    // 對于Chorme,origin屬性為originalEvent.origin屬性
    var origin = event.origin || event.originalEvent.origin
    if (origin !== "http://window1.com:8080") {
        return 
    }
    window.postMessage("I"m ok", "http://window1.com:8080")
}

看到了嗎,我們在window上綁定了一個事件監聽函數,監聽message事件。一旦我們接收到其他域通過postMessage發送的信息,就會觸發我們的receiveMessage回調函數。該函數會首先檢查發送信息的域是否是我們想要的(之后我們會對此詳細說明),如果驗證成功則會像窗口1發送一條消息。

看起來很好懂不是嗎,一方發送信息,一方捕捉信息。但是,我需要格外提醒你的是所有“跨域”技術都需要關注的“安全問題”。讓我們想想postMessage技術之所以能實現跨域資源共享,本質上是要依賴于客戶端腳本設置了相應的message監聽事件。因此只要有消息通過postMessage發送過來,我們的腳本都會接收并進行處理。由于任何域都可以通過postMessage發送跨域信息,因此對于設置了事件監聽器的頁面來說,判斷到達頁面的信息是否是安全的是非常重要的事,因為我們并不想要執行有危險的數據。

那么接下來的問題便是,如何鑒別發送至頁面的信息呢?答案是通過 message事件監聽函數的事件對象,我們稱它為event,該對象有三個屬性:

data:值為其他window傳遞過來的對象;

origin:值為消息發送方窗口的域名;

source:值為對發送消息的窗口對象的引用;

很顯然的,我們應該著重檢測event對象的origin屬性,建立一個白名單對origin屬性進行檢測通常是一個明智的做法。

最后,再讓我們談談postMessage對象的瀏覽器兼容性,這方面到是很幸運,除了IE8以下的IE瀏覽器,所有的瀏覽器都支持postMessage方法!

至此,我們終于完全講完了“跨域共享資源”這一主題。花了不少力氣是吧?希望這是值得的。







? Hey!到這里《再也不學AJAX了!》這個專題系列就完全結束了,還記得我們的初心嗎?我希望你能通過閱讀這個系列的文章,以較為輕松的方式,系統完整地掌握AJAX技術,從此再也不用刻意學習零散的AJAX知識。希望我達成了我的目標,也希望你在閱讀學習的過程中感到愉快。

關于AJAX技術這個專題,其實我還想講述的兩個話題是:更優雅的資源獲取方式:fetch API 以及 深入jQuery:AJAX的實現,但是鑒于我個人時間精力有限(完成一個系列文章真的比我想的要付出更多時間!),就決定暫時先放下,等將來有機會再以這個系列的番外篇的形式補充上去,希望你們可以理解和接受:)。

這是我第一次在技術平臺中以“系列”的方式發表技術文章,我個人覺得這樣的方式更容易令人在整體上把握和理解一個技術,從而做到更靈活熟練的使用。希望你們也認同這一點并在閱讀過程中感到愉快。之后,我也會繼續在專欄中發表關于Web開發技術的系列文章,希望得到你們的認可和支持。

最后,再談談我在技術平臺發表文章的初心:之所以開始在各平臺(目前為稀土掘金和segmentfault)發表技術文章,主要是為了幫助我消化知識,鍛煉寫作的文筆,驗證我對某個技術的理解是否正確,以及積攢人氣滿足虛榮心。在這個過程中,也希望讀者能夠通過閱讀我的文章,加深對某一技術的理解。我認為這是一件雙贏的事情,因此我十分歡迎,甚至是期待你在閱讀我任何文章的過程中都能夠:

如果覺得有所收獲,毫不猶豫的點擊贊賞按鈕(我真的真的會很開心?);

如果想到了其他相關知識,或發現我對某個技術的理解不正確,毫不猶豫的在評論區留言與我交流

如果對于我講述中的某個概念還是不懂,毫不猶豫的在留言區告知我你的困惑,我會思考怎么樣把這個概念講述的更加清楚明白;

如果覺得我的文章不錯,毫不猶豫的將我的文章推薦給他人,邀請他們成為我的讀者;

如果你覺得閱讀我的文章所花費的時間很值得,對你有很大幫助并且也認可我的勞動成果,你大可以點擊下方紅色的“贊賞支持”按鈕為這篇文章付費,同時表達你對我創作的認可與支持。寫作能夠對人有益又能獲得報酬,這著實令人倍感欣慰。

我的創作和成長需要你們的幫助和支持,作為報答,我會持續發布優質的文章,陪同你們一起成長。關注我,一起加油吧! ?

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92392.html

相關文章

  • 再也不學AJAX!(跨域獲取資源 ② - JSONP & CORS

    摘要:瀏覽器的同源策略固然保障了互聯網世界的數據隱私與數據安全,但是如果當我們需要使用跨域請求資源時,同源策略又會成為開發者的阻礙。我們之前提到過,如果想要繞過瀏覽器同源策略,實現使用技術跨域獲取資源,需要服務端和客戶端的協同合作。 瀏覽器的同源策略固然保障了互聯網世界的數據隱私與數據安全,但是如果當我們需要使用AJAX跨域請求資源時,同源策略又會成為開發者的阻礙。在本文中,我們會簡單介紹需...

    enda 評論0 收藏0
  • 再也不學AJAX!(一)AJAX概述

    摘要:需要注意的是,并不是的替代品,兩者各自有其適應的場景。但為了方便交流,我們通常將獲取資源的一方稱為客戶端主要的工具是瀏覽器,而將派發資源的一方稱為服務端又稱為服務器。它可以幫助我們為之后概念細節的學習打下良好基礎。 再也不學AJAX了是一個與AJAX主題相關的文章系列,包含以下三個部分的內容: AJAX概述:主要回答AJAX是什么這個問題; 使用AJAX:介紹如何通過JavaSc...

    neu 評論0 收藏0
  • 再也不學AJAX!(跨域獲取資源 ① - 同源策略

    摘要:瀏覽器的同源策略瀏覽器所遵守的同源策略是指限制不同源之間執行特定操作。這正是同源策略想要規避的安全隱患。目前為止,你已經充分了解同源策略這個主題。 我們之前提到過,AJAX技術使開發者能夠專注于互聯網中數據的傳輸,而不再拘泥于數據傳輸的載體。通過AJAX技術,我們獲取數據的方式變得更加靈活,可控和優雅。 但是AJAX技術并不是一把萬能鑰匙,互聯網中的數據隱私和數據安全(例如你的銀行賬號...

    godlong_X 評論0 收藏0
  • 雜談:前端Web通信

    摘要:作為開發同學的小伙伴客戶端的瀏覽器,有點小調皮還做了一個同源策略的限制,當我們的數據請求遇到不同源的情況下跨域,我們就得嘗試其它的通信方法,不能一條道走到黑。 showImg(https://segmentfault.com/img/bVburZO?w=600&h=450); Web2.0以來,Ajax的出世,解決了傳統表單提交頁面跳轉,閃爍白屏等問題。使得Web頁面可以實現局部更新,...

    Betta 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<