Facebook家的SocketRocket应该是OC语言里针对WebSocket最好的框架啦,因为业务需求,我们把应用的部分请求更换成了长链接,以下是使用总结。
至于为什么要使用WebSocket?当然是为了与服务器更方便快捷地进行通讯,不用每次请求都得客户端先给服务器发一个Request,然后等待服务器对应的response;而可以由服务端决定什么时候给你推送什么消息,这点对于某些通信类的功能模块是很方便的。
如果你想深入了解下Websocket的原理,可以看这篇WebSocket 是什么原理?为什么可以实现持久连接?
接下来谈谈WebSocket的使用,因为整个SocketRocket就只有一个.h和.m文件,所以socket的使用也相对简单。
1.首先建造一个SocketRocketUtility单例以便整个项目使用WebSocket来通信
@implementation SocketRocketUtility
+(SocketRocketUtility *)instance{
static SocketRocketUtility *Instance = nil;
static dispatch_once_t once;
dispatch_once(&once, ^{
Instance = [[SocketRocketUtility alloc]init];
}
}
2.然后创建一个Socket来建立通信
在.m文件中添加类的Extension
@interface SocketRocketUtility()<SRWebSocketDelegate>
@property (nonatomic, strong) SRWebSocket *socket;
@end
创建socket并建立连接
- (void) SRWebSocketOpen{
self.socket =[[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: @"your string "]]];
self.socket.delegate = self;
[self.socket open];
}
-
然后处理socket连接的结果,监听连接是否成功的回调
#pragma mark - socket delegate
如果连接建立成功
- (void)webSocketDidOpen:(SRWebSocket *)webSocket { //开启心跳 [self initHeartBeat]; if (webSocket == self.socket) { NSLog(@"************************** socket 连接成功************************** "); } }
如果连接建立失败
- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { if (webSocket == self.socket) { NSLog(@"************************** socket 连接失败************************** "); _socket = nil; //连接失败就重连 [self reConnect]; } }
这里有个心跳规则,其实一般的Websocket的第三方框架会自带这个并不需要自己做特别处理,只要与服务器商量好时间,定时地向服务器发送ping消息,服务器会返回相应地pong消息,表示我们的连接还在
- (void) initHeartBeat{
heartBeat = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(ping) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:heartBeat forMode:NSRunLoopCommonModes];
}
这里用了NSTimer,所以要注意对应所在线程的runloop;正常情况下,将NSTimer添加到主线程的runloop中,为了确保这段代码在主行程中执行,我们添加一段宏
#define dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}
然后将上面的代码改成
dispatch_main_async_safe(^{
heartBeat = [NSTimer timerWithTimeInterval:10 target:self selector:@selector(ping) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop]addTimer:heartBeat forMode:NSRunLoopCommonModes];
})
具体的ping方法就是调用第三方框提供的方法
- (void) ping{
if(self.socket.readyState == SR_OPEN){
[self.socket sendPing:nil];
}
}
4.就是想服务器发送数据,一般跟服务器通信传递的数据是json格式,所以记得把之前字典里的信息以及服务器要求的一些相关字段转换成json格式,然后send:
- (void)sendData{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (weakself.socket != nil) {
// 只有 SR_OPEN 开启状态才能调 send 方法啊,不然要崩
if (weakself.socket.readyState == SR_OPEN) {
[weakself.socket send:data]; // 发送数据
} else if (weakself.socket.readyState == SR_CONNECTING) {
[self reConnect];
} else if (weakself.socket.readyState == SR_CLOSING || weakself.socket.readyState == SR_CLOSED) {
// websocket 断开了,调用 reConnect 方法重连
[self reConnect];
}
}
else{
//如果在发送数据,但是socket已经关闭,可以在再次打开
[self SRWebSocketOpen];
}
});
}
5.接收服务器信息
#pragma mark - 收到的回调
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message {
if (webSocket == self.socket) {
NSLog(@"message:%@",message);
if(!message){
return;
}
//收到的数据格式也是json格式的,可以用MJExtension里字典转模型的方法更改为自定义的数据格式
//这里要将接收到的消息专递出去,可以用block或者通知的形式;个人偏好block一点
[self handleReceivedMessage:message];
}
}
6.关闭Socket连接
最后可以选择在合适的时候关闭长连接
-(void)SRWebSocketClose{
if (self.socket){
[self.socket close];
self.socket = nil;
//断开连接时销毁心跳
[self destoryHeartBeat];
}
}
具体代码在这里 WebsocketDemo-iOS
这里推荐一个可用的Websocket在线测试网站:http://www.blue-zero.com/WebSocket/
表示可以看看我的star,对于一个程序员,我star里面的东西还是蛮不错哒
- 参考文章 iOS Websocket接入