IOS开发之SOCKET长连接的使用

IOS应用的长连接如果保证效率的话,一般在应用内使用自己的socket长连接,在退出应用或者应用切换到后台之后使用苹果的推送来通知,这样就保证了在软件使用过程中,如果苹果服务器出问题不至于影响软件使用的情况。

一、IOS的长连接库

这个demo使用了一个第三方库:CocoaAsyncSocket,通过这个库来实现长连接

CocoaAsyncSocket下载

Github下载地址:https://github.com/DamonHu/CocoaAsyncSocket

GitOsc下载地址:http://git.oschina.net/DamonHoo/CocoaAsyncSocket

demo下载

Github下载地址:https://github.com/DamonHu/AsyncSocketDemo

GitOsc下载地址:http://git.oschina.net/DamonHoo/AsyncSocketDemo

二、长连接的流程

Socket长连接分为TCP和UDP两种,区别就是握手验证的区别,包括TCP的三次握手这个都可以百度下,连接的流程就是请求连接,连接成功之后客户端向服务器发送数据,服务器把处理的数据发送给客户端。

空白.png

和HTTP请求流程差不多,区别就是http请求每次更新数据都要向对应的端口发送一次请求,之后返回数据之后关闭连接,而长连接就是客户端和服务器一直连着,当有数据更新的时候,服务器会直接发给客户端,不需要客户端主动请求。

在这过程中,为了保证服务端和客户端一直是连接状态,客户端会定时不间断的发送心跳数据到服务器,表明还连接着,不然长时间没有数据更新,会断开连接,这样一直有心跳数据的时候,就会保证了连接没有中断,至于心跳数据的内容,就是前端后端共同商量的,和请求的数据是单独的。就相当于单独出来一个请求,数据是商量之后的心跳数据。

三、代码实现

UDP和TCP在这个库代码没什么区别,所以用TCP示例

流程就是

  • 发送连接请求

  • 请求成功开始定时发送心跳数据

  • 向服务器发送数据

  • 服务器向客户端发送数据,客户端改变

  • 断网重连

  • 用户关闭长连接

1、发送链接请求

创建一个tcp长连接,并且可以设定tcp内容,这个是内容是自己用来区分不同的socket请求的,然后通过connectToHost可以发送连接请求

-(void)tcpTest:(UIButton*)sender
{
    NSLog(@"tcpTest");
    self.myTcpSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    [self tcpConnetwithData:[NSString stringWithFormat:@"%d",(int)sender.tag]];
}
-(void)tcpConnetwithData:(NSString*)userData
{
    [self.myTcpSocket setUserData:userData];
    
    NSError *error = nil;
    if (![ self.myTcpSocket connectToHost:@"hudongdong.com" onPort:80 withTimeout:2.0f error:&error]) {
        NSLog(@"error:%@",error);
    }
}

2、请求成功开始定时发送心跳数据

发送完连接请求之后,如果连接成功会有成功回调,表示连接成功,在成功之后,可以开始发送心跳数据了

//连接
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    NSLog(@"didConnectToHost");
    //连上之后可以每隔30s发送一次心跳包
    self.mytime =[NSTimer scheduledTimerWithTimeInterval:30.0f target:self selector:@selector(heartbeatFunc) userInfo:nil repeats:YES];
    [self.mytime fire];
}

3、向服务器发送数据

发送数据用的就是这个函数

[self.myTcpSocket writeData:data withTimeout:10.0f tag:0];

所以心跳数据和正常数据的请求区别就是一个内容是用户传的,一个是前后端规定的

//发送心跳包
-(void)heartbeatFunc
{
    //心跳包的内容是前后端自定义的
    NSString *heart = @"Damon";
    NSData *data= [heart dataUsingEncoding:NSUTF8StringEncoding];
    [self.myTcpSocket writeData:data withTimeout:10.0f tag:0];
}

//发送数据
-(void)sendData
{
    NSString *dataStr = @"Damon_Hu";
    NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
    [self.myTcpSocket writeData:data withTimeout:10.0f tag:1];
    
    NSString *dataStr2 = @"Damon_Hu2";
    NSData *data2 = [dataStr2 dataUsingEncoding:NSUTF8StringEncoding];
    [self.myTcpSocket writeData:data2 withTimeout:10.0f tag:2];
}

向服务器发送数据完成之后,客户端也会有一个回调函数

//向服务器发送完数据之后回调
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    NSLog(@"have send");
    if (tag == 1) {
        NSLog(@"first send");
    }
    else if (tag ==2){
        NSLog(@"second send");
    }
}

在这个回调函数可以做相关逻辑。

4、服务器向客户端发送数据,客户端更新相关界面

当服务器向客户端发送有数据的时候,会调用这个函数,然后做对应的逻辑

//本地接收到数据之后回调
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    [self.myTcpSocket readDataWithTimeout:10.0f tag:tag];
    //接受到数据之后写入本地
    [self reciveData:data];
}
//接受数据更新本地内容
-(void)reciveData:(NSData*)data
{
    //接收到的数据写入本地
    NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}

5、断网重连和手动断网

当心跳数据断了的时候,需要判断是用户手动切断的,还是因为网络原因断了,所以需要自己在前端做一个判断是哪个原因。

首先定义一个状态值

enum{
    SOCKET_OFFLINE_SERVER = -2,//服务器断开
    SOCKET_OFFLINE_USER,       //用户主动断开
};

如果是用户手动切断的,那么就修改状态值,断开链接和停止心跳包发送

-(void)disConnectSocket
{
    for (GCDAsyncSocket *socket in self.mySocketArray) {
        socket.userData = [NSString stringWithFormat:@"%d",SOCKET_OFFLINE_USER];
        [self.mytime invalidate];   //停止心跳包发送
        [socket disconnect];    //断开链接
    }
    
}

如果不是用户手动切断,那就是其他原因了,所以需要断网重连接,断开连接的时候,会有这个回调

//断开连接
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err
{
    NSLog(@"socketDidDisconnect");
    //主动断开
    if ([sock.userData isEqualToString:[NSString stringWithFormat:@"%d",SOCKET_OFFLINE_USER]]) {
        return;
    }
    else{
        NSLog(@"%@",err);
        //断线重连
        [self tcpConnetwithData:@"1"];
    }
}

需要做的就是判断如果是用户主动断开就不再重连接,如果不是主动断开的,那么就重新请求连接即可。

四、后续说明

这个是关于Socket链接的demo,但是有很多地方需要调试,因为没有服务端,所以暂时这样,到有服务端的时候,再调试下,其他具体未说细节可以百度,这个demo就是说明一个思路,我服务端还没有搭建,所以没有测试,后面测试通过之后补充上来,demo用了masonry库自动布局,使用的pod导入。

五、参考文章:

Last modification:January 1st, 1970 at 08:00 am
如果看了这个文章可以让你少加会班,可以请我喝杯可乐
已打赏名单
微信公众号

Leave a Comment