在iOS开发中使用socket(CFNetwork),一般都是用第三方库AsyncSocket。
参考博客:http://my.oschina.net/worldligang/blog/396881?fromerr=WRR1ZAie
CocoaAsyncSocket的git下载地址:https://github.com/robbiehanson/CocoaAsyncSocket
下载CocoaAsyncSocket的文件,导入runloop中的AsyncSocket.h和AsyncSocket.m文件。
以下就是封装的CocoaAsyncSocket工具类DLSocketServe。
1 // 2 // DLSocketServe.h 3 // AsnycSocketDemo 4 // 5 // Created by wiseman on 15/12/23. 6 // Copyright (c) 2015年 wiseman. All rights reserved. 7 // 8 #import <Foundation/Foundation.h> 9 #import "AsyncSocket.h" 10 11 enum{ 12 SocketOfflineByServer, //服务器掉线 13 SocketOfflineByUser, //用户断开 14 SocketOfflineByWifiCut, //wifi 断开 15 }; 16 17 @interface DLSocketServe : NSObject<AsyncSocketDelegate> 18 19 @property (strong,nonatomic) AsyncSocket * socket; 20 21 @property (retain,nonatomic) NSTimer *heartTimer; //心跳计时器 22 23 +(DLSocketServe *)sharedSocketServe; 24 25 //socket连接 26 -(void)startConnetSocket; 27 28 //断开socket连接 29 -(void)cutOffSocket; 30 31 //发送消息 32 -(void)sendMessage:(id)message; 33 @end
1 // 2 // DLSocketServe.m 3 // AsnycSocketDemo 4 // 5 // Created by wiseman on 15/12/23. 6 // Copyright (c) 2015年 wiseman. All rights reserved. 7 // 8 9 10 #define HOST @"192.168.1.1" 11 #define PORT 8080 12 13 #define TIME_OUT 20 14 15 //设置读取超时 -1 表示不会使用超时 16 #define READ_TIME_OUT -1 17 18 //设置写入超时 -1表示不会使用超时 19 #define WRITW_TIME_OUT -1 20 #define MAX_BUFFER 1024 21 22 23 #import "DLSocketServe.h" 24 25 @implementation DLSocketServe 26 27 static DLSocketServe *socketServe = nil; 28 29 //创建一个单例方法 30 +(DLSocketServe *)sharedSocketServe{ 31 //@synchronized 的作用是创建一个互斥锁,保证此时没有其它线程对self对象进行修改。这个是objective-c的一个锁定令牌,防止self对象在同一时间内被其它线程访问,起到线程的保护作用。 一般在公用变量的时候使用,如单例模式或者操作类的static变量中使用。 32 @synchronized(self){ 33 if(socketServe == nil) { 34 socketServe = [[[self class] alloc] init]; 35 } 36 } 37 return socketServe; 38 } 39 40 +(instancetype)allocWithZone:(struct _NSZone *)zone{ 41 @synchronized(self){ 42 if (socketServe == nil) { 43 socketServe = [super allocWithZone:zone]; 44 return socketServe; 45 } 46 } 47 return nil; 48 } 49 50 -(void)startConnetSocket{ 51 self.socket = [[AsyncSocket alloc]initWithDelegate:self]; 52 [self.socket setRunLoopModes:@[NSRunLoopCommonModes]]; 53 if (![self SocketOpen:HOST port:PORT]) { 54 55 } 56 } 57 58 -(NSInteger)SocketOpen:(NSString *)addr port:(NSInteger)port{ 59 if (!([self.socket isConnected])) { 60 NSError *error = nil; 61 [self.socket connectToHost:addr onPort:port withTimeout:TIME_OUT error:&error]; 62 } 63 return 0; 64 } 65 66 67 //发送消息 68 -(void)sendMessage:(id)message{ 69 //向服务器发送数据 70 NSData *cmdData = [message dataUsingEncoding:NSUTF8StringEncoding]; 71 [self.socket writeData:cmdData withTimeout:WRITW_TIME_OUT tag:1]; 72 } 73 74 #pragma mark - AsyncSocket代理 75 -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{ 76 NSLog(@"这是异步返回的连接成功"); 77 78 //通过定时器不断的发送消息,来检测长连接 79 self.heartTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(checkLongConnectByServe) userInfo:nil repeats:YES]; 80 [self.heartTimer fire]; 81 } 82 83 //心跳连接 84 -(void)checkLongConnectByServe{ 85 //向服务器发送固定的消息,来检测长连接 86 NSString *longConnect = @"connect is here"; 87 NSData *data = [longConnect dataUsingEncoding:NSUTF8StringEncoding]; 88 [self.socket writeData:data withTimeout:0 tag:0]; 89 } 90 91 //断开连接 92 //1.用户手动断开连接 93 -(void)cutOffSocket{ 94 self.socket.userData = SocketOfflineByUser; 95 //cutOffSocket 是用户断开连接之后,不在尝试重新连接 96 [self.socket disconnect]; 97 } 98 99 //2.wifi断开,socket断开连接 100 -(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err{ 101 102 NSData * unreadData = [sock unreadData]; // ** This gets the current buffer 103 if(unreadData.length > 0) { 104 // socket出错会回调onSocket:willDisconnectWithError:方法,可以通过unreadData来读取未来得及读取的buffer。 105 [self onSocket:sock didReadData:unreadData withTag:0]; 106 107 } else { 108 109 //wifi断开之后,会回调onSocket:willDisconnectWithError:方法,err.code == 57,这个时候设置self.socket.userData = SocketOfflineByWifiCut 110 NSLog(@" willDisconnectWithError %ld err = %@",sock.userData,[err description]); 111 if (err.code == 57) { 112 self.socket.userData = SocketOfflineByWifiCut; 113 } 114 115 } 116 117 } 118 119 //重新连接 120 //socket断开之后会回调 在onSocketDidDisconnect回调方法里面,会根据self.socket.userData来判断是否需要重新连接。 121 -(void)onSocketDidDisconnect:(AsyncSocket *)sock{ 122 NSLog(@"%ld",sock.userData); 123 if (sock.userData == SocketOfflineByServer) { 124 //服务器掉线,重连 125 [self startConnetSocket]; 126 } 127 else if (sock.userData == SocketOfflineByUser){ 128 //如果由用户断开,不进行重连 129 return; 130 } 131 else if (sock.userData == SocketOfflineByWifiCut){ 132 //wifi断开,不进行重连 133 return; 134 } 135 } 136 137 138 //发送消息成功之后回调 139 -(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{ 140 //发送消息成功之后会调用onSocket:didWriteDataWithTag:,在这个方法里可以进行读取消息。 141 //读取消息 142 [self.socket readDataWithTimeout:-1 buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0]; 143 } 144 145 //接受消息成功之后回调 146 -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{ 147 //服务端返回消息数据量比较大时,可能分多次返回。所以在读取消息的时候,设置MAX_BUFFER表示每次最多读取多少,当data.length < MAX_BUFFER我们认为有可能是接受完一个完整的消息,然后才解析 148 149 if (data.length < MAX_BUFFER) { 150 //收到结果解析 151 NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil]; 152 NSLog(@"%@",dic); 153 //解析出来的消息,可以通过通知,代理,block等传递出去 154 155 } 156 157 [self.socket readDataWithTimeout:READ_TIME_OUT buffer:nil bufferOffset:0 maxLength:MAX_BUFFER tag:0]; 158 159 } 160 161 - (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket 162 { 163 NSLog(@"didAcceptNewSocket"); 164 } 165 166 167 168 @end
封装好之后,使用:
导入:#import "DLSocketServe.h"
1 DLSocketServe *socketServe = [DLSocketServe sharedSocketServe]; 2 //socket连接前先断开连接以免之前socket连接没有断开导致闪退 3 [socketServe cutOffSocket]; 4 socketServe.socket.userData = SocketOfflineByServer; 5 [socketServe startConnectSocket]; 6 7 //发送消息 @"hahaha"只是举个列子,具体根据服务端的消息格式 8 [socketServe sendMessage:@"hahaha"];