本片我們說下WebSocket聊训,之前項(xiàng)目中有幾個(gè)輪詢的情況抱究,使用基于http協(xié)議的接口,每隔幾秒調(diào)用一下带斑,感覺有點(diǎn)浪費(fèi)資源鼓寺。Http1.0默認(rèn)是短連接勋拟,客戶端和服務(wù)器每進(jìn)行一次HTTP操作,就建立一次連接妈候,任務(wù)結(jié)束就中斷連接客戶端主動(dòng)請(qǐng)求敢靡,請(qǐng)求過后關(guān)閉,這樣每隔幾秒鐘都要去進(jìn)行三次握手州丹,請(qǐng)求打開-關(guān)閉操作很浪費(fèi)資源醋安。Http1.1之后,默認(rèn)使用長(zhǎng)連接墓毒,用以保持連接特性吓揪,雖然可以使用Http的長(zhǎng)連接來實(shí)現(xiàn)但是也有弊端總會(huì)造成雙方資源的浪費(fèi),還有也牽扯H5瀏覽器所计,所以經(jīng)過調(diào)研柠辞,覺得使用WebSocket來處理輪詢的接口。
首先大家要知道什么是協(xié)議呢主胧?我們只有了解了網(wǎng)絡(luò)協(xié)議才能對(duì)我們接下來的內(nèi)容理解有幫助叭首,才不會(huì)迷茫,那么我們通過一張圖來說明下
這就是兩個(gè)人不在同一頻道焙格,各自說各自的,也不懂的對(duì)方的夷都,協(xié)議可以說是規(guī)則眷唉。
網(wǎng)絡(luò)協(xié)議:計(jì)算機(jī)雙方必須共同遵從的一組約定。如怎么樣建立連接囤官、怎么樣互相識(shí)別等冬阳。只有遵守這個(gè)約定,計(jì)算機(jī)之間才能相互通信交流党饮。它的三要素是:語(yǔ)法肝陪、語(yǔ)義、時(shí)序刑顺。
首先我們先來了解幾個(gè)概念:
Http協(xié)議:HTTP屬于應(yīng)用層協(xié)議氯窍,HTTP的長(zhǎng)連接和短連接本質(zhì)上是TCP長(zhǎng)連接和短連接。
TCP協(xié)議:TCP協(xié)議屬于傳輸層蹲堂,TCP協(xié)議是可靠的荞驴、面向連接的。主要解決如何在IP層之上可靠地傳遞數(shù)據(jù)包贯城,使得網(wǎng)絡(luò)上接收端收到發(fā)送端所發(fā)出的所有包,并且順序與發(fā)送順序一致霹娄。TCP連接的建立依靠“三次握手”能犯,而釋放則需要“四次握手”鲫骗。
IP協(xié)議:IP協(xié)議屬于網(wǎng)絡(luò)層,主要解決網(wǎng)絡(luò)路由和尋址問題踩晶。
WebSocket協(xié)議:也是基于TCP的長(zhǎng)連接执泰,目的就是解決網(wǎng)絡(luò)傳輸中的雙向通信的問題,就是取代Http在雙向通信場(chǎng)景下的使用,而且它的實(shí)現(xiàn)方式有些也是基于HTTP的(WS的默認(rèn)端口是80和443),WebSocket協(xié)議有兩部分組成:握手和數(shù)據(jù)傳輸渡蜻。Websocket是一個(gè)持久化的協(xié)議术吝,相對(duì)于HTTP這種是非持久的協(xié)議。Http協(xié)議是被動(dòng)性的茸苇,也就是只能客戶端發(fā)起排苍。
Http協(xié)議雙向通訊解決方案:
1.輪詢(polling),輪詢就會(huì)造成對(duì)網(wǎng)絡(luò)和通信雙方的資源的浪費(fèi)学密,且非實(shí)時(shí)淘衙。
2.長(zhǎng)輪詢,客戶端發(fā)送一個(gè)超時(shí)時(shí)間很長(zhǎng)的Request腻暮,服務(wù)器hold住這個(gè)連接彤守,在有新數(shù)據(jù)到達(dá)時(shí)返回Response,相比#1哭靖,占用的網(wǎng)絡(luò)帶寬少了具垫,其他類似。
3.長(zhǎng)連接试幽,這里講的其實(shí)是HTTP的長(zhǎng)連接筝蚕,本質(zhì)上還是Request/Response消息對(duì),仍然會(huì)造成資源的浪費(fèi)抡草、實(shí)時(shí)性不強(qiáng)等問題饰及。
*長(zhǎng)連接短連接操作過程
短連接的操作步驟是:
建立連接——數(shù)據(jù)傳輸——關(guān)閉連接...建立連接——數(shù)據(jù)傳輸——關(guān)閉連接
長(zhǎng)連接的操作步驟是:
建立連接——數(shù)據(jù)傳輸...(保持連接)...數(shù)據(jù)傳輸——關(guān)閉連接
上面我們對(duì)一些協(xié)議做了了解。
以上的使用Http方案會(huì)有弊端康震,這里我們使用WebSocket協(xié)議來替代輪詢的
*還有一點(diǎn)需要說明的地方就是WebSocket與Socket是沒什么關(guān)系的,WebSocket協(xié)議和Http協(xié)議是在應(yīng)用層屏箍,二Socket是對(duì)TCP/IP 協(xié)議的封裝赴魁,也就是只是接口(類似于得底層的封裝),讓你在使用的時(shí)候更方便操作疹鳄。
到這里我們對(duì)網(wǎng)絡(luò)七層概念瘪弓、及各個(gè)協(xié)議在哪個(gè)層和他們之間的協(xié)同工作原理有了了解。這樣才會(huì)讓我們?nèi)ナ褂盟鼈兊臅r(shí)候會(huì)更高效呛占。
*我們借用知乎上大佬的例子來理解下WebSocket執(zhí)行工作原理:
----首先,被動(dòng)性,當(dāng)服務(wù)器完成協(xié)議升級(jí)后(HTTP->Websocket)坠狡,服務(wù)端就可以主動(dòng)推送信息給客戶端啦逃沿。所以上面的情景可以做如下修改。
客戶端:啦啦啦,我要建立Websocket協(xié)議,需要的服務(wù):chat,Websocket協(xié)議版本:17(HTTP Request)
服務(wù)端:ok创千,確認(rèn),已升級(jí)為Websocket協(xié)議(HTTP Protocols Switched)
客戶端:麻煩你有信息的時(shí)候推送給我噢体捏。河泳。
服務(wù)端:ok某抓,有的時(shí)候會(huì)告訴你的汉矿。
服務(wù)端:balabalabalabala
服務(wù)端:balabalabalabala
服務(wù)端:哈哈哈哈哈啊哈哈哈哈
服務(wù)端:笑死我了哈哈哈哈哈哈哈
就變成了這樣只需要經(jīng)過一次HTTP請(qǐng)求曲尸,就可以做到源源不斷的信息傳送了纽乱。(在程序設(shè)計(jì)中为严,這種設(shè)計(jì)叫做回調(diào)第股,即:你有信息了再來通知我诲锹,而不是我傻乎乎的每次跑來問你)這樣的協(xié)議解決了同步有延遲庸诱,而且還非常消耗資源的這種情況。
WebSocket協(xié)議有很多種
這邊我們使用的是STOMP協(xié)議,STOMP定義了客戶端和服務(wù)器之間以Frame進(jìn)行同行缀去,F(xiàn)rame的格式為:
COMMAND
header1:value1
header2:value2
Body^@
COMMAND分為CONNECT朵耕、SEND阎曹、SUBSCRIBE、UNSUBSCRIBE注暗、BEGIN捆昏、COMMIT赚楚、ABORT、ACK骗卜、NACK宠页、DISCONNECT這幾種左胞。COMMAND之后下一行緊跟著的是頭部的鍵值對(duì),之后加入一條空行举户,空行之后為body,即傳遞的消息實(shí)體烤宙。
本文的核心內(nèi)容:通俗點(diǎn)stomp協(xié)議就是(本人親自Debug獲取,絕對(duì)通俗易懂):
\x0A 16進(jìn)制是\n
\x00 16進(jìn)制是0
COMMAND + \x0A + 循環(huán)添加傳遞的headerdic參數(shù)(key+ : + value + \x0A )循環(huán)完后+\x0A + 若果有body需要加body + \x00
之后再utf-8編碼
COMMAND可以為:CONNECT俭嘁、SEND躺枕、SUBSCRIBE、UNSUBSCRIBE兄淫、BEGIN屯远、COMMIT、ABORT捕虽、ACK、NACK坡脐、DISCONNECT泄私、CONNECTED、ERROR备闲、MESSAGE晌端、RECEIPT
其實(shí)我們也可以自己定義規(guī)則類似于STOMP的協(xié)議,只要前后端等定義相同的規(guī)則恬砂,按照所定義的規(guī)則實(shí)現(xiàn)咧纠,統(tǒng)一解析一樣就可以。
iOS客戶端處理WebSocket可以使用第三方庫(kù)jetfire泻骤、SocketRocket漆羔,但是要想處理STOMP協(xié)議或者其他協(xié)議的就要自己寫個(gè)實(shí)現(xiàn)類,來處理拼接狱掂、解析邏輯演痒。這里我們先來看下基于SocketRocket的實(shí)現(xiàn)STOMP協(xié)議WebSocket的流程:
1.打開請(qǐng)求URL
2.得到回調(diào)webSocketDidOpen
3.連接Connect,發(fā)送連接消息
核心發(fā)送消息代碼趋惨,不管事要連接還是要處理發(fā)送各種Command都需要使用:
private func sendFrame(command: String?, header: [String: String]?, body: AnyObject?) {
if socket?.readyState == .OPEN {
var frameString = ""
if command != nil {
frameString = command! + "\n"
}
if let header = header {
for (key, value) in header {
frameString += key
frameString += ":"
frameString += value
frameString += "\n"
}
}
if let body = body as? String {
frameString += "\n"
frameString += body
} else if let _ = body as? NSData {
}
if body == nil {
frameString += "\n"
}
frameString += StompCommands.controlChar
if socket?.readyState == .OPEN {
socket?.send(frameString)
} else {
print("no socket connection")
if let delegate = delegate {
DispatchQueue.main.async(execute: {
delegate.stompClientDidDisconnect(client: self)
})
}
}
}
}
仔細(xì)看下這段代碼你會(huì)覺得跟我之前說的通俗規(guī)則是不是一樣呢鸟顺?可以對(duì)比下。
4.發(fā)送后就是等著服務(wù)端回調(diào)給我們消息器虾,不過也可能是失敗的消息.
5.連接完成后基本操作實(shí)現(xiàn)就是send和subscribe
6.如果不用的時(shí)候可以取消連接disConnect
之前網(wǎng)上找了好多第三方庫(kù)都是說可以實(shí)現(xiàn)Stomp協(xié)議讯嫂,但是我使用后都不可以,最后親身測(cè)試一下兩個(gè)是完全可以的兆沙。通過驗(yàn)證也得出了上邊的協(xié)議規(guī)則欧芽。
下邊是github上的兩個(gè)實(shí)現(xiàn)庫(kù)
WebsocketStompKit、StompClientLib
也可以使用pod ZMBase(Swift)或者OTBase(OC),里邊抽出了實(shí)現(xiàn)的類
pod 'ZMBase', '~> 0.1.1'
pod 'OTBase', '~> 0.1.1'
這里整理了一個(gè)包含iOS(OC挤悉、Swift)渐裸、Android的實(shí)現(xiàn)Demo巫湘,如果需要請(qǐng)點(diǎn)擊。
感謝大家閱讀昏鹃!喜歡的點(diǎn)個(gè)贊尚氛、關(guān)注一波!