这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战
由于项目极光推送有的时候延迟,因此使用了Socket.IO,iOS端也要集成响应的socket。
1. 概念
Socket:所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。
WebSocket:WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
而比较新的技术去做轮询的效果是Comet。这种技术虽然可以双向通信,但依然需要反复发出请求。而且在Comet中,普遍采用的长链接,也会消耗服务器资源。
在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
在iOS 平台上,我们知道socket的开源框架有 CocoaAsyncSocket, 而websocket的框架有Facebook的 SocketRocket, 以及socket.io-client-swift。接下来就看下Socket.IO-Client-Swif在OC中的使用。
2.Socket.IO-Client-Swif使用
2.1 导入
use_frameworks!
target 'YourApp' do
pod 'Socket.IO-Client-Swift', '~> 15.2.0'
end
在OC中使用swift要添加桥
没有OC项目中没有swift文件随意创建一个swift文件
导入:
Swift:
import SocketIO
Objective-C:
@import SocketIO;
2.2 应用
- (SocketManager *)manger {
if (!_manger) {
NSString *url = WebSocket_url;//后端约定的URL,connectParams是后端约定的自定义参数
NSDictionary *paramDict = @{@"log": @YES,
@"compress": @NO ,
@"connectParams":@{@"userID":[NSString stringWithFormat:@"kbdc_0_%@",[UserConfig shareInstance].model.id]}
};
_manger = [[SocketManager alloc] initWithSocketURL:[NSURL URLWithString:url] config:paramDict];
}
return _manger;
}
- (SocketIOClient *)socket {
if (!_socket) {
// _socket = [self.manger socketForNamespace:@"saas"];//传入约定好的命名空间
_socket = self.manger.defaultSocket; //如果没有区分命名空间的话,可以去默认的socket
}
return _socket;
}
connect进行连接
//建立长连接
- (void)connectServer{
self.isActivelyClose = NO;
[self.socket disconnect];//如果连接了,断开连接
_manger = nil;
_socket = nil;
[self.socket connect];//懒加载重新连接
[self blockDealWithMethod];//监听回调
}
主要是初始化manager,之后socket进行连接。
callback监听回调
-(void)blockDealWithMethod
{
@weakify(self)
[self.socket on:@"connect" callback:^(NSArray * _Nonnull dataArray, SocketAckEmitter * _Nonnull emitter) {
[self webSocketDidOpen];
NSLog(@"链接成功connect:%@",dataArray);
}];
[self.socket on:@"reconnect_failed" callback:^(NSArray * _Nonnull dataArray, SocketAckEmitter * _Nonnull emitter) {
@strongify(self)
[self webSocketDidFailWithError:nil];
NSLog(@"重连失败%@",dataArray);
}];
[self.socket on:@"sendMsg" callback:^(NSArray * _Nonnull dataArray, SocketAckEmitter * _Nonnull emitter) {
@strongify(self)
if ([self.delegate respondsToSelector:@selector(webSocketManagerDidReceiveMessageWithString:)]) {
[self.delegate webSocketManagerDidReceiveMessageWithString:dataArray[0]];
}
NSLog(@"收到的消息是%@",dataArray);
NSString *dictStr = dataArray[0];
NSData *jsonData = [dictStr dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
[PushHandle shareHandle].webSocketInfoDic= dic;//需要处理的服务器发送的消息。
}];
[self.socket on:@"sendHeartbeatMsg" callback:^(NSArray * _Nonnull dataArray, SocketAckEmitter * _Nonnull emitter) {
NSLog(@"收到💗");
}];
[self.socket on:@"disconnect" callback:^(NSArray * _Nonnull dataArray, SocketAckEmitter * _Nonnull emitter) {
NSLog(@"断开连接%@",dataArray);
}];
}
通过监听对应的消息类型,进行相对应的消息回调。主要是sendMsg中进行逻辑处理,大致类型如上图。
clsoeSocket关闭长连接
//关闭长连接
- (void)RMWebSocketClose{
self.isActivelyClose = YES;
self.isConnect = NO;
self.connectType = WebSocketDefault;
if(self.socket)
{
[self.socket disconnect];
_manger = nil;
_socket = nil;
}
//关闭心跳定时器
[self destoryHeartBeat];
//关闭网络检测定时器
[self destoryNetWorkStartTesting];
}
keepHeartbeat发送心跳
//发送心跳
- (void)senderheartBeat{
//和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
WS(weakSelf);
dispatch_main_async_safe(^{
if(weakSelf.socket.status == 3){
[weakSelf sendPing:nil];
}
});
}
- (void)sendPing:(id)sender{
[self.socket emit:@"keepHeartbeat" with:@[@"hello"]];
}
#pragma mark - NSTimer
\
//初始化心跳
- (void)initHeartBeat{
//心跳没有被关闭
if(self.heartBeatTimer) {
return;
}
[self destoryHeartBeat];
dispatch_main_async_safe(^{
self.heartBeatTimer = [NSTimer timerWithTimeInterval:55 target:self selector:@selector(senderheartBeat) userInfo:nil repeats:true];
[[NSRunLoop currentRunLoop]addTimer:self.heartBeatTimer forMode:NSRunLoopCommonModes];
})
}
主要初始化定时器,和后台规定多久发送一次消息类型是keepHeartbeat,通过emit发送,发送消息也可以.
- 断线重连
//重新连接
- (void)reConnectServer{
if(self.socket.status == 3 ){
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.socket.status == 3 && weakSelf.socket.status == 2) {
return;
}
[weakSelf connectServer];
// CTHLog(@"正在重连......");
if(weakSelf.reConnectTime == 0){ //重连时间2的指数级增长
weakSelf.reConnectTime = 2;
}else{
weakSelf.reConnectTime *= 2;
}
});
}
//没有网络的时候开始定时 -- 用于网络检测
- (void)noNetWorkStartTestingTimer{
WS(weakSelf);
dispatch_main_async_safe(^{
weakSelf.netWorkTestingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:weakSelf selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:weakSelf.netWorkTestingTimer forMode:NSDefaultRunLoopMode];
});
}
//定时检测网络
- (void)noNetWorkStartTesting{
//有网络
if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus != AFNetworkReachabilityStatusNotReachable)
{
//关闭网络检测定时器
[self destoryNetWorkStartTesting];
//开始重连
[self reConnectServer];
}
}
//取消网络检测
- (void)destoryNetWorkStartTesting{
WS(weakSelf);
dispatch_main_async_safe(^{
if(weakSelf.netWorkTestingTimer)
{
[weakSelf.netWorkTestingTimer invalidate];
weakSelf.netWorkTestingTimer = nil;
}
});
}
3. 总结
对于Socket.IO-Client-Swif,我们单独封装一个单例的类,方便我们全局调用。消息处理也设计一个单例,方便处理,以上就是Socket.IO-Client-Swif在OC项目中的集成,主要是初始化SocketManager和SocketIOClient,进行连接,监听消息,发送消息或心跳,注意重连机制。