




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
【移動應用開發(fā)技術】怎么在iOS中實現(xiàn)WebSocket長鏈接
今天就跟大家聊聊有關怎么在iOS中實現(xiàn)WebSocket長鏈接,可能很多人都不太了解,為了讓大家更加了解,在下給大家總結了以下內容,希望大家根據(jù)這篇文章可以有所收獲。WebSocketWebSocket是HTML5一種新的協(xié)議。它實現(xiàn)了瀏覽器與服務器全雙工通信,能更好的節(jié)省服務器資源和帶寬并達到實時通訊,它建立在TCP之上,同HTTP一樣通過TCP來傳輸數(shù)據(jù),但是它和HTTP最大不同是:WebSocket是一種雙向通信協(xié)議.由于項目需要創(chuàng)建一個聊天室,需要通過長鏈接,和后臺保持通訊,進行聊天,并且實時進行熱點消息的推送.目前Facebook的SocketRocket應該是目前最好的關于SocketRocket使用的框架了.而且簡單易用.使用一般一個項目在啟動后的某個時機會啟動創(chuàng)建一個長鏈接,如果需要多個就多次創(chuàng)建.如果只要一個就可以封裝為一個單例,全局使用.可以使用podpod管理庫,在podfile中加入pod'SocketRocket'在使用命令行工具cd到當前工程安裝podinstall導入頭文件后即可使用.為求穩(wěn)定,我的做法是copy的FaceBook里SocketRocket庫到項目里.-->SocketRocket地址1.首先創(chuàng)建一個名為WebSocketManager的單例類,+(instancetype)shared;2.創(chuàng)建一個枚舉,分別表示WebSocket的鏈接狀態(tài)typedef
NS_ENUM(NSUInteger,WebSocketConnectType){
WebSocketDefault
=
0,
//初始狀態(tài),未連接,不需要重新連接
WebSocketConnect,
//已連接
WebSocketDisconnect
//連接后斷開,需要重新連接
};3.創(chuàng)建連接//建立長連接
-
(void)connectServer;4.處理連接成功的結果;-(void)webSocketDidOpen:(RMWebSocket
*)webSocket;
//連接成功回調5.處理連接失敗的結果-
(void)webSocket:(SRWebSocket
*)webSocket
didFailWithError:(NSError
*)error;//連接失敗回調6.接收消息///接收消息回調,需要提前和后臺約定好消息格式.
-
(void)webSocket:(SRWebSocket
*)webSocket
didReceiveMessageWithString:(nonnull
NSString
*)string7.關閉連接-
(void)webSocket:(SRWebSocket
*)webSocket
didCloseWithCode:(NSInteger)code
reason:(NSString
*)reason
wasClean:(BOOL)wasClean;///關閉連接回調的代理8.為保持長鏈接的連接狀態(tài),需要定時向后臺發(fā)送消息,就是俗稱的:心跳包.需要創(chuàng)建一個定時器,固定時間發(fā)送消息.9.鏈接斷開情況處理:首先判斷是否是主動斷開,如果是主動斷開就不作處理.如果不是主動斷開鏈接,需要做重新連接的邏輯.具體代碼如下:WebSocketManager.h中的代碼#import
<foundation
foundation=""
h="">
#import
"RMWebSocket.h"
typedef
NS_ENUM(NSUInteger,WebSocketConnectType){
WebSocketDefault
=
0,
//初始狀態(tài),未連接
WebSocketConnect,
//已連接
WebSocketDisconnect
//連接后斷開
};
@class
WebSocketManager;
@protocol
WebSocketManagerDelegate
<nsobject>
-
(void)webSocketManagerDidReceiveMessageWithString:(NSString
*)string;
@end
NS_ASSUME_NONNULL_BEGIN
@interface
WebSocketManager
:
NSObject
@property
(nonatomic,
strong)
RMWebSocket
*webSocket;
@property(nonatomic,weak)
id
<websocketmanagerdelegate
nbsp="">
delegate;
@property
(nonatomic,
assign)
BOOL
isConnect;
//是否連接
@property
(nonatomic,
assign)
WebSocketConnectType
connectType;
+(instancetype)shared;
-
(void)connectServer;//建立長連接
-
(void)reConnectServer;//重新連接
-
(void)RMWebSocketClose;//關閉長連接
-
(void)sendDataToServer:(NSString
*)data;//發(fā)送數(shù)據(jù)給服務器
@end
NS_ASSUME_NONNULL_END
</websocketmanagerdelegate>
</nsobject>
</foundation>WebSocketManager.m中的代碼#import
"WebSocketManager.h"
@interface
WebSocketManager
()
<rmwebsocketdelegate>
@property
(nonatomic,
strong)
NSTimer
*heartBeatTimer;
//心跳定時器
@property
(nonatomic,
strong)
NSTimer
*netWorkTestingTimer;
//沒有網絡的時候檢測網絡定時器
@property
(nonatomic,
assign)
NSTimeInterval
reConnectTime;
//重連時間
@property
(nonatomic,
strong)
NSMutableArray
*sendDataArray;
//存儲要發(fā)送給服務端的數(shù)據(jù)
@property
(nonatomic,
assign)
BOOL
isActivelyClose;
//用于判斷是否主動關閉長連接,如果是主動斷開連接,連接失敗的代理中,就不用執(zhí)行
重新連接方法
@end
@implementation
WebSocketManager
+(instancetype)shared{
static
WebSocketManager
*_instance
=
nil;
static
dispatch_once_t
onceToken;
dispatch_once(&onceToken,
^{
_instance
=
[[self
alloc]init];
});
return
_instance;
}
-
(instancetype)init
{
self
=
[super
init];
if(self){
self.reConnectTime
=
0;
self.isActivelyClose
=
NO;
self.sendDataArray
=
[[NSMutableArray
alloc]
init];
}
return
self;
}
//建立長連接
-
(void)connectServer{
self.isActivelyClose
=
NO;
self.webSocket.delegate
=
nil;
[self.webSocket
close];
_webSocket
=
nil;
//
self.webSocket
=
[[RMWebSocket
alloc]
initWithURL:[NSURL
URLWithString:@"/ws/token=88888888"]];
self.webSocket
=
[[RMWebSocket
alloc]
initWithURL:[NSURL
URLWithString:@"ws://:7272"]];
self.webSocket.delegate
=
self;
[self.webSocket
open];
}
-
(void)sendPing:(id)sender{
[self.webSocket
sendPing:nil
error:NULL];
}
#pragma
mark
#pragma
mark
-
socket
delegate
///開始連接
-(void)webSocketDidOpen:(RMWebSocket
*)webSocket{
NSLog(@"socket
開始連接");
self.isConnect
=
YES;
self.connectType
=
WebSocketConnect;
[self
initHeartBeat];///開始心跳
}
///連接失敗
-(void)webSocket:(RMWebSocket
*)webSocket
didFailWithError:(NSError
*)error{
NSLog(@"連接失敗");
self.isConnect
=
NO;
self.connectType
=
WebSocketDisconnect;
DLog(@"連接失敗,這里可以實現(xiàn)掉線自動重連,要注意以下幾點");
DLog(@"1.判斷當前網絡環(huán)境,如果斷網了就不要連了,等待網絡到來,在發(fā)起重連");
DLog(@"3.連接次數(shù)限制,如果連接失敗了,重試10次左右就可以了");
//判斷網絡環(huán)境
if
(AFNetworkReachabilityManager.sharedMworkReachabilityStatus
==
AFNetworkReachabilityStatusNotReachable){
//沒有網絡
[self
noNetWorkStartTestingTimer];//開啟網絡檢測定時器
}else{
//有網絡
[self
reConnectServer];//連接失敗就重連
}
}
///接收消息
-(void)webSocket:(RMWebSocket
*)webSocket
didReceiveMessageWithString:(NSString
*)string{
NSLog(@"接收消息
%@",string);
if
([self.delegate
respondsToSelector:@selector(webSocketManagerDidReceiveMessageWithString:)])
{
[self.delegate
webSocketManagerDidReceiveMessageWithString:string];
}
}
///關閉連接
-(void)webSocket:(RMWebSocket
*)webSocket
didCloseWithCode:(NSInteger)code
reason:(NSString
*)reason
wasClean:(BOOL)wasClean{
self.isConnect
=
NO;
if(self.isActivelyClose){
self.connectType
=
WebSocketDefault;
return;
}else{
self.connectType
=
WebSocketDisconnect;
}
DLog(@"被關閉連接,code:%ld,reason:%@,wasClean:%d",code,reason,wasClean);
[self
destoryHeartBeat];
//斷開連接時銷毀心跳
//判斷網絡環(huán)境
if
(AFNetworkReachabilityManager.sharedMworkReachabilityStatus
==
AFNetworkReachabilityStatusNotReachable){
//沒有網絡
[self
noNetWorkStartTestingTimer];//開啟網絡檢測
}else{
//有網絡
NSLog(@"關閉連接");
_webSocket
=
nil;
[self
reConnectServer];//連接失敗就重連
}
}
///ping
-(void)webSocket:(RMWebSocket
*)webSocket
didReceivePong:(NSData
*)pongData{
NSLog(@"接受pong數(shù)據(jù)-->
%@",pongData);
}
#pragma
mark
-
NSTimer
//初始化心跳
-
(void)initHeartBeat{
//心跳沒有被關閉
if(self.heartBeatTimer)
{
return;
}
[self
destoryHeartBeat];
dispatch_main_async_safe(^{
self.heartBeatTimer
=
[NSTimer
timerWithTimeInterval:10
target:self
selector:@selector(senderheartBeat)
userInfo:nil
repeats:true];
[[NSRunLoop
currentRunLoop]addTimer:self.heartBeatTimer
forMode:NSRunLoopCommonModes];
})
}
//重新連接
-
(void)reConnectServer{
if(self.webSocket.readyState
==
RM_OPEN){
return;
}
if(self.reConnectTime
>
1024){
//重連10次
2^10
=
1024
self.reConnectTime
=
0;
return;
}
WS(weakSelf);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(self.reConnectTime
*NSEC_PER_SEC)),
dispatch_get_main_queue(),
^{
if(weakSelf.webSocket.readyState
==
RM_OPEN
&&
weakSelf.webSocket.readyState
==
RM_CONNECTING)
{
return;
}
[weakSelf
connectServer];
//
CTHLog(@"正在重連");
if(weakSelf.reConnectTime
==
0){
//重連時間2的指數(shù)級增長
weakSelf.reConnectTime
=
2;
}else{
weakSelf.reConnectTime
*=
2;
}
});
}
//發(fā)送心跳
-
(void)senderheartBeat{
//和服務端約定好發(fā)送什么作為心跳標識,盡可能的減小心跳包大小
WS(weakSelf);
dispatch_main_async_safe(^{
if(weakSelf.webSocket.readyState
==
RM_OPEN){
[weakSelf
sendPing:nil];
}
});
}
//沒有網絡的時候開始定時
--
用于網絡檢測
-
(void)noNetWorkStartTestingTimer{
WS(weakSelf);
dispatch_main_async_safe(^{
weakSWorkTestingTimer
=
[NSTimer
scheduledTimerWithTimeInterval:1.0
target:weakSelf
selector:@selector(noNetWorkStartTesting)
userInfo:nil
repeats:YES];
[[NSRunLoop
currentRunLoop]
addTimer:weakSWorkTestingTimer
forMode:NSDefaultRunLoopMode];
});
}
//定時檢測網絡
-
(void)noNetWorkStartTesting{
//有網絡
if(AFNetworkReachabilityManager.sharedMworkReachabilityStatus
!=
AFNetworkReachabilityStatusNotReachable)
{
//關閉網絡檢測定時器
[self
destoryNetWorkStartTesting];
//開始重連
[self
reConnectServer];
}
}
//取消網絡檢測
-
(void)destoryNetWorkStartTesting{
WS(weakSelf);
dispatch_main_async_safe(^{
if(weakSWorkTestingTimer)
{
[weakSWorkTestingTimer
invalidate];
weakSWorkTestingTimer
=
nil;
}
});
}
//取消心跳
-
(void)destoryHeartBeat{
WS(weakSelf);
dispatch_main_async_safe(^{
if(weakSelf.heartBeatTimer)
{
[weakSelf.heartBeatTimer
invalidate];
weakSelf.heartBeatTimer
=
nil;
}
});
}
//關閉長連接
-
(void)RMWebSocketClose{
self.isActivelyClose
=
YES;
self.isConnect
=
NO;
self.connectType
=
WebSocketDefault;
if(self.webSocket)
{
[self.webSocket
close];
_webSocket
=
nil;
}
//關閉心跳定時器
[self
destoryHeartBeat];
//關閉網絡檢測定時器
[self
destoryNetWorkStartTesting];
}
//發(fā)送數(shù)據(jù)給服務器
-
(void)sendDataToServer:(NSString
*)data{
[self.sendDataArray
addObject:data];
//[_webSocket
sendString:data
error:NULL];
//沒有網絡
if
(AFNetworkReachabilityManager.sharedMworkReachabilityStatus
==
AFNetworkReachabilityStatusNotReachable)
{
//開啟網絡檢測定時器
[self
noNetWorkStartTestingTimer];
}
else
//有網絡
{
if(self.webSocket
!=
nil)
{
//
只有長連接OPEN開啟狀態(tài)才能調
send
方法,不然會Crash
if(self.webSocket.readyState
==
RM_OPEN)
{
//
if
(self.sendDataArray.count
>
0)
//
{
//
NSString
*data
=
self.sendDataArray[0];
[_webSocket
sendString:data
error:NULL];
//發(fā)送數(shù)據(jù)
//
[self.sendDataArray
removeObjectAtInd
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 河南省洛陽市伊川縣2024-2025學年六年級下學期期末數(shù)學試卷(含詳解)
- 華章教育初三
- 2025年中國天然氣加氣站設備市場前景預測及未來發(fā)展趨勢報告
- 2021-2026年中國汽車雷達市場調查研究及行業(yè)投資潛力預測報告
- 2024-2030年中國園林科技行業(yè)市場全景分析及投資前景展望報告
- 中國石墨化石油焦增碳劑市場調查與投資前景預測報告
- 中國二手汽車市場深度評估及行業(yè)投資前景咨詢報告
- 2025年智能分揀系統(tǒng)項目提案報告
- 中國枕式多功能包裝機行業(yè)市場發(fā)展前景及發(fā)展趨勢與投資戰(zhàn)略研究報告(2024-2030)
- 金融投資服務合同協(xié)議
- 2025年保密觀知識競賽題庫必考題含答案詳解
- 【作業(yè)設計】《跨學科實踐:制作簡易桿秤》作業(yè)設計人教版八年級下冊物理
- 車間班組安全培訓課件
- 2025春季學期國開河南電大本科《行政管理理論與實踐專題講座》一平臺無紙化考試(作業(yè)練習+我要考試)試題及答案
- 馬工程教育學課程
- 內蒙古自治區(qū)某礦區(qū)銀多金屬礦勘探及外圍普查設計
- 中國兒童嚴重過敏反應診斷與治療建議
- 中醫(yī)課件 第二節(jié)方劑的分類及常用方劑(臨本)學習資料
- 施工單位項目物資管理
- 2025安徽安慶市桐城經開區(qū)建設投資集團有限公司招聘12人筆試參考題庫附帶答案詳解
- 給水管道試壓、沖洗消毒方案
評論
0/150
提交評論