微信、QQ這類IM App怎麼做——談談Websocket
前言
關於我和WebSocket的緣:我從大二在計算機網路課上聽老師講過之後,第一次使用就到了畢業之後的第一份工作。直到最近換了工作,到了一家是含有IM社交聊天功能的app的時候,我覺得我現在可以談談我對WebSocket/Socket的一些看法了。要想做IM聊天app,就不得不理解WebSocket和Socket的原理了,聽我一一道來。
目錄
- 1.WebSocket使用場景
- 2.WebSocket誕生由來
- 3.談談WebSocket協議原理
- 4.WebSocket 和 Socket的區別與聯絡
- 5.iOS平臺有哪些WebSocket和Socket的開源框架
- 6.iOS平臺如何實現WebSocket協議
一.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。
11.總結
從上面我列舉的這些場景來看,一個共同點就是,高實時性!
二.WebSocket誕生由來
1.最開始的輪詢Polling階段
這種方式下,是不適合獲取實時資訊的,客戶端和伺服器之間會一直進行連線,每隔一段時間就詢問一次。客戶端會輪詢,有沒有新訊息。這種方式連線數會很多,一個接受,一個傳送。而且每次傳送請求都會有Http的Header,會很耗流量,也會消耗CPU的利用率。
2.改進版的長輪詢Long polling階段
長輪詢是對輪詢的改進版,客戶端傳送HTTP給伺服器之後,有沒有新訊息,如果沒有新訊息,就一直等待。當有新訊息的時候,才會返回給客戶端。在某種程度上減小了網路頻寬和CPU利用率等問題。但是這種方式還是有一種弊端:例如假設伺服器端的資料更新速度很快,伺服器在傳送一個資料包給客戶端後必須等待客戶端的下一個Get請求到來,才能傳遞第二個更新的資料包給客戶端,那麼這樣的話,客戶端顯示實時資料最快的時間為2×RTT(往返時間),而且如果在網路擁塞的情況下,這個時間使用者是不能接受的,比如在股市的的報價上。另外,由於http資料包的頭部資料量往往很大(通常有400多個位元組),但是真正被伺服器需要的資料卻很少(有時只有10個位元組左右),這樣的資料包在網路上週期性的傳輸,難免對網路頻寬是一種浪費。
3.WebSocket誕生
現在急需的需求是能支援客戶端和伺服器端的雙向通訊,而且協議的頭部又沒有HTTP的Header那麼大,於是,Websocket就誕生了!
上圖就是Websocket和Polling的區別,從圖中可以看到Polling裡面客戶端傳送了好多Request,而下圖,只有一個Upgrade,非常簡潔高效。至於消耗方面的比較就要看下圖了
上圖中,我們先看藍色的柱狀圖,是Polling輪詢消耗的流量,
Use case A: 1,000 clients polling every second: Network throughput is (871 x 1,000) = 871,000 bytes = 6,968,000 bits per second (6.6 Mbps)
Use case B: 10,000 clients polling every second: Network throughput is (871 x 10,000) = 8,710,000 bytes = 69,680,000 bits per second (66 Mbps)
Use case C: 100,000 clients polling every 1 second: Network throughput is (871 x 100,000) = 87,100,000 bytes = 696,800,000 bits per second (665 Mbps)
而Websocket的Frame是 just two bytes of overhead instead of 871,僅僅用2個位元組就代替了輪詢的871位元組!
Use case A: 1,000 clients receive 1 message per second: Network throughput is (2 x 1,000) = 2,000 bytes = 16,000 bits per second (0.015 Mbps)
Use case B: 10,000 clients receive 1 message per second: Network throughput is (2 x 10,000) = 20,000 bytes = 160,000 bits per second (0.153 Mbps)
Use case C: 100,000 clients receive 1 message per second: Network throughput is (2 x 100,000) = 200,000 bytes = 1,600,000 bits per second (1.526 Mbps)
相同的每秒客戶端輪詢的次數,當次數高達10W/s的高頻率次數的時候,Polling輪詢需要消耗665Mbps,而Websocket僅僅只花費了1.526Mbps,將近435倍!!
三.談談WebSocket協議原理
Websocket是應用層第七層上的一個應用層協議,它必須依賴 HTTP 協議進行一次握手 ,握手成功後,資料就直接從 TCP 通道傳輸,與 HTTP 無關了。
Websocket的資料傳輸是frame形式傳輸的,比如會將一條訊息分為幾個frame,按照先後順序傳輸出去。這樣做會有幾個好處:
1 大資料的傳輸可以分片傳輸,不用考慮到資料大小導致的長度標誌位不足夠的情況。
2 和http的chunk一樣,可以邊生成資料邊傳遞訊息,即提高傳輸效率。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
四.WebSocket 和 Socket的區別與聯絡
首先,Socket 其實並不是一個協議。它工作在 OSI 模型會話層(第5層),是為了方便大家直接使用更底層協議(一般是 TCP 或 UDP )而存在的一個抽象層。Socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個呼叫介面(API)。
Socket通常也稱作”套接字”,用於描述IP地址和埠,是一個通訊鏈的控制程式碼。網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個雙向鏈路的一端稱為一個Socket,一個Socket由一個IP地址和一個埠號唯一確定。應用程式通常通過”套接字”向網路發出請求或者應答網路請求。
Socket在通訊過程中,服務端監聽某個埠是否有連線請求,客戶端向服務端傳送連線請求,服務端收到連線請求向客戶端發出接收訊息,這樣一個連線就建立起來了。客戶端和服務端也都可以相互傳送訊息與對方進行通訊,直到雙方連線斷開。
所以基於WebSocket和基於Socket都可以開發出IM社交聊天類的app
五.iOS平臺有哪些WebSocket和Socket的開源框架
Socket開源框架有:CocoaAsyncSocket,socketio/socket.io-client-swift
WebSocket開源框架有:facebook/SocketRocket,tidwall/SwiftWebSocket
六.iOS平臺如何實現WebSocket協議
Talk is cheap。Show me the code ——Linus Torvalds
我們今天來看看facebook/SocketRocket的實現方法
首先這是SRWebSocket定義的一些成員變數
@property (nonatomic, weak) id <SRWebSocketDelegate> delegate; /** A dispatch queue for scheduling the delegate calls. The queue doesn't need be a serial queue. If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls. */ @property (nonatomic, strong) dispatch_queue_t delegateDispatchQueue; /** An operation queue for scheduling the delegate calls. If `nil` and `delegateOperationQueue` is `nil`, the socket uses main queue for performing all delegate method calls. */ @property (nonatomic, strong) NSOperationQueue *delegateOperationQueue; @property (nonatomic, readonly) SRReadyState readyState; @property (nonatomic, readonly, retain) NSURL *url; @property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders; // Optional array of cookies (NSHTTPCookie objects) to apply to the connections @property (nonatomic, copy) NSArray<NSHTTPCookie *> *requestCookies; // This returns the negotiated protocol. // It will be nil until after the handshake completes. @property (nonatomic, readonly, copy) NSString *protocol;
下面這些是SRWebSocket的一些方法
// Protocols should be an array of strings that turn into Sec-WebSocket-Protocol. - (instancetype)initWithURLRequest:(NSURLRequest *)request; - (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols; - (instancetype)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray<NSString *> *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; // Some helper constructors. - (instancetype)initWithURL:(NSURL *)url; - (instancetype)initWithURL:(NSURL *)url protocols:(NSArray<NSString *> *)protocols; - (instancetype)initWithURL:(NSURL *)url protocols:(NSArray<NSString *> *)protocols allowsUntrustedSSLCertificates:(BOOL)allowsUntrustedSSLCertificates; // By default, it will schedule itself on +[NSRunLoop SR_networkRunLoop] using defaultModes. - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode; // SRWebSockets are intended for one-time-use only. Open should be called once and only once. - (void)open; - (void)close; - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason; ///-------------------------------------- #pragma mark Send ///-------------------------------------- //下面是4個傳送的方法 /** Send a UTF-8 string or binary data to the server. @param message UTF-8 String or Data to send. @deprecated Please use `sendString:` or `sendData` instead. */ - (void)send:(id)message __attribute__((deprecated("Please use `sendString:` or `sendData` instead."))); - (void)sendString:(NSString *)string; - (void)sendData:(NSData *)data; - (void)sendPing:(NSData *)data; @end
對應5種狀態的代理方法
///-------------------------------------- #pragma mark - SRWebSocketDelegate ///-------------------------------------- @protocol SRWebSocketDelegate <NSObject> - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; @optional - (void)webSocketDidOpen:(SRWebSocket *)webSocket; - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; - (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload; // Return YES to convert messages sent as Text to an NSString. Return NO to skip NSData -> NSString conversion for Text messages. Defaults to YES. - (BOOL)webSocketShouldConvertTextFrameToString:(SRWebSocket *)webSocket; @end
didReceiveMessage方法是必須實現的,用來接收訊息的。
下面4個did方法分別對應著Open,Fail,Close,ReceivePong不同狀態的代理方法
方法就上面這些了,我們實際來看看程式碼怎麼寫
先是初始化Websocket連線,注意此處ws://或者wss://連線有且最多隻能有一個,這個是Websocket協議規定的
self.ws = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@:%zd/ws", serverProto, serverIP, serverPort]]]]; self.ws.delegate = delegate; [self.ws open];
傳送訊息
[self.ws send:message];
接收訊息以及其他3個代理方法
//這個就是接受訊息的代理方法了,這裡接受伺服器返回的資料,方法裡面就應該寫處理資料,儲存資料的方法了。 - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message { NSDictionary *data = [NetworkUtils decodeData:message]; if (!data) return; } //這裡是Websocket剛剛Open之後的代理方法。就想微信剛剛連線中,會顯示連線中,當連線上了,就不顯示連線中了,取消顯示連線的方法就應該寫在這裡面 - (void)webSocketDidOpen:(SRWebSocket *)webSocket { // Open = silent ping [self.ws receivedPing]; } //這是關閉Websocket的代理方法 - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean { [self failedConnection:NSLS(Disconnected)]; } //這裡是連線Websocket失敗的方法,這裡面一般都會寫重連的方法 - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { [self failedConnection:NSLS(Disconnected)]; }
最後
以上就是我想分享的一些關於Websocket的心得,文中如果有錯誤的地方,歡迎大家指點!一般沒有微信QQ那麼大使用者量的app,用Websocket應該都可以完成IM社交聊天的任務。當使用者達到億級別,應該還有很多需要優化,優化效能各種的吧。
最後,微信和QQ的實現方法也許並不是只用Websocket和Socket這麼簡單,也許是他們自己開發的一套能支援這麼大使用者,大資料的,各方面也都優化都最優的方法。如果有開發和微信和QQ的大神看到這篇文章,可以留言說說看你們用什麼方式實現的,也可以和我們一起分享,我們一起學習!我先謝謝大神們的指點了!
相關文章
- 淺談WebSocketWeb
- 創業者談:做微信還是做APP,兩種選擇的不同風險創業APP
- 談談微信小程式微信小程式
- [分享]離職面談怎麼談
- Nginx代理websocket為什麼要這樣做?NginxWeb
- 談談我是怎麼學習linux的Linux
- 【Java面試】請談談AQS是怎麼回事兒?Java面試AQS
- 想知道微信怎麼做指紋支付開發?看這裡!
- 談談java的類與物件Java物件
- 談談 Java 類載入機制Java
- 資料運營:活動社交類 App 談怎樣盤活使用者?APP
- 談一談越來越難做的前端前端
- 微信攔截及app類分享連結的解決辦法,細談微信域名防封技術原理APP
- 小馬識途營銷顧問談網站seo具體怎麼做?網站
- 遊戲運營雜談之怎麼做一份資料日報遊戲
- fir.im Weekly - 做一款 App 需要考慮什麼APP
- 談談畫素以及微信小程式的 rpx微信小程式
- 談談iOS接入微信支付遇到的小坑iOS
- 怎麼做微信收款碼代理商?
- 【搞定Jvm面試】 面試官:談談 JVM 類載入過程是怎樣的?JVM面試
- UIAppearance漫談UIAPP
- 淺談HTML5 WebSocket的機制HTMLWeb
- 都談一談你是怎麼在 Linux 桌面作業系統上做 UI 自動化測試的?Linux作業系統UI
- 生意難做,營收下滑!!不要只談增長,也要講講怎麼盈利!營收
- 網路安全老兵座談:雲安全審計(評估)應該怎麼做?
- [歪談]我們該怎麼學習?做一個學者還是習者?
- 談談最近做的一個自動化平臺
- 談談JavaScript中的call、apply和bindJavaScriptAPP
- 淺談 Trait 類AI
- 淺談Java抽象類Java抽象
- 談談怎樣清理Linux磁碟碎片Linux
- IM即時通訊專案講解(一) 實現類似qq微信表情皮膚無縫切換
- 網際網路產品運營入門談:怎麼做內容建設?
- 小白談一談:命令在redis中是怎麼被執行的呢?Redis
- 恆訊科技談談:冷門的澳大利亞伺服器怎麼樣?伺服器
- 談談Object類的終止器的實現!Object
- iOS APP安全雜談iOSAPP
- 雜談Apple SwiftAPPSwift