0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

websocket協(xié)議的原理

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-09 15:13 ? 次閱讀

WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動(dòng)發(fā)送信息給客戶端。

WebSocket通信協(xié)議于2011年被IETF定為標(biāo)準(zhǔn)RFC 6455,并被RFC7936所補(bǔ)充規(guī)范。

一、WebSocket簡(jiǎn)介

webSocket是什么:

1、WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議

2、WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)

3、在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸

4、需要安裝第三方包:cmd中:go get -u -v github.com/gorilla/websocket

WebSocket 是一種標(biāo)準(zhǔn)協(xié)議,用于在客戶端和服務(wù)端之間進(jìn)行雙向數(shù)據(jù)傳輸。但它跟 HTTP 沒什么關(guān)系,它是一種基于 TCP 的一種獨(dú)立實(shí)現(xiàn)。

以前客戶端想知道服務(wù)端的處理進(jìn)度,要不停地使用 Ajax 進(jìn)行輪詢,讓瀏覽器隔個(gè)幾秒就向服務(wù)器發(fā)一次請(qǐng)求,這對(duì)服務(wù)器壓力較高。另外一種輪詢就是采用 long poll 的方式,這就跟打電話差不多,沒收到消息就一直不掛電話,也就是說,客戶端發(fā)起連接后,如果沒消息,就一直不返回 Response 給客戶端,連接階段一直是阻塞的。

而 WebSocket 解決了 HTTP 的這幾個(gè)難題。首先,當(dāng)服務(wù)器完成協(xié)議升級(jí)后( HTTP -> WebSocket ),服務(wù)端可以主動(dòng)推送信息給客戶端,解決了輪詢?cè)斐傻耐窖舆t問題。由于 WebSocket 只需要一次 HTTP 握手,服務(wù)端就能一直與客戶端保持通訊,直到關(guān)閉連接,這樣就解決了服務(wù)器需要反復(fù)解析 HTTP 協(xié)議,減少了資源的開銷。

WebSocket協(xié)議支持(在受控環(huán)境中運(yùn)行不受信任的代碼的)客戶端與(選擇加入該代碼的通信的)遠(yuǎn)程主機(jī)之間進(jìn)行全雙工通信。用于此的安全模型是Web瀏覽器常用的基于原始的安全模式。 協(xié)議包括一個(gè)開放的握手以及隨后的TCP層上的消息幀。 該技術(shù)的目標(biāo)是為基于瀏覽器的、需要和服務(wù)器進(jìn)行雙向通信的(服務(wù)器不能依賴于打開多個(gè)HTTP連接(例如,使用XMLHttpRequest或和長(zhǎng)輪詢))應(yīng)用程序提供一種通信機(jī)制。

圖片

websocket 是一個(gè)基于應(yīng)用層的網(wǎng)絡(luò)協(xié)議,建立在tcp 協(xié)議之上,和 http 協(xié)議可以說是兄弟的關(guān)系,但是這個(gè)兄弟有點(diǎn)依賴 http ,為什么這么說呢?我們都知道 HTTP 實(shí)現(xiàn)了三次握手來建立通信連接,實(shí)際上 websocket 的創(chuàng)始人很聰明,他不想重復(fù)的去造輪子,反正我兄弟已經(jīng)實(shí)現(xiàn)了握手了,我干嘛還要重寫一套呢?先讓它去沖鋒陷陣呢,我坐收漁翁之利不是更香 嗎,所以一般來說,我們會(huì)先用 HTTP 先進(jìn)行三次握手,再向服務(wù)器請(qǐng)求升級(jí)為websocket 協(xié)議,這就好比說,嘿兄弟你先去給我排個(gè)隊(duì)占個(gè)坑位建個(gè)小房子,到時(shí)候我在把這房子改造成摩天大樓。而且一般來說 80 和 443 端口一般 web 服務(wù)端都會(huì)外放出去,這樣可以有效的避免防火墻的限制。當(dāng)然,你創(chuàng)建的 websocket 服務(wù)端進(jìn)程的端口也需要外放出去。

很多人會(huì)想問,web開發(fā) 使用 HTTP 協(xié)議不是已經(jīng)差不多夠用了嗎?為什么還要我再多學(xué)一種呢?這不是搞事情嘛,仔細(xì)想想,一門新技術(shù)的產(chǎn)生必然有原因的,如果沒有需求,我們干嘛那么蛋疼去寫那么多東西,就是因?yàn)?HTTP 這個(gè)協(xié)議有些業(yè)務(wù)需求支持太過于雞肋了,從 HTTP 0.9 到現(xiàn)在的 HTTP3.0 ,HTTP協(xié)議可以說說是在普通的web開發(fā)領(lǐng)域已經(jīng)是十分完善且高效的了,說這個(gè)協(xié)議養(yǎng)活了全球半數(shù)的公司也不為過吧,像 2.0 服務(wù)器推送技術(shù),3.0 采用了 UDP 而放棄了原來的 TCP ,這些改動(dòng)都是為了進(jìn)一步提升協(xié)議的性能,然而大家現(xiàn)在還是基本使用的 HTTP 1.1 這個(gè)最為經(jīng)典的協(xié)議, 也是讓開發(fā)者挺尷尬的。

絕大多數(shù)的web開發(fā)都是應(yīng)用層開發(fā)者,大多數(shù)都是基于已有的應(yīng)用層去開發(fā)應(yīng)用,可以說我們最熟悉、日常打交道最多的就是應(yīng)用層協(xié)議了,底下 TCP/IP 協(xié)議我們基本很少會(huì)去處理,當(dāng)然大廠可能就不一樣了,自己弄一套協(xié)議也是正常的,這大概也是程序員和碼農(nóng)的區(qū)別吧,搬磚還是創(chuàng)新,差別還是很大的。網(wǎng)絡(luò)這種分層協(xié)議的好處我在之前的文章也說過了,這種隔離性很方便就可以讓我們基于原來的基礎(chǔ)去拓展,具有較好的兼容性。

總的來說,它就是一種依賴HTTP協(xié)議的,支持全雙工通信的一種應(yīng)用層網(wǎng)絡(luò)協(xié)議。

二、WebSocket產(chǎn)生背景

簡(jiǎn)單的說,WebSocket協(xié)議之前,雙工通信是通過多個(gè)http鏈接來實(shí)現(xiàn),這導(dǎo)致了效率低下。WebSocket解決了這個(gè)問題。下面是標(biāo)準(zhǔn)RFC6455中的產(chǎn)生背景概述。

長(zhǎng)久以來, 創(chuàng)建實(shí)現(xiàn)客戶端和用戶端之間雙工通訊的web app都會(huì)造成HTTP輪詢的濫用: 客戶端向主機(jī)不斷發(fā)送不同的HTTP呼叫來進(jìn)行詢問。

這會(huì)導(dǎo)致一系列的問題:

  • 1.服務(wù)器被迫為每個(gè)客戶端使用許多不同的底層TCP連接:一個(gè)用于向客戶端發(fā)送信息,其它用于接收每個(gè)傳入消息。
  • 2.有些協(xié)議有很高的開銷,每一個(gè)客戶端和服務(wù)器之間都有HTTP頭。
  • 3.客戶端腳本被迫維護(hù)從傳出連接到傳入連接的映射來追蹤回復(fù)。

一個(gè)更簡(jiǎn)單的解決方案是使用單個(gè)TCP連接雙向通信。 這就是WebSocket協(xié)議所提供的功能。 結(jié)合WebSocket API ,WebSocket協(xié)議提供了一個(gè)用來替代HTTP輪詢實(shí)現(xiàn)網(wǎng)頁(yè)到遠(yuǎn)程主機(jī)的雙向通信的方法。

WebSocket協(xié)議被設(shè)計(jì)來取代用HTTP作為傳輸層的雙向通訊技術(shù),這些技術(shù)只能犧牲效率和可依賴性其中一方來提高另一方,因?yàn)镠TTP最初的目的不是為了雙向通訊。

三、WebSocket實(shí)現(xiàn)原理

圖片

在實(shí)現(xiàn)websocket連線過程中,需要通過瀏覽器發(fā)出websocket連線請(qǐng)求,然后服務(wù)器發(fā)出回應(yīng),這個(gè)過程通常稱為“握手” 。**在 WebSocket API,瀏覽器和服務(wù)器只需要做一個(gè)握手的動(dòng)作,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道。兩者之間就直接可以數(shù)據(jù)互相傳送。**在此WebSocket 協(xié)議中,為我們實(shí)現(xiàn)即時(shí)服務(wù)帶來了兩大好處:

  1. Header:互相溝通的Header是很小的-大概只有 2 Bytes。
  2. Server Push:服務(wù)器的推送,服務(wù)器不再被動(dòng)的接收到瀏覽器的請(qǐng)求之后才返回?cái)?shù)據(jù),而是在有新數(shù)據(jù)時(shí)就主動(dòng)推送給瀏覽器。

四、WebSocket協(xié)議舉例

圖片

瀏覽器請(qǐng)求:

  • GET /webfin/websocket/ HTTP/1.1。
  • Host: localhost。
  • Upgrade: websocket。
  • Connection: Upgrade。
  • Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==。
  • Origin: http://服務(wù)器地址。
  • Sec-WebSocket-Version: 13。

服務(wù)器回應(yīng):

  • HTTP/1.1 101 Switching Protocols。
  • Upgrade: websocket。
  • Connection: Upgrade。
  • Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=。
  • WebSocket借用http請(qǐng)求進(jìn)行握手,相比正常的http請(qǐng)求,多了一些內(nèi)容。其中:
  • Upgrade: websocket。
  • Connection: Upgrade。
  • 表示希望將http協(xié)議升級(jí)到Websocket協(xié)議。Sec-WebSocket-Key是瀏覽器隨機(jī)生成的base64 encode的值,用來詢問服務(wù)器是否是支持WebSocket。

服務(wù)器返回:

  • Upgrade: websocket。
  • Connection: Upgrade。
  • 告訴瀏覽器即將升級(jí)的是Websocket協(xié)議

Sec-WebSocket-Accept是將請(qǐng)求包“Sec-WebSocket-Key”的值,與”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″這個(gè)字符串進(jìn)行拼接,然后對(duì)拼接后的字符串進(jìn)行sha-1運(yùn)算,再進(jìn)行base64編碼得到的。用來說明自己是WebSocket助理服務(wù)器。

Sec-WebSocket-Version是WebSocket協(xié)議版本號(hào)。RFC6455要求使用的版本是13,之前草案的版本均應(yīng)當(dāng)被棄用。

五、WebSocket使用

1.WebSocket 介紹

WebSocket 發(fā)起單個(gè)請(qǐng)求,服務(wù)端不需要等待客服端,客戶端在任何時(shí)候也能發(fā)消息到服務(wù)端,減少了輪詢時(shí)候的延遲.經(jīng)歷一次連接后,服務(wù)器能給客戶端發(fā)多次。下圖是輪詢與WebSocket的區(qū)別。

圖片

基于http的實(shí)時(shí)消息是相當(dāng)?shù)膹?fù)雜,在無狀態(tài)的請(qǐng)求中維持回話的狀態(tài)增加了復(fù)雜度,跨域也很麻煩,使用ajax處理請(qǐng)求有序請(qǐng)求需要考慮更多。通過ajax進(jìn)行交流也不簡(jiǎn)單。每一個(gè)延伸http功能的目的不是增加他的復(fù)雜度。websocket 可以大大簡(jiǎn)化實(shí)時(shí)通信應(yīng)用中的鏈接。

Websocket是一種底層網(wǎng)絡(luò)協(xié)議,可以讓你在這個(gè)基礎(chǔ)上建立別的標(biāo)準(zhǔn)協(xié)議。比如在WebSocket的客戶端的基礎(chǔ)上使用XMPP登錄不同的聊天服務(wù)器,因?yàn)樗械腦MPP服務(wù)理解相同的標(biāo)準(zhǔn)協(xié)議。WebSocket是web應(yīng)用的一種創(chuàng)新。

為了與其他平臺(tái)競(jìng)爭(zhēng),WebSocket是H5應(yīng)用提供的一部分先進(jìn)功能。每個(gè)操作系統(tǒng)都需要網(wǎng)絡(luò)功能,能夠讓應(yīng)用使用Sockets與別的主機(jī)進(jìn)行通信,是每個(gè)大平臺(tái)的核心功能。在很多方面,讓W(xué)eb應(yīng)用表現(xiàn)的像操作系統(tǒng)平臺(tái)是html5的趨勢(shì)。像socket這樣底層的網(wǎng)絡(luò)協(xié)議APIs不會(huì)符合原始的安全模型,也不會(huì)有web api那樣的設(shè)計(jì)風(fēng)格。WebSocket給H5應(yīng)用提供TCP的方式不會(huì)消弱網(wǎng)絡(luò)安全且有現(xiàn)代的Api。

WebSocket是Html5平臺(tái)的一個(gè)重要組件也是開發(fā)者強(qiáng)有力的工具。簡(jiǎn)單的說,你需要WebSocket創(chuàng)建世界級(jí)的web應(yīng)用。它彌補(bǔ)了http不適合實(shí)時(shí)通信的重大缺陷。異步、雙向通信模式,通過傳輸層協(xié)議使WebSocket具有普遍靈活性。想象一下你能用WebSocket創(chuàng)建正真實(shí)實(shí)時(shí)應(yīng)用的所有方式。比如聊天、協(xié)作文檔編輯、大規(guī)模多人在線游戲(MMO),股票交易應(yīng)用等等。

WebSocket是一個(gè)協(xié)議,但也有一個(gè)WebSocket API,這讓你的應(yīng)用去控制WebSocket的協(xié)議去響應(yīng)被服務(wù)端觸發(fā)的事件。API是W3C開發(fā),協(xié)議是IETE制定?,F(xiàn)代瀏覽器支持WebSocket API,這包括使用全雙工和雙向鏈接的方法和特性。讓你執(zhí)行像打開關(guān)閉鏈接、發(fā)送接收消息、監(jiān)聽服務(wù)端事件等必要操作。

2.WebSocket API

WebSocket API其實(shí)就是一個(gè)使用WebSocket協(xié)議的接口,通過它來建立全雙工通道來收發(fā)消息,簡(jiǎn)單易學(xué),要連接遠(yuǎn)程服務(wù)器,只需要?jiǎng)?chuàng)建一個(gè)WebSocket對(duì)象實(shí)體,并傳入一個(gè)服務(wù)端的URL。在客戶端和服務(wù)端一開始握手的期間,http協(xié)議升級(jí)到WebSocket協(xié)議就建立了連接,底層都是TCP協(xié)議。一旦建立連接,通過WebSocket接口可以反復(fù)的發(fā)送消息。在你的代碼里面,你可以使用異步事件監(jiān)聽連接生命周期的每個(gè)階段。

WebSocket API是純事件驅(qū)動(dòng),一旦建立全雙工連接,當(dāng)服務(wù)端給客戶端發(fā)送數(shù)據(jù)或者資源,它能自動(dòng)發(fā)送狀態(tài)改變的數(shù)據(jù)和通知。所以你不需要為了狀態(tài)的更新而去輪訓(xùn)Server,在客戶端監(jiān)聽即可。

首先,我們需要通過調(diào)用WebSocket構(gòu)造函數(shù)來創(chuàng)建一個(gè)WebSocket連接,構(gòu)造函數(shù)會(huì)返回一個(gè)WebSocket實(shí)例,可以用來監(jiān)聽事件。這些事件會(huì)告訴你什么時(shí)候連接建立,什么時(shí)候消息到達(dá),什么時(shí)候連接關(guān)閉了,以及什么時(shí)候發(fā)生了錯(cuò)誤。WebSocket協(xié)議定義了兩種URL方案,WS和WSS分別代表了客戶端和服務(wù)端之間未加密和加密的通信。WS(WebSocket)類似于Http URL,而WSS(WebSocket Security)URL 表示連接是基于安全傳輸層(TLS/SSL)和https的連接是同樣的安全機(jī)制。

WebSocket的構(gòu)造函數(shù)需要一個(gè)URL參數(shù)和一個(gè)可選的協(xié)議參數(shù)(一個(gè)或者多個(gè)協(xié)議的名字),協(xié)議的參數(shù)例如XMPP(Extensible Messaging and Presence Protocol)、SOAP(Simple Object Access Protocol)或者自定義協(xié)議。而URL參數(shù)需要以WS://或者WSS://開頭,例如:ws://www.websocket.org,如果URL有語法錯(cuò)誤,構(gòu)造函數(shù)會(huì)拋出異常。

// Create new WebSocket connection
var ws = new WebSocket("ws://www.websocket.org");
//測(cè)試了下鏈接不上。

圖片

第二個(gè)參數(shù)是協(xié)議名稱,是可選的,服務(wù)端和客服端使用的協(xié)議必須一致,這樣收發(fā)消息彼此才能理解,你可以定義一個(gè)或多個(gè)客戶端使用的協(xié)議,服務(wù)端會(huì)選擇一個(gè)來使用,一個(gè)客服端和一個(gè)服務(wù)端之間只能有一個(gè)協(xié)議。當(dāng)然都得基于WebSocket,WebSocket的重大好處之一就是基于WebSocket協(xié)議的廣泛使用,讓你的Web能夠擁有傳統(tǒng)桌面程序那樣的能力。

言歸正傳,我們回到構(gòu)造函數(shù),在第一次握手之后,和協(xié)議的名稱一起,客戶端會(huì)發(fā)送一個(gè)Sec-WebSocket-Protocol 頭,服務(wù)端會(huì)選擇0個(gè)或一個(gè)協(xié)議,響應(yīng)會(huì)帶上同樣的Sec-WebSocket-Protocol 頭,否則會(huì)關(guān)閉連接。通過協(xié)議協(xié)商(Protocol negotiation ),我們可以知道給定的WebSocket服務(wù)器所支持的協(xié)議和版本,然后應(yīng)用選擇協(xié)議使用。

// Connecting to the server with one protocol called myProtocol
var ws = new WebSocket("ws://echo.websocket.org", "myProtocol");
//myProtocol 是假設(shè)的一個(gè)定義好的且符合標(biāo)準(zhǔn)的協(xié)議。

你可以傳遞一個(gè)協(xié)議的數(shù)組。

var echoSocket = new WebSocket("ws://echo.websocket.org", ["com.kaazing.echo","example.imaginary.protocol"])
//服務(wù)端會(huì)選擇其中一個(gè)使用
echoSocket.onopen = function(e) {
// Check the protocol chosen by the server
console.log(echoSocket.protocol);
}

輸出:com.kaazing.ech

協(xié)議這個(gè)參數(shù)有三種。

1.注冊(cè)協(xié)議:根據(jù)RFC6455(WebSocket 協(xié)議)和IANA被官方注冊(cè)的標(biāo)準(zhǔn)協(xié)議。例如 微軟的SOAP。

圖片

看到兩個(gè)華為的:

2.開放協(xié)議:被廣泛使用的標(biāo)注協(xié)議,例如XMPP和STOMP。但沒有被正式注冊(cè)。

3.自定義協(xié)議:自己編寫和使用的WebSocket的協(xié)議。 協(xié)議會(huì)再后續(xù)章節(jié)給出詳細(xì)介紹,下面先看事件、對(duì)象和方法以及實(shí)例。

3.WebSocket事件

WebSocket API是純事件驅(qū)動(dòng),通過監(jiān)聽事件可以處理到來的數(shù)據(jù)和改變的鏈接狀態(tài)??蛻舳瞬恍枰獮榱烁聰?shù)據(jù)而輪訓(xùn)服務(wù)器。服務(wù)端發(fā)送數(shù)據(jù)后,消息和事件會(huì)異步到達(dá)。WebSocket編程遵循一個(gè)異步編程模型,只需要對(duì)WebSocket對(duì)象增加回調(diào)函數(shù)就可以監(jiān)聽事件。你也可以使用addEventListener()方法來監(jiān)聽。而一個(gè)WebSocket對(duì)象分四類不同事件。

1.open

一旦服務(wù)端響應(yīng)WebSocket連接請(qǐng)求,就會(huì)觸發(fā)open事件。響應(yīng)的回調(diào)函數(shù)稱為onopen。

// Event handler for the WebSocket connection opening
ws.onopen = function(e) {
console.log("Connection open...");
};

open事件觸發(fā)的時(shí)候,意味著協(xié)議握手結(jié)束,WebSocket已經(jīng)準(zhǔn)備好收發(fā)數(shù)據(jù)。如果你的應(yīng)用收到open事件,就可以確定服務(wù)端已經(jīng)處理了建立連接的請(qǐng)求,且同意和你的應(yīng)用通信。

2.Message

當(dāng)消息被接受會(huì)觸發(fā)消息事件,響應(yīng)的回調(diào)函數(shù)叫做onmessage。如下:

// 接受文本消息的事件處理實(shí)例:
ws.onmessage = function(e) {
if(typeof e.data === "string"){
console.log("String message received", e, e.data);
} else {
console.log("Other message received", e, e.data);
}
};

除了文本消息,WebSocket消息機(jī)制還能處理二進(jìn)制數(shù)據(jù),有Blob和ArrayBuffer兩種類型,在讀取到數(shù)據(jù)之前需要決定好數(shù)據(jù)的類型。

// 設(shè)置二進(jìn)制數(shù)據(jù)類型為blob(默認(rèn)類型)
ws.binaryType = "blob";
// Event handler for receiving Blob messages
ws.onmessage = function(e) {
if(e.data instanceof Blob){
console.log("Blob message received", e.data);
var blob = new Blob(e.data);
}
};
//ArrayBuffer
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
if(e.data instanceof ArrayBuffer){
console.log("ArrayBuffer Message Received", + e.data);
// e.data即ArrayBuffer類型
var a = new Uint8Array(e.data);
}
};

3.Error

如果發(fā)生意外的失敗會(huì)觸發(fā)error事件,相應(yīng)的函數(shù)稱為onerror,錯(cuò)誤會(huì)導(dǎo)致連接關(guān)閉。如果你收到一個(gè)錯(cuò)誤事件,那么你很快會(huì)收到一個(gè)關(guān)閉事件,在關(guān)閉事件中也許會(huì)告訴你錯(cuò)誤的原因。而對(duì)錯(cuò)誤事件的處理比較適合做重連的邏輯。

//異常處理
ws.onerror = function(e) {
console.log("WebSocket Error: " , e);
//Custom function for handling errors
handleErrors(e);
};

4.Close

不言而喻,當(dāng)連接關(guān)閉的時(shí)候回觸發(fā)這個(gè)事件,對(duì)應(yīng)onclose方法,連接關(guān)閉之后,服務(wù)端和客戶端就不能再收發(fā)消息。

WebSocket的規(guī)范其實(shí)還定義了ping和pong 架構(gòu)(frames),可以用來做keep-alive,心跳,網(wǎng)絡(luò)狀態(tài)查詢,latency instrumentation(延遲儀表?),但是目前 WebSocket API還沒有公布這些特性,盡管瀏覽器支持了ping,但不會(huì)觸發(fā)ping事件,相反,瀏覽器會(huì)自動(dòng)響應(yīng)pong,第八章會(huì)將更多關(guān)于ping和pong的細(xì)節(jié)。

當(dāng)然你可以調(diào)用close方法斷開與服務(wù)端的鏈接來觸發(fā)onclose事件:

ws.onclose = function(e) {
console.log("Connection closed", e);
};

連接失敗和成功的關(guān)閉握手都會(huì)觸發(fā)關(guān)閉事件,WebSocket的對(duì)象的readyState屬性就代表連接的狀態(tài)(2代表正在關(guān)閉,3代表已經(jīng)關(guān)閉)。關(guān)閉事件有三個(gè)屬性可以用來做異常處理和重獲: wasClean,code和reason。wasClean是一個(gè)bool值,代表連接是否干凈的關(guān)閉。 如果是響應(yīng)服務(wù)端的close事件,這個(gè)值為true,如果是別的原因,比如因?yàn)槭堑讓覶CP連接關(guān)閉,wasClean為false。code和reason代表關(guān)閉連接時(shí)服務(wù)端發(fā)送的狀態(tài),這兩個(gè)屬性和給入close方法的code和reason參數(shù)是對(duì)應(yīng)的,稍后會(huì)描述細(xì)節(jié)。

4.WebSocket 方法

WebSocket 對(duì)象有兩個(gè)方法:send()和close()。

1.send()

一旦在服務(wù)端和客戶端建立了全雙工的雙向連接,可以使用send方法去發(fā)送消息。

//發(fā)送一個(gè)文本消息
ws.send("Hello WebSocket!");

當(dāng)連接是open的時(shí)候send()方法傳送數(shù)據(jù),當(dāng)連接關(guān)閉或獲取不到的時(shí)候回拋出異常。一個(gè)通常的錯(cuò)誤是人們喜歡在連接open之前發(fā)送消息。如下所示:

// 這將不會(huì)工作
var ws = new WebSocket("ws://echo.websocket.org")
ws.send("Initial data");

正確的姿勢(shì)如下,應(yīng)該等待open事件觸發(fā)后再發(fā)送消息。

var ws = new WebSocket("ws://echo.websocket.org")
ws.onopen = function(e) {
ws.s

如果想通過響應(yīng)別的事件去發(fā)送消息,可以檢查readyState屬性的值為open的時(shí)候來實(shí)現(xiàn)。

function myEventHandler(data) {
if (ws.readyState === WebSocket.OPEN) {
//open的時(shí)候即可發(fā)送
ws.send(data);
} else {
// Do something else in this case.
//Possibly ignore the data or enqueue it.
}
}

發(fā)送二進(jìn)制數(shù)據(jù):

// Send a Blob
var blob = new Blob("blob contents");
ws.send(blob);
// Send an ArrayBuffer
var a = new Uint8Array([8,6,7,5,3,0,9]);
ws.send(a.buffer);

Blob對(duì)象和JavaScript File API一起使用的時(shí)候相當(dāng)有用,可以發(fā)送或接受文件,大部分的多媒體文件,圖像,視頻音頻文件。這一章末尾會(huì)結(jié)合File API提供讀取文件內(nèi)容來發(fā)送WebSocket消息的實(shí)例代碼。

2.close()

使用close方法來關(guān)閉連接,如果連接以及關(guān)閉,這方法將什么也不做。調(diào)用close方法只后,將不能發(fā)送數(shù)據(jù)。

ws.close();

close方法可以傳入兩個(gè)可選的參數(shù),code(numerical)和reason(string),以告訴服務(wù)端為什么終止連接。第三章講到關(guān)閉握手的時(shí)候再詳細(xì)討論這兩個(gè)參數(shù)。

// 成功結(jié)束會(huì)話
ws.close(1000, "Closing normally");
//1000是狀態(tài)碼,代表正常結(jié)束。

5.WebSocket 屬性

WebSocket對(duì)象有三個(gè)屬性,readyState,bufferedAmount和Protocol。

1.readyState

WebSocket對(duì)象通過只讀屬性readyState來傳達(dá)連接狀態(tài),它會(huì)更加連接狀態(tài)自動(dòng)改變。下表展示了readyState屬性的四個(gè)不同的值。

圖片

了解當(dāng)前連接的狀態(tài)有助于我們調(diào)試。

2.bufferedAmount

有時(shí)候需要檢查傳輸數(shù)據(jù)的大小,尤其是客戶端傳輸大量數(shù)據(jù)的時(shí)候。雖然send()方法會(huì)馬上執(zhí)行,但數(shù)據(jù)并不是馬上傳輸。瀏覽器會(huì)緩存應(yīng)用流出的數(shù)據(jù),你可以使用bufferedAmount屬性檢查已經(jīng)進(jìn)入隊(duì)列但還未被傳輸?shù)臄?shù)據(jù)大小。這個(gè)值不包含協(xié)議框架、操作系統(tǒng)緩存和網(wǎng)絡(luò)軟件的開銷。

下面這個(gè)例子展示了如何使用bufferedAmount屬性每秒更新發(fā)送。如果網(wǎng)絡(luò)不能處理這個(gè)頻率,它會(huì)自適應(yīng)。

// 10k
var THRESHOLD = 10240;
//建立連接
var ws = new WebSocket("ws://echo.websocket.org");
// Listen for the opening event
ws.onopen = function () {
setInterval( function() {
//緩存未滿的時(shí)候發(fā)送
if (ws.bufferedAmount < THRESHOLD) {
ws.send(getApplicationState());
}
}, 1000);
};
//使用bufferedAmount屬性發(fā)送數(shù)據(jù)可以避免網(wǎng)絡(luò)飽和。

3.protocol

在構(gòu)造函數(shù)中,protocol參數(shù)讓服務(wù)端知道客戶端使用的WebSocket協(xié)議。而WebSocket對(duì)象的這個(gè)屬性就是指的最終服務(wù)端確定下來的協(xié)議名稱,當(dāng)服務(wù)端沒有選擇客戶端提供的協(xié)議或者在連接握手結(jié)束之前,這個(gè)屬性都是空的。

完整實(shí)例:

現(xiàn)在我們已經(jīng)過了一遍WebSocket的構(gòu)造函數(shù)、事件、屬性和方法,接下來通過一個(gè)完整的實(shí)例來學(xué)習(xí)WebSocket API。實(shí)例使用“Echo”服務(wù)器:ws://echo.websocket.org,它能夠接受和返回發(fā)過去的數(shù)據(jù)。這樣有助于理解WebSocket API是如何和服務(wù)器交互的。

首先,我們先建立連接,讓頁(yè)面展示客戶端連接服務(wù)端的信息,然后發(fā)送、接受消息,最后關(guān)閉連接。

Websocket Echo Client


// 初始化連接和事件
function setup() {
output = document.getElementById("output");
ws = new WebSocket("ws://echo.websocket.org/echo");
// 監(jiān)聽open
ws.onopen = function (e) {
log("Connected");
sendMessage("Hello WebSocket!");
}
// 監(jiān)聽close
ws.onclose = function (e) {
log("Disconnected: " + e.reason);
}
//監(jiān)聽errors
ws.onerror = function (e) {
log("Error ");
}
// 監(jiān)聽 messages
ws.onmessage = function (e) {
log("Message received: " + e.data);
//收到消息后關(guān)閉
ws.close();
}
}
// 發(fā)送消息
function sendMessage(msg) {
ws.send(msg);
log("Message sent");
}
// logging
function log(s) {
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.textContent = s;
output.appendChild(p);
// Also log information on the javascript console
console.log(s);
}
// Start
setup();

圖片

判斷瀏覽器是否支持:

if (window.WebSocket){
console.log("This browser supports WebSocket!");
} else {
console.log("This browser does not support WebSocket.");
}

圖片

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    9160

    瀏覽量

    85428
  • 瀏覽器
    +關(guān)注

    關(guān)注

    1

    文章

    1025

    瀏覽量

    35360
  • WebSocket
    +關(guān)注

    關(guān)注

    0

    文章

    29

    瀏覽量

    3748
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    一文詳解WebSocket協(xié)議

    在效率和網(wǎng)絡(luò)帶寬利用率方面存在諸多問題。WebSocket協(xié)議應(yīng)運(yùn)而生,對(duì)外提供了簡(jiǎn)單的雙向數(shù)據(jù)傳輸能力。
    的頭像 發(fā)表于 01-07 11:26 ?7451次閱讀
    一文詳解<b class='flag-5'>WebSocket</b><b class='flag-5'>協(xié)議</b>

    如何移植libwebsockets

    libwebsockets是一個(gè)高性能的開源C語言庫(kù),專為實(shí)現(xiàn)WebSocket協(xié)議及相關(guān)的HTTP協(xié)議而設(shè)計(jì)。它不僅使開發(fā)者能夠在客戶端與服務(wù)器端輕松構(gòu)建WebSocket連接,還可
    的頭像 發(fā)表于 06-17 13:53 ?2262次閱讀
    如何移植libwebsockets

    labview TCP 發(fā)送問題

    我要利用labview TCP模塊,與用java搭建基于websocket協(xié)議 的服務(wù)器連接。發(fā)送程序按照例程寫的,可以建立連接,labview這邊顯示已發(fā)送,但是服務(wù)器那邊收不到數(shù)據(jù),感覺是卡在緩沖區(qū)里了。怎么辦?弄了好久,有次在字符串后加上回車符可以成功發(fā)送,現(xiàn)在又不
    發(fā)表于 11-27 23:59

    【迪文COF結(jié)構(gòu)智能屏試用體驗(yàn)】3. 結(jié)合esp32搭建聊天室

    wifi,編寫websocket協(xié)議包2. uart獲取迪文屏傳入的文本信息2.1 websocket編寫推薦夢(mèng)程大佬的demo,https://gitee.com/dreamcmi
    發(fā)表于 03-19 22:55

    iOS系統(tǒng)SRWebSocket的源碼解析上

    一。 前言: WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信可以通俗的解釋為服務(wù)器主動(dòng)發(fā)送信息給客戶端。 區(qū)別于MQTT、XMPP等
    發(fā)表于 09-25 15:51 ?1次下載

    根據(jù)WebSocket協(xié)議完全使用C++實(shí)現(xiàn)函數(shù)

    由于需要在項(xiàng)目中增加Websocket協(xié)議,與客戶端進(jìn)行通信,不想使用開源的庫(kù),比如WebSocketPP,就自己根據(jù)WebSocket協(xié)議實(shí)現(xiàn)一套函數(shù),完全使用C++實(shí)現(xiàn)。
    的頭像 發(fā)表于 11-28 14:29 ?4906次閱讀

    如何使用SpringBoot集成Netty開發(fā)一個(gè)基于WebSocket的聊天室說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是基于SpringBoot,借助Netty控制長(zhǎng)鏈接,使用WebSocket協(xié)議做一個(gè)實(shí)時(shí)的聊天室。
    發(fā)表于 05-29 17:56 ?1次下載
    如何使用SpringBoot集成Netty開發(fā)一個(gè)基于<b class='flag-5'>WebSocket</b>的聊天室說明

    WebSocket有什么優(yōu)點(diǎn)

    WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議WebSocket通信協(xié)議于2011年被IETF定為標(biāo)準(zhǔn)RFC 6455,并由RFC7936補(bǔ)充規(guī)范。
    的頭像 發(fā)表于 02-15 15:53 ?8308次閱讀
    <b class='flag-5'>WebSocket</b>有什么優(yōu)點(diǎn)

    WebSocket工作原理及使用方法

    它有很多名字; WebSocket,WebSocket協(xié)議WebSocket API。從首選的消息傳遞應(yīng)用程序到流行的在線多人游戲,WebSock
    的頭像 發(fā)表于 05-05 22:12 ?7880次閱讀
    <b class='flag-5'>WebSocket</b>工作原理及使用方法

    為什么客戶不直接控制智能家居網(wǎng)關(guān),而要使用云服務(wù)器做中轉(zhuǎn)?

    WebSocket和HTTP是獨(dú)立的協(xié)議,那么為什么WebSocket協(xié)議借用了HTTP的協(xié)議來建立連接而不是自建一套獨(dú)立的
    發(fā)表于 08-31 15:42 ?1824次閱讀
    為什么客戶不直接控制智能家居網(wǎng)關(guān),而要使用云服務(wù)器做中轉(zhuǎn)?

    有了HTTP為什么還要有websocket協(xié)議?

    如果我們的 HTTP 請(qǐng)求將超時(shí)設(shè)置的很大,比如 30 秒,在這 30 秒內(nèi)只要服務(wù)器收到了掃碼請(qǐng)求,就立馬返回給客戶端網(wǎng)頁(yè)。如果超時(shí),那就立馬發(fā)起下一次請(qǐng)求。
    的頭像 發(fā)表于 10-20 14:34 ?808次閱讀

    為什么有了HTTP,還需要WebSocket協(xié)議?

    HTTP是基于TCP協(xié)議的,同一時(shí)間里,客戶端和服務(wù)器只能有一方主動(dòng)發(fā)數(shù)據(jù),是半雙工通信。
    的頭像 發(fā)表于 01-07 10:29 ?682次閱讀

    鴻蒙上WebSocket的使用方法

    WebSocket 是一種網(wǎng)絡(luò)通訊協(xié)議,很多網(wǎng)絡(luò)開發(fā)工作者都需要它。本文介紹在 OpenHarmony 上 WebSocket 協(xié)議的使用方法。
    的頭像 發(fā)表于 03-08 14:17 ?1893次閱讀

    為什么有了HTTP,還需要WebSocket協(xié)議?

    HTTP是基于TCP協(xié)議的,同一時(shí)間里,客戶端和服務(wù)器只能有一方主動(dòng)發(fā)數(shù)據(jù),是半雙工通信。通常,打開某個(gè)網(wǎng)頁(yè),我們每點(diǎn)擊一次網(wǎng)頁(yè)上的某個(gè)選項(xiàng),前端就會(huì)發(fā)送一次HTTP請(qǐng)求,網(wǎng)站返回一次HTTP響應(yīng)
    的頭像 發(fā)表于 01-12 15:54 ?720次閱讀
    為什么有了HTTP,還需要<b class='flag-5'>WebSocket</b><b class='flag-5'>協(xié)議</b>?

    什么是WebSocket協(xié)議?

    主動(dòng)給客戶端發(fā)消息。而類似網(wǎng)頁(yè)游戲這樣的場(chǎng)景,是需要客戶端和服務(wù)器之間互相主動(dòng)發(fā)大量數(shù)據(jù)的。推薦閱讀:《TCP/IP協(xié)議不止是兩個(gè)協(xié)議!》《超干的干貨來了!一文了解
    的頭像 發(fā)表于 08-25 08:23 ?1232次閱讀
    什么是<b class='flag-5'>WebSocket</b><b class='flag-5'>協(xié)議</b>?