在看這篇之前,如果您還不了解直播原理,請查看這篇文章如何快速的開發(fā)一個完整的iOS直播app(原理篇)
在直播中,聊天和發(fā)礼物,需要用到及時通訊技術(shù),市面上的App大多數(shù)采用的都是第三方SDK,融云,環(huán)信等,但是本例子采用websocket搭建及時通訊服務(wù)器。
如果喜歡我的文章,可以關(guān)注我微博:袁崢Seemygo,也可以來小碼哥,了解下我們的iOS培訓(xùn)課程。后續(xù)還會更新更多內(nèi)容,有任何問題,歡迎簡書留言袁崢Seemygo。。。
即時通訊(Instant messaging,簡稱IM)是一個終端服務(wù),允許兩人或多人使用網(wǎng)路即時的傳遞文字訊息、檔案、語音與視頻交流
Websocket協(xié)議解析:
請求頭
GET ws://localhost:12345/websocket/test.html HTTP/1.1 Origin: http://localhost Connection: Upgrade Host: localhost:12345 Sec-WebSocket-Key: jspZdPxs9MrWCt3j6h7KdQ== //主要這個字段,這個叫“夢幻字符串”,這個也是個密鑰,只有有這個密鑰 服務(wù)器才能通過解碼 認(rèn)出來,哦~這是個WB的請求,我要建立TCP連接了!!!如果這個字符串沒有按照加密規(guī)則加密,那服務(wù)端就認(rèn)不出來,就會認(rèn)為這整個協(xié)議就是個HTTP請求。更不會開TCP。其他的字段都可以隨便設(shè)置,但是這個字段是最重要的字段,標(biāo)識WB協(xié)議的一個字段。 Upgrade: websocket Sec-WebSocket-Version: 13響應(yīng)頭
HTTP/1.1 101 Web Socket PRotocol Handshake WebSocket-Location: ws://localhost:12345/websocket/test.php Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: zUyzbJdkVJjhhu8KiAUCDmHtY/o= //這個字段,叫“夢幻字符串”,和上面那個夢幻字符串作用一樣。不同的是,這個字符串是要讓客戶端辨認(rèn)的,客戶端拿到后自動解碼。并且辨認(rèn)是不是一個WB請求。然后進(jìn)行相應(yīng)的操作。這個字段也是重中之重,不可隨便修改的。加密規(guī)則,依然是有規(guī)則的,可以去百度一下。 WebSocket-Origin: http://localhostSec-WebSocket-Key:其值采用base64編碼的隨機16字節(jié)長的字符序列Sec-WebSocket-Accept如何生成Socket.IO簡介
WebSocket的功能是很強大的,使用起來也靈活,可以適用于不同的場景。不過WebSocket技術(shù)也比較復(fù)雜,需要加密解密,包裝協(xié)議,自己實現(xiàn)3次握手,還需要對數(shù)據(jù)流進(jìn)行加密解密處理,服務(wù)器端和瀏覽器端的實現(xiàn)都不同于一般的Web應(yīng)用,因此自己實現(xiàn)很麻煩,可以使用Socket.IO框架。
Socket.IO:是一個完全由javaScript實現(xiàn)、基于Node.js、支持WebSocket的協(xié)議用于實時通信、跨平臺的開源框架。
Socket.IO:它包括了客戶端(iOS,Android)和服務(wù)器端(Node.js)的代碼,可以很好的實現(xiàn)iOS即使通訊技術(shù)。
Socket.IO框架地址
Socket.IO教程
Socket.IO建立連接 服務(wù)器代碼
1.如何導(dǎo)入Socket.IO?
和導(dǎo)入express框架一樣,使用package給package文件添加依賴"dependencies": { "express": "^4.14.0", "socket.io": "^1.4.8" }2.如何創(chuàng)建socket
socket本質(zhì)還是http協(xié)議,所以需要綁定http服務(wù)器,才能啟動socket服務(wù).
而且需要通過web服務(wù)器監(jiān)聽端口,socket不能監(jiān)聽端口,有人訪問端口才能建立連接,所以先創(chuàng)建web服務(wù)器
* 1.面向express框架開發(fā),加載express框架,方便處理get,post請求* 2.因為socket依賴http,創(chuàng)建http服務(wù)器,使用http模塊.* 3.可以通過express創(chuàng)建http服務(wù)器http.server(express)* 4.通過http服務(wù)器創(chuàng)建socket* 5.監(jiān)聽http服務(wù)器``` // 引入expressvar http = require('http');var express = require('express');// 創(chuàng)建web服務(wù)器var server = http.Server(express);// 引入sockervar socketIO = require('socket.io');// 需要傳入服務(wù)器,socket基于httpvar socket = socketIO(server);// 監(jiān)聽web服務(wù)器server.listen(8080);```3.如何建立socket連接(服務(wù)器不需要主動建立連接,建立連接是客戶端的事情,服務(wù)器只需要監(jiān)聽連接)
客戶端主動連接會發(fā)送connection事件,只需要監(jiān)聽connection事件有沒有發(fā)送,就知道客戶端有沒有主動連接服務(wù)器Socket.IO本質(zhì)是通過發(fā)送和接受事件觸發(fā)服務(wù)器和客戶端之間的通訊,任何能被編輯成JSON或二進(jìn)制的對象都可以傳遞。監(jiān)聽事件,用socket.on,這個方法會有兩個參數(shù),第一個參數(shù)是事件名稱,第二個參數(shù)是監(jiān)聽事件的回調(diào)函數(shù),監(jiān)聽到就會執(zhí)行這個回調(diào)函數(shù)監(jiān)聽connection,回調(diào)函數(shù)會傳入一個連接好的socket,這個socket就是客戶端的socketsocket連接原理,就是客戶端和服務(wù)端通過socket連接,服務(wù)器有socket,客戶端也有// 監(jiān)聽socket連接 // function參數(shù)必填socket socket.on('connection',function(clientSocket){ console.log('建立連接',clientSocket); });書寫客戶端代碼,驗證是否能建立連接Socket.IO建立連接 客戶端代碼
1.下載Socket.IO-Client-Swift
Socket.IO只有swift,如果需要用OC代碼,需要swift和OC混編還有如果代碼是OC,并且使用cocoapods,就不要使用cocoapods導(dǎo)入swift代碼,會有問題.2.下載完了,直接把Source文件夾拖入到自己工程中.
會報錯,說當(dāng)前swift版本過時,需要更新。點擊Xcode頂部Edit => Convert => TO Current Swift Syntas 就好了。3.OC和Swift混編,Swift代碼怎么在OC中使用,直接導(dǎo)入"工程文件名-Swift.h"就可以使用,這個文件Xcode會自動幫我們生成,無序手動自己生成.
#import "客戶端-Swift.h"4.注意工程文件名不能帶有-這個符號,而且有時候會延遲,并不是馬上導(dǎo)入"工程文件名-Swift.h"就好.
5.創(chuàng)建socket對象,然后連接用connect方法,socket對象需要強引用
注意協(xié)議:ws開頭創(chuàng)建socket對象,需要傳入字典,字典配置如下。
所有關(guān)于SocketIOClientOption的設(shè)置.如果是ObjC,轉(zhuǎn)換名字lowerCamelCase. case ConnectParams([String: AnyObject]) // 通過字典內(nèi)容連接 case Cookies([NSHTTPCookie]) // An array of NSHTTPCookies. Passed during the handshake. Default is nil. case DoubleEncodeUTF8(Bool) // Whether or not to double encode utf8. If using the node based server this should be true. Default is true. case ExtraHeaders([String: String]) // 添加自定義請求頭初始化來請求, 默認(rèn)為nil case ForcePolling(Bool) // 是否使用 xhr-polling. Default is `false` case ForceNew(Bool) // 將為每個連接創(chuàng)建一個新的connect, 如果你在重新連接時有bug時使用. case ForceWebsockets(Bool) // 是否使用 WebSockets. Default is `false` case HandleQueue(dispatch_queue_t) // 調(diào)度handle的運行隊列. Default is the main queue. case Log(Bool) // 是否打印調(diào)試信息. Default is false. case Logger(SocketLogger) // 可自定義SocketLogger調(diào)試日志.默認(rèn)是系統(tǒng)的. case Nsp(String) // 如果使用命名空間連接. Must begin with /. Default is `/` case Path(String) // 如果服務(wù)器使用一個自定義路徑. 例如: `"/swift/"`. Default is `""` case Reconnects(Bool) // 是否重新連接服務(wù)器失敗. Default is `true` case ReconnectAttempts(Int) // 重新連接多少次. Default is `-1` (無限次) case ReconnectWait(Int) // 等待重連時間. Default is `10` case sessionDelegate(NSURLSessionDelegate) // NSURLSessionDelegate 底層引擎設(shè)置. 如果你需要處理自簽名證書. Default is nil. case Secure(Bool) // 如果連接要使用TLS. Default is false. case SelfSigned(Bool) // WebSocket.selfSignedSSL設(shè)置 (Don't do this, iOS will yell at you) case VoipEnabled(Bool) // 如果你的客戶端使用VoIP服務(wù),只有用這個選項,Default is false6.因為需要進(jìn)行3次握手,不可能馬上建議連接,需要監(jiān)聽是否連接成功的回調(diào),使用on方法
7.ON方法兩個參數(shù)(第一個參數(shù),監(jiān)聽的事件名稱,第二個參數(shù):監(jiān)聽事件回調(diào)函數(shù),會自動調(diào)用)
回調(diào)函數(shù)也有兩個參數(shù)(第一個參數(shù):服務(wù)器傳遞的數(shù)據(jù) 第二個參數(shù):確認(rèn)請求數(shù)據(jù))在TCP/IP協(xié)議中,如果接收方成功的接收到數(shù)據(jù),那么會回復(fù)一個ACK數(shù)據(jù)。NSURL *url = [NSURL URLWithString:@"ws://192.168.0.100:8080"]; SocketIOClient *socket = [[SocketIOClient alloc] initWithSocketURL:url config:@{@"log": @YES, @"forcePolling": @YES}]; _socket = socket; [socket connect]; // 監(jiān)聽連接成功 [socket on:@"connect" callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) { NSLog(@"確定與服務(wù)器連接"); NSLog(@"%@ %@",data,ask); }];SocketIO發(fā)送事件,通過事件傳遞數(shù)據(jù)
SocketIO 客戶端發(fā)送事件代碼
注意:只有連接成功之后,才能發(fā)送事件向服務(wù)器發(fā)送事件(emit:第一參數(shù)事件的名稱,第二個參數(shù)傳輸?shù)臄?shù)據(jù),是一個數(shù)組)[socket emit:@"chat" with:@[@"你好"]];SocketIO 服務(wù)器監(jiān)聽事件代碼
監(jiān)聽客戶端事件,需要嵌套在連接好的connect回調(diào)函數(shù)中必須使用回調(diào)函數(shù)的socket參數(shù),如function(s)中的s,監(jiān)聽事件,因此這是客戶端的socket,肯定監(jiān)聽客戶端發(fā)來的事件服務(wù)器監(jiān)聽連接的回調(diào)函數(shù)的參數(shù)可以添加多個,具體看客戶端傳遞數(shù)據(jù)數(shù)組有幾個,每個參數(shù)都是與客戶段一一對應(yīng),第一個參數(shù)對應(yīng)客戶端數(shù)組第0個數(shù)據(jù)// 監(jiān)聽socket連接socket.on('connection',function(s){ console.log('監(jiān)聽到客戶端連接'); // data:客戶端數(shù)組第0個元素 // data1:客戶端數(shù)組第1個元素 s.on('chat',function(data,data1){ console.log('監(jiān)聽到chat事件'); console.log(data,data1); });});SocketIO 服務(wù)器發(fā)送事件代碼
這里的socket一定要用服務(wù)器端的socket給當(dāng)前客戶端發(fā)送數(shù)據(jù),其他客戶端收不到.socket.emit('chat','服務(wù)器'+data);發(fā)給所有客戶端,不包含當(dāng)前客戶端socket.emit.broadcast.emit('chat','發(fā)給所有客戶端,不包含當(dāng)前客戶端'+data);發(fā)給所有客戶端,包含當(dāng)前客戶端socket.emit.sockets.emit('chat','發(fā)給所有客戶端,包含當(dāng)前客戶端'+data);SocketIO 客戶端監(jiān)聽事件代碼
[socket on:@"chat" callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) { NSLog(@"%@",data[0]); }];SocketIO分組
開發(fā)中什么場景需要使用SocketIO分組?(T)一個客戶端和服務(wù)器只會保持一個socket連接,比如直播App中會開很多主播房間,每個房間都有自己的聊天室,那怎么把信息推送到對應(yīng)的房間,比如A用戶要給A主播間發(fā)送信息,怎么推送過去,通過服務(wù)器只能給當(dāng)前客戶端推送,那一推,當(dāng)前客戶端所有直播間都有A用戶的信息。怎么解決多個直播聊天室問題?給每個主播的房間都分組,服務(wù)器就可以給指定組推送數(shù)據(jù),就不會影響到其他直播間SocketIO如何分組?服務(wù)器代碼: socket.join(),()里面放分組名稱,與之對應(yīng)的 socket.leave()注意這里的socket是客戶端的socket,也就是連接成功,傳遞過來的socketsocket分組的原理,只要客戶端socket調(diào)用join,服務(wù)器就會把客戶端socket和分組的名稱綁定起來,到時候就可以根據(jù)分組的名稱找到對應(yīng)客戶端的socket,就能給指定的客戶端推送信息.注意:一個客戶端socket只能添加到一組,離開的時候,要記得移除.客戶端可以這樣測試,搞兩臺電腦/兩臺手機在同一個局域網(wǎng)內(nèi),運行就有兩個客戶端,分別加入不同組.服務(wù)器只給一個客戶端socket發(fā)送信息,另外一個客戶端收不到服務(wù)器代碼// 監(jiān)聽socket連接socket.on('connection',function(s){ console.log('監(jiān)聽到客戶端連接'); s.on('createRoom',function(roomName){ s.join(roomName); rooms.push(roomName); console.log('創(chuàng)建房間'+ roomName); }); s.on('chatRoom',function(data){ console.log(rooms[0] + '說話'); socket.to(rooms[0]).emit('chat','房間1的數(shù)據(jù)'); });});原文地址:http://www.jianshu.com/p/6e7fb61c25e1
| 
 
 | 
新聞熱點
疑難解答
圖片精選