iOS-TCP-UDP-基于CocoaAsyncSocket的运用

GCDAsyncSocket

在iOS开发中使用socket,一般都是用第三方库GCDAsyncSocket(虽然也有原生CFSocket)。

GCDAsyncSocket 下载地址: GCDAsyncSocket

使用之前需要先在项目引入ASyncSocket库:

  1. 把ASyncSocket库源码加入项目:只需要增加RunLoop目录中的AsyncSocket.h、AsyncSocket.m、AsyncUdpSocket.h和AsyncUdpSocket.m四个文件。
  2. 在项目增加CFNetwork框架:
    • 在Framework目录右健,选择Add–>Existing Files…
    • 选择 CFNetwork.framework

下面开始介绍一下如何使用ASyncSocket:

一般来说,一个用户只需要建立一个socket长连接,所以可以用单例类方便使用。

单例方法

1
2
3
4
5
6
7
8
9
10
// 创建单例
+ (Singleton *) sharedInstance
{
static Singleton *sharedInstace = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstace = [[self alloc] initPrivate];
});
return sharedInstace;
}

// 私有创建方法,不公开

1
2
3
4
5
6
- (instancetype)initPrivate {
if (self = [super init]) {
_lockStr = @"1234";
}
return self;
}

// 废除init创建方法

1
2
3
- (instancetype)init {
@throw [NSException exceptionWithName:@"初始化异常" reason:@"不允许通过init方法创建对象" userInfo:nil];
}

建立socket长连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define TIME_OUT 20

// 建立socket连接
-(void)socketConnectHost{
_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
NSLog(@"连接服务器");
NSError *error = nil;
[_socket connectToHost:_socketHost onPort:_socketPort withTimeout:TIME_OUT error:&error];
}

// socket成功连接回调
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port {
NSLog(@"成功连接到%@:%d",host,port);
_bufferData = [[NSMutableData alloc] init]; // 存储接收数据的缓存区
[_socket readDataWithTimeout:-1 tag:99];
}

心跳

1
2
3
4
5
6
7
8
9
@property (nonatomic, retain) NSTimer *heartTimer;   // 心跳计时器
在连接成功的回调方法里,启动定时器,每隔2秒向服务器发送固定的消息来检测长连接。
// 心跳连接
-(void)longConnectToSocket{
根据服务器要求发送固定格式的数据,假设为指令@"longConnect",但是一般不会是这么简单的指令
NSString *longConnect = @"longConnect";
NSData *dataStream = [longConnect dataUsingEncoding:NSUTF8StringEncoding];
[_socket writeData:dataStream withTimeout:1 tag:1];
}

断开连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
* 主动断开
- (void)cutOffSocket {
[_socket disconnect];
_socket.userData = @(SocketOfflineByUser);
NSLog(@"断开连接");
}
* 被动断开
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err {
if (err.code == 57) {
_socket.userData = @(SocketOfflineByWifiCut); // wifi断开
}
else {
_socket.userData = @(SocketOfflineByServer); // 服务器掉线
}
NSLog(@"断开连接,错误:%@",err);
}

发送消息

1
2
3
4
5
6
7
8
9
10
11
12
// 发消息
- (void)sendMessage:(NSData *)data {
[_socket writeData:data withTimeout:TIME_OUT tag:10];
}

// wirte成功
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
// 持续接收数据
// 超时设置为附属,表示不会使用超时
[_socket readDataWithTimeout:-1 tag:tag];
}

接收消息

1
2
3
4
5
6
7
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
// 在这里处理消息
[self disposeBufferData:data];

//持续接收服务端的数据
[sock readDataWithTimeout:-1 tag:tag];
}
  Total:    No.