1. 程式人生 > >轉載一篇通俗易懂的講解入門即時通訊(IM)的文章

轉載一篇通俗易懂的講解入門即時通訊(IM)的文章

寫在前面:本文轉載自簡書網,底部已給出原文連結,相信會對初次接觸即時通訊開發的同學很多幫助。

初涉IM,首先我有這麼幾個問題需要弄明白:

Socket 和 WebSocket 有哪些區別和聯絡?
WebSocket 和 HTTP 有什麼關係?
WebSocket 和 HTML5 是什麼關係?
什麼是 長連線/短連線、長輪詢/短輪詢?
WebSocket在哪些場景下使用?
如果想做IOS的即時通訊,是使用Socket還是WebSocket?
通過google查找了大量資料,覺得有必要把相關內容做一個整理,上面幾個問題也會在敘述中逐漸清晰。

1、Socket 和 WebSocket 有哪些區別和聯絡?

就像JavaJavaScript北大北大青鳥雷鋒雷峰塔一樣,沒有什麼關係,除了在名字上沾親帶故的以外,就是兩個完全不同的東西。

WebSocket

WebSocket一種在單個TCP連線上進行全雙工通訊的協議。WebSocket通訊協議於2011年被IETF定為標準RFC 6455,並被RFC7936所補充規範。WebSocket API也被W3C定為標準。——維基百科

背景
現在,很多網站為了實現推送技術,所用的技術都是輪詢。輪詢是在特定的時間間隔(如每1秒)由客戶端對伺服器發出HTTP請求,然後由伺服器返回最新的資料給客戶端。這種傳統的模式帶來很明顯的缺點,即客戶端需要不斷的向伺服器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的資料可能只是很小的一部分,顯然這樣會浪費很多的頻寬等資源。

而比較新的技術去做輪詢的效果是Comet。這種技術雖然可以雙向通訊,但依然需要反覆發出請求。而且在Comet中,普遍採用的長連結,也會消耗伺服器資源。
在這種情況下,HTML5定義了WebSocket協議,能更好的節省伺服器資源和頻寬,並且能夠更實時地進行通訊。
Websocket使用ws或wss的統一資源標誌符,類似於HTTPS,其中wss表示在TLS之上的Websocket。如:

ws://example.com/wsapi
wss://secure.example.com/

Websocket使用和 HTTP 相同的 TCP 埠,可以繞過大多數防火牆的限制。預設情況下,Websocket協議使用80埠;執行在TLS之上時,預設使用443埠。

WebSocket出現的目的:即時通訊,替代HTTP輪詢

WebSocket 使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。在 WebSocket API 中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸。

(1)最初的輪詢(polling)階段

在這裡插入圖片描述
上圖表明,客戶端傳送一個request,伺服器不管有沒有新訊息,都會立即返回一個response,然後關閉連線,這次HTTP請求結束。請記住 Request = Response , 在HTTP1.0和HTTP1.1中都是這樣,也就是說一個request只能有一個response對應。客戶端需要不斷執行這個請求過程(也就是輪詢),查詢服務端有沒有新的訊息(資料)。

輪詢場景:

Client:親親,有沒有新訊息(Request)
Server:木有(Response)
Client:親親,有沒有新訊息(Request)
Server:木有。。(Response)
Client:親親,有沒有新訊息(Request)
Server:好煩,沒有。。(Response)
Client:那個。。有沒有新訊息(Request)
Server: 有啦,給你(Response)
Client:親親,有沒有新訊息(Request)
Server:。。沒。。。沒有(Response)
...
...

從上面可以看出來,客戶端不斷的建立HTTP連線,然後等待服務端處理,可以提現HTTP協議的另一個缺陷:被動性,也就是服務端不能主動聯絡客戶端,只能有客戶端發起。而且,HTTP request的Header是很長的,為了傳輸一個很小的資料卻佔用了很多的頻寬流量去傳輸Header。可見,輪詢需要伺服器有很快的處理速度,且非常消耗資源,也不具備即時性。

(2)長輪詢 (Long polling) 階段

在這裡插入圖片描述
Long Polling 是對 Polling 的改進,原理跟 Polling 相似,都是採用輪詢的方式,不過Long Polling 採取的是阻塞模型:客戶端發起連線後,如果服務端沒有新訊息,就一直不返回Response給客戶端。直到有新訊息或者超時才返回給客戶端,返回之後這次請求結束。客戶端再次建立連線,重複這個過程。。。。

情景:

Client:親親,有沒有新訊息? 沒有的話,等有了再給我吧 (Request)
Server:額。。。~~~ (1小時後) ~~~ 有新訊息了,給你 (Response)
...
...

相比於 Polling ,Long Polling 在某種程度上減小了對網路寬頻的消耗等問題,但缺陷也很明顯:假設伺服器端的資料更新速度很快,伺服器在傳送一個數據包給客戶端後必須等待客戶端的下一個Get請求到來,才能傳遞第二個更新的資料包給客戶端,那麼這樣的話,客戶端顯示實時資料最快的時間為2×RTT(往返時間),而且如果在網路擁塞的情況下,這個時間使用者是不能接受的,比如在股市的的報價上;另外,由於http資料包的頭部資料量往往很大(通常有400多個位元組),但是真正被伺服器需要的資料卻很少(有時只有10個位元組左右),這樣的資料包在網路上週期性的傳輸,難免對網路頻寬是一種浪費;而且 Long Polling 要求伺服器具有高併發性,也就是同時接待大量客戶端的能力。

(3) WebSocket
從上面分析可以看出,Polling ,Long Polling 不是最好的方式,Polling 需要服務端更快的處理速度,Long Polling 需要高併發性。在這種情況下,WebSocket出現,解決了上面提到的 HTTP 協議存在的幾個缺陷。

下面圖,是WebSocket建立連線、傳輸資料以及關閉連線的模型
在這裡插入圖片描述
情景:

Client:親親,我要建立WebSocket協議,需要的服務:chat,WebSocket協議版本:17 (HTTP Request)
Server:ok,確認,已升級為WebSocket協議 (HTTP Protocols Switched)
Client:麻煩你有新訊息的時候推送給我哦。。
Server:ok,有的時候會告訴你的。
Server:balabalabalabala
Server:balabalabalabala
Server:我看到一個笑話,哈哈哈
Server:哈哈哈哈哈哈哈。。。。。

Websocket是應用層第七層上的一個應用層協議,它必須依賴 HTTP 協議進行一次握手 ,握手成功後,資料就直接從 TCP 通道傳輸,與 HTTP 無關了。
Websocket的資料傳輸是以 frame (幀) 形式傳輸的,比如會將一條訊息分為幾個 frame,按照先後順序傳輸出去。這樣做會有幾個好處:

大資料的傳輸可以分片傳輸,不用考慮到資料大小導致的長度標誌位不足夠的情況。
和 HTTP 的chunk一樣,可以邊生成資料邊傳遞訊息,即提高傳輸效率。
一個典型的Websocket握手請求如下:

客戶端請求

GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

伺服器迴應

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/

欄位說明:
1、Connection必須設定Upgrade,表示客戶端希望連線升級。
2、Upgrade欄位必須設定Websocket,表示希望升級到Websocket協議。
3、Sec-WebSocket-Key是隨機的字串,伺服器端會用這些資料來構造出一個SHA-1的資訊摘要。把“Sec-WebSocket-Key”加上一個特殊字串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然後計算SHA-1摘要,之後進行BASE-64編碼,將結果做為“Sec-WebSocket-Accept”頭的值,返回給客戶端。如此操作,可以儘量避免普通HTTP請求被誤認為Websocket協議。
4、Sec-WebSocket-Version 表示支援的Websocket版本。RFC6455要求使用的版本是13,之前草案的版本均應當被棄用。
5、Origin欄位是可選的,通常用來表示在瀏覽器中發起此Websocket連線所在的頁面,類似於Referer。但是,於Referer不同的是,Origin只包含了協議和主機名稱。
6、其他一些定義在HTTP協議中的欄位,如Cookie等,也可以在Websocket中使用。

Socket

Socket並不是一個協議,而是為了方便使用TCP或UDP而抽象出來的一層,是位於應用層和傳輸控制層之間的一組介面。如果你要使用HTTP來構建服務,那麼就不需要關心Socket,如果你想基於TCP/IP來構建服務,那麼Socket可能就是你會接觸到的API。

網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket。
建立網路通訊連線至少要一對埠號(socket)。socket本質是程式設計介面(API),對TCP/IP的封裝,
TCP/IP也要提供可供程式設計師做網路開發所用的介面,這就是Socket程式設計介面;
HTTP是轎車,提供了封裝或者顯示資料的具體形式;Socket是發動機,提供了網路通訊的能力。—— 百度百科

Socket是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。
在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,
對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。 —— XX百科

在這裡插入圖片描述
當兩臺主機通訊時,必須通過Socket連線,Socket則利用TCP/IP協議建立TCP連線。
TCP連線則更依靠於底層的IP協議,IP協議的連線則依賴於鏈路層等更低層次。
WebSocket則是一個典型的應用層協議。

2、 WebSocket 和 HTTP 有什麼關係?

先看一下這兩個概念在維基百科中的解釋:

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. The WebSocket protocol was standardized by the IETF as RFC 6455 in 2011, and the WebSocket API in Web IDL is being standardized by the W3C.
WebSocket is designed to be implemented in web browsers and web servers, but it can be used by any client or server application. The WebSocket Protocol is an independent TCP-based protocol. Its only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request.[1]
The WebSocket protocol enables interaction between a browser and a web server with lower overheads, facilitating real-time data transfer from and to the server. This is made possible by providing a standardized way for the server to send content to the browser without being solicited by the client, and allowing for messages to be passed back and forth while keeping the connection open. In this way, a two-way (bi-directional) ongoing conversation can take place between a browser and the server. The communications are done over TCP port number 80 (or 443 in the case of TLS-encrypted connections), which is of benefit for those environments which block non-web Internet connections using a firewall.

—— From Wikipedia, the free encyclopedia

The Hypertext Transfer Protocol (HTTP) is an application protocol for distributed, collaborative, and hypermedia information systems.
HTTP is the foundation of data communication for the World Wide Web.
Hypertext is structured text that uses logical links (hyperlinks) between nodes containing text. HTTP is the protocol to exchange or transfer hypertext.
Development of HTTP was initiated by Tim Berners-Lee at CERN in 1989. Standards development of HTTP was coordinated by the Internet Engineering Task Force (IETF) and the World Wide Web Consortium (W3C), culminating in the publication of a series of Requests for Comments (RFCs). The first definition of HTTP/1.1, the version of HTTP in common use, occurred in RFC 2068 in 1997, although this was obsoleted by RFC 2616 in 1999 and then again by RFC 7230 and family in 2014.
A later version, the successor HTTP/2, was standardized in 2015, and is now supported by major web servers.

—— From Wikipedia, the free encyclopedia

相同點:
WebSocket 和 HTTP 都是基於TCP的應用層協議,都是可靠性傳輸協議。

不同點:

本來就是兩種完全不同的協議。。。
WebSocket 是全雙工協議,可以雙向傳送或接受資訊,連線建立之後,通訊雙方都可以在任何時刻向另一方傳送資料。HTTP請求需要等待客戶端發起請求服務端才能響應。
與 HTTP 不同的是,Websocket 需要先建立連線,這就使得其成為一種有狀態的協議,之後通訊時可以省略部分狀態資訊。而HTTP請求可能需要在每個請求都攜帶狀態資訊(如身份認證等)
Websocket定義了二進位制幀,相對HTTP,可以更輕鬆地處理二進位制內容。
HTTP 協議要不斷的建立,關閉Request,由於HTTP是無狀態協議,每一次傳送都是一次新的開始,所以每次都要重新傳輸identity info (鑑別資訊),來告訴服務端你是誰。Websocket 只需要一次HTTP握手,所以說整個通訊過程是建立在一次連線狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的資訊,直到你關閉請求,這樣就可以避免反覆解析HTTP協議,還要檢視identity info(鑑別資訊)。
聯絡:
Websocket 通過 HTTP/1.1 協議的101狀態碼進行握手。
為了建立Websocket連線,需要通過客戶端發出請求,之後伺服器進行迴應,這個過程通常稱為“握手”(handshaking)。

雖然WebSocket在握手的時候是通過 HTTP 進行的(為了相容性考慮,也許以後WebSocket會有自己的握手方式),但也僅僅是這樣而已,它們就是兩種不同的應用層協議。

3、 WebSocket 和 HTML5 是什麼關係?

HTML5是指的一系列新的API,或者說新規範,新技術。而WebSocket就是HTML5中出的的一種協議。

4、什麼是 長連線/短連線、長輪詢/短輪詢?

查了許多資料,於是總結一下自己的理解:

長連線/短連線,長輪詢/短輪詢 本身就是不通層次的概念。

長/短連線 是針對TCP傳輸層的概念,也就是,TCP連線才有長/短連線之說。

短連線是指通訊雙方有資料互動時,就建立一個TCP連線,資料傳送完成後,則斷開此連線,即每次連線只完成一項業務的傳送。

長連線指在一個TCP連線上可以連續傳送多個數據包,在連線保持期間,如果沒有資料包傳送,需要TCP keep alive。TCP keep alive 的兩種方式:

應用層面的心跳機制
自定義心跳訊息頭 : 一般客戶端主動傳送, 伺服器接收後進行迴應(也可以不迴應).
TCP協議自帶的 keep alive:開啟keep-alive功能即可. 具體屬性也可以通過API設定.
TCP KeepAlive 是用於檢測TCP連線的狀態,而心跳機制有兩個作用:一是檢測TCP的狀態,二是檢測通訊雙方的狀體。

考慮一種情況,某臺伺服器因為某些原因導致負載超高,CPU 100%,無法響應任何業務請求,但是使用 TCP 探針則仍舊能夠確定連線狀態,這就是典型的連線活著但業務提供方已死的狀態,對客戶端而言,這時的最好選擇就是斷線後重新連線其他伺服器,而不是一直認為當前伺服器是可用狀態,一直向當前伺服器傳送些必然會失敗的請求。

從上面我們可以知道,KeepAlive 並不適用於檢測雙方存活的場景,這種場景還得依賴於應用層的心跳。應用層心跳有著更大的靈活性,可以控制檢測時機,間隔和處理流程,甚至可以在心跳包上附帶額外資訊。從這個角度而言,應用層的心跳的確是最佳實踐。

寫到這順便說一下 HTTP Keep Alive 和 TCP keep alive 這兩個看上去肯定有點兒啥關係的概念。事實上,HTTP keep-alive與TCP keep-alive 是兩個完全沒有關係的東西。

HTTP keep-alive是HTTP協議的一個特性,目的是為了讓TCP連線保持的更久一點,以便客戶端/服務端在同一個TCP連線上可以傳送多個HTTP request/response。

TCP keep-alive是一種檢測連線狀況的保活機制。It keeps TCP connection opened by sending small packets. 當網路兩端建立了TCP連線之後,閒置idle(雙方沒有任何資料流傳送往來)了tcp_keepalive_time後,伺服器核心就會嘗試向客戶端傳送偵測包,來判斷TCP連線狀況(有可能客戶端崩潰、強制關閉了應用、主機不可達等等)。如果沒有收到對方的回答(ack包),則會在 tcp_keepalive_intvl後再次嘗試傳送偵測包,直到收到對對方的ack,如果一直沒有收到對方的ack,一共會嘗試 tcp_keepalive_probes次,每次的間隔時間在這裡分別是15s, 30s, 45s, 60s, 75s。如果嘗試tcp_keepalive_probes,依然沒有收到對方的ack包,則會丟棄該TCP連線。TCP連線預設閒置時間是2小時,一般設定為30分鐘足夠了。

既然長/短連線是針對TCP的概念,但是我們卻經常看到HTTP 長連線(HTTP persistent connection)這樣的說法,到底什麼鬼?憋急,先看一段解釋:

HTTP persistent connection, also called HTTP keep-alive, or HTTP connection reuse, is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses, as opposed to opening a new connection for every single request/response pair. The newer HTTP/2 protocol uses the same idea and takes it further to allow multiple concurrent requests/responses to be multiplexed over a single connection.

—— From Wikipedia, the free encyclopedia

可以發現,HTTP persistent connection就是上面我們解釋的HTTP Keep Alive。其實很簡單,TCP長連線是針對TCP層的概念,就是讓一個TCP連線長時間保持不要斷開。HTTP的長連線(keep alive)是HTTP協議的一個特性,也是希望保持住長時間的TCP連線,才能在這個TCP通道上傳送多個HTTP請求,而不必每次請求都開啟一個新的TCP連線。

長輪詢/短輪詢 在上文已經介紹了,它是伺服器的實現方式!由服務端編寫的程式碼決定。

5、WebSocket在哪些場景下使用?

1.社交聊天

最著名的就是微信,QQ,這一類社交聊天的app。這一類聊天app的特點是低延遲,高即時。即時是這裡面要求最高的,如果有一個緊急的事情,通過IM軟體通知你,假設網路環境良好的情況下,這條message還無法立即送達到你的客戶端上,緊急的事情都結束了,你才收到訊息,那麼這個軟體肯定是失敗的。

2.彈幕

說到這裡,大家一定裡面想到了A站和B站了。確實,他們的彈幕一直是一種特色。而且彈幕對於一個視訊來說,很可能彈幕才是精華。發彈幕需要實時顯示,也需要和聊天一樣,需要即時。

3.多玩家遊戲

4.協同編輯

現在很多開源專案都是分散在世界各地的開發者一起協同開發,此時就會用到版本控制系統,比如Git,SVN去合併衝突。但是如果有一份文件,支援多人實時線上協同編輯,那麼此時就會用到比如WebSocket了,它可以保證各個編輯者都在編輯同一個文件,此時不需要用到Git,SVN這些版本控制,因為在協同編輯介面就會實時看到對方編輯了什麼,誰在修改哪些段落和文字。

5.股票基金實時報價

金融界瞬息萬變——幾乎是每毫秒都在變化。如果採用的網路架構無法滿足實時性,那麼就會給客戶帶來巨大的損失。幾毫秒錢股票開始大跌,幾秒以後才重新整理資料,一秒鐘的時間內,很可能使用者就已經損失巨大財產了。

6.體育實況更新

全世界的球迷,體育愛好者特別多,當然大家在關心自己喜歡的體育活動的時候,比賽實時的賽況是他們最最關心的事情。這類新聞中最好的體驗就是利用Websocket達到實時的更新!

7.視訊會議/聊天

視訊會議並不能代替和真人相見,但是他能讓分佈在全球天涯海角的人聚在電腦前一起開會。既能節省大家聚在一起路上花費的時間,討論聚會地點的糾結,還能隨時隨地,只要有網路就可以開會。

8.基於位置的應用

越來越多的開發者借用移動裝置的GPS功能來實現他們基於位置的網路應用。如果你一直記錄使用者的位置(比如執行應用來記錄運動軌跡),你可以收集到更加細緻化的資料。

9.線上教育

線上教育近幾年也發展迅速。優點很多,免去了場地的限制,能讓名師的資源合理的分配給全國各地想要學習知識的同學手上,Websocket是個不錯的選擇,可以視訊聊天、即時聊天以及其與別人合作一起在網上討論問題…

10.智慧家居

這也是我一畢業加入的一個偉大的物聯網智慧家居的公司。考慮到家裡的智慧裝置的狀態必須需要實時的展現在手機app客戶端上,毫無疑問選擇了Websocket。

從上面我列舉的這些場景來看,一個共同點就是,高實時性!

6、如果想做IOS的即時通訊,是使用Socket還是WebSocket?

使用 Socket 和 WebSocket 都可以做即時通訊。具體怎麼做呢?公司專案IOS端使用了第三方庫 CocoaAsynSocket ,它是對socket的OC封裝,支援TCP、UDP連線。CocoaAsynSocket 的使用非常簡單,也有官方和其他資料可以查詢,這裡就不囉嗦了。這篇文章只是對即時通訊涉及的一些基本概念,根據我自己的理解,做一個總結,如果有不對的地方還請不吝指正,感激不盡!

筆者也是剛剛接觸即時通訊開發這一塊,對於怎麼上手比較迷茫,感謝原作者提供的這篇文章,令我茅塞頓開。同時要感慨一下原文的排版確實十分簡潔舒適。