微信、QQ這類IM App怎麼作——談談Websocket

要想作IM聊天app,就不得不理解WebSocket和Socket的原理了,聽我一一道來。web

 

 

1WebSocket的使用場景swift

1.社交聊天服務器

最著名的就是微信,QQ,這一類社交聊天的app。這一類聊天app的特色是低延遲,高即時。即時是這裏面要求最高的,若是有一個緊急的事情,經過IM軟件通知你,假設網絡環境良好的狀況下,這條message還沒法當即送達到你的客戶端上,緊急的事情都結束了,你才收到消息,那麼這個軟件確定是失敗的。微信

2.彈幕cookie

說到這裏,你們必定裏面想到了A站和B站了。確實,他們的彈幕一直是一種特點。並且彈幕對於一個視頻來講,極可能彈幕纔是精華。發彈幕須要實時顯示,也須要和聊天同樣,須要即時。網絡

3.多玩家遊戲架構

4.協同編輯app

如今不少開源項目都是分散在世界各地的開發者一塊兒協同開發,此時就會用到版本控制系統,好比Git,SVN去合併衝突。可是若是有一份文檔,支持多人實時在線協同編輯,那麼此時就會用到好比WebSocket了,它能夠保證各個編輯者都在編輯同一個文檔,此時不須要用到Git,SVN這些版本控制,由於在協同編輯界面就會實時看到對方編輯了什麼,誰在修改哪些段落和文字。框架

5.股票基金實時報價socket

金融界瞬息萬變——幾乎是每毫秒都在變化。若是採用的網絡架構沒法知足實時性,那麼就會給客戶帶來巨大的損失。幾毫秒錢股票開始大跌,幾秒之後才刷新數據,一秒鐘的時間內,極可能用戶就已經損失巨大財產了。

6.體育實況更新

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

7.視頻會議/聊天

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

8.基於位置的應用

愈來愈多的開發者借用移動設備的GPS功能來實現他們基於位置的網絡應用。若是你一直記錄用戶的位置(好比運行應用來記錄運動軌跡),你能夠收集到更加細緻化的數據。

9.在線教育

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

10.智能家居

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

11.總結

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

 

 

2WebSocket誕生由來

 

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倍!!

 

3談談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 ...                |
 +---------------------------------------------------------------+

 

4Websocket和Socket的區別與聯繫

首先,Socket 其實並非一個協議。它工做在 OSI 模型會話層(第5層),是爲了方便你們直接使用更底層協議(通常是 TCP 或 UDP )而存在的一個抽象層。Socket是對TCP/IP協議的封裝,Socket自己並非協議,而是一個調用接口(API)。

 

Socket一般也稱做」套接字」,用於描述IP地址和端口,是一個通訊鏈的句柄。網絡上的兩個程序經過一個雙向的通信鏈接實現數據的交換,這個雙向鏈路的一端稱爲一個Socket,一個Socket由一個IP地址和一個端口號惟一肯定。應用程序一般經過」套接字」向網絡發出請求或者應答網絡請求。

Socket在通信過程當中,服務端監聽某個端口是否有鏈接請求,客戶端向服務端發送鏈接請求,服務端收到鏈接請求向客戶端發出接收消息,這樣一個鏈接就創建起來了。客戶端和服務端也均可以相互發送消息與對方進行通信,直到雙方鏈接斷開。

因此基於WebSocket和基於Socket均可以開發出IM社交聊天類的app

 

 

5iOS有哪些WebSocket和Socket的開源框架

Socket開源框架有:CocoaAsyncSocketsocketio/socket.io-client-swift

WebSocket開源框架有:facebook/SocketRockettidwall/SwiftWebSocket

 

6iOS平臺如何實現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)];

}
相關文章
相關標籤/搜索