长连接(OC)

来源:互联网 发布:linux添加ip地址 编辑:程序博客网 时间:2024/06/05 20:07

使用环境:实现与服务器保持一个长连接,用于接收消息;

编译环境: xcode8;

语 言:object-c;

协 议:TCP, socket;

描述:实现了app 与服务器保持一个长连接,能够及时判断出断网和重新连接网络,并且重新保持长连接;现在,只在前台的情况下,才保持长连接,后台和 app 没有打开的情况下,使用 APNs.(app 在后台的时候,长连接会很快的被系统 kill,如果不用 APNs, 也可以使用一段音乐,在后台的时候一直播放,这样就不会被 kill).

代码分为两部分, 一份是服务端, 一份是客户端, 服务端用于测试;

服务端代码:

1.项目中导入了GCDAsyncSocket这个类;(该类不做描述,请自行度娘);
2.自己创建一个类ServerListener,用于监听;

————————————–ServerListener.h————————————–

#import <Foundation/Foundation.h>@interface ServerListener : NSObject//开始服务-(void)start;//停止服务-(void)stop;@end

————————————–ServerListener.m————————————–

#import "ServerListener.h"#import "GCDAsyncSocket.h"//导入头文件@interface ServerListener()<GCDAsyncSocketDelegate>@property(nonatomic,strong)GCDAsyncSocket *serverSocket;//用来记录创建的 socket 对象@property(nonatomic,strong)NSMutableArray *clientSockets;//用来保存所有的 socket 对象数组@end@implementation ServerListener//懒加载-(NSMutableArray *)clientSockets{    if (!_clientSockets) {        _clientSockets = [NSMutableArray array];    }    return _clientSockets;}//开启服务器-(void)start{    // 创建服务端的Socket对象    self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];    // 绑定端口并监听    NSError *err = nil;    // 1.端口自己给,建议1024以上    [self.serverSocket acceptOnPort:5288 error:&err];    if (err) {        NSLog(@"端口号被占用%@",err);    }else{        NSLog(@"端口号绑定成功,服务开启");    }}//停止监听-(void)stop{    [self.serverSocket disconnect];}#pragma mark - socket的代理方法#pragma mark 有客户端请求连接到服务器-(void)socket:(GCDAsyncSocket *)serverSocket didAcceptNewSocket:(GCDAsyncSocket *)clientSocket{    // 1.存储客户端的socket对象    [self.clientSockets addObject:clientSocket];    NSLog(@"当前有%ld个连接的客户端 %@",self.clientSockets.count,clientSocket);    // 2.开启读取数据    //注意:没读取一次,就要调用下边方法一次;    [clientSocket readDataWithTimeout:-1 tag:0];}/** * 读取数据之前,调用客户端Socket对象的readDataWithTimeout方法 */#pragma mark 读取到客户端的数据-(void)socket:(GCDAsyncSocket *)clientSocket didReadData:(NSData *)data withTag:(long)tag{    // 1.转字符串    NSString *readStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    // 开启读取数据,为了下次能接收到数据    [clientSocket readDataWithTimeout:-1 tag:0];    // 2.处理请求数据    // 遍历所有客户端连接对象,将数据转发给其它的客户端    for (GCDAsyncSocket *socket in self.clientSockets) {        if (clientSocket != socket) {          //接收到消息,转发到客户端去,消息只发送给其他客户端;         [socket writeData:data withTimeout:-1 tag:0];        }      }}@end
3.在 main.m 中开启服务器;
//在 main.m中开启服务器#import <Foundation/Foundation.h>#import "ServerListener.h"int main(int argc, const char * argv[]) {    @autoreleasepool {        ServerListener *server = [[ServerListener alloc] init];        //开启服务器        [server start];        //开启主线程运行循环,保证程序不停止        [[NSRunLoop mainRunLoop] run];    }    return 0;}//注意:当开启服务器之后,可以在命令终端中输入: telnet ip port//ip 是服务器的 ip,连接上服务器;

客户端代码:

注意:因为长连接需要在 app打开的时候就开启, 所以在AppDelegate中实现相应的代码;
1.导入GCDAsyncSocket类, 网络监测类(ToolOperation);
2.在AppDelegate中实现相应的代码;

—————————————–AppDelegate—————————————–

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {#pragma 每次进入app,开启长连接,同时开启(心跳)-lzp    if ([ToolOperation NetWorkCheck]) {        if (self.clientSocket == nil) {            // 1.创建客户端Socket对象            GCDAsyncSocket *clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];            // 2.连接服务器            //HOST:服务器地址            //PORT: 服务器接口            NSError *err = nil;            [clientSocket connectToHost:HOST onPort:PORT error:&err];            if (err) {                //连接错误            }            //保存客服端对象            self.clientSocket = clientSocket;        }else{            // 2.连接服务器            NSError *err = nil;            [self.clientSocket connectToHost:HOST onPort:PORT error:&err];            if (err) {                //连接错误            }        }    }    if (self.daojishiTimer == nil) {    //开启心跳        self.daojishiCount = 0;        self.NetWorkCount = 0;        self.daojishiTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];    }    return YES;}- (void)applicationDidEnterBackground:(UIApplication *)application {#pragma 当进入后台的时候,取消长连接,并且停止(心跳)-lzp    [self.daojishiTimer invalidate];    self.daojishiTimer = nil;    [self.clientSocket disconnect];//关闭长连接}- (void)applicationWillEnterForeground:(UIApplication *)application {#pragma 当回到前台的时候,恢复长连接,开启(心跳)-lzp    if (self.clientSocket == nil) {        // 1.创建客户端Socket对象        GCDAsyncSocket *clientSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];        // 2.连接服务器        NSError *err = nil;        [clientSocket connectToHost:HOST onPort:PORT error:&err];        if (err) {            //连接错误        }        self.clientSocket = clientSocket;    }else{        // 2.连接服务器        NSError *err = nil;        [self.clientSocket connectToHost:HOST onPort:PORT error:&err];        if (err) {            //连接错误        }    }    if (self.daojishiTimer == nil) {    //开启心跳        self.daojishiCount = 0;//用来记录每隔 count 秒,请求一次长连接;        self.NetWorkCount = 0;//用来记录断网重新联网,请求一次长连接一次后的时间        self.daojishiTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFireMethod:) userInfo:nil repeats:YES];    }}

————————————-socket的代理方法————————————————

#pragma mark - socket的代理方法-lzp-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{//    NSLog(@"与服务器连接成功");#pragma 这里用于长连接保持的验证;;-lzp    NSString * WriteStr = @"token";    NSData * WriteData = [WriteStr dataUsingEncoding:NSUTF8StringEncoding];    [self.clientSocket writeData:WriteData withTimeout:30 tag:0];    // 连接成功,设置读取数据//每次读完都要调用下边的方法    [self.clientSocket readDataWithTimeout:-1 tag:0];}#pragma (心跳)方法-lzp-(void)timerFireMethod:(NSTimer *)theTimer{    NSInteger timeCount = 20;    self.daojishiCount = self.daojishiCount + 1;//    NSLog(@"%ld",(long)self.daojishiCount);    if (self.daojishiCount == timeCount && [ToolOperation NetWorkCheck] && !self.clientSocket.isConnected) {        NSError *err = nil;        [self.clientSocket connectToHost:HOST onPort:PORT error:&err];        if (err) {            //连接错误        }        //连接不成功--有网--发送了一次请求    }else if ([ToolOperation NetWorkCheck] && self.daojishiCount != timeCount && !self.clientSocket.isConnected){        self.NetWorkCount = self.NetWorkCount + 1;        if (!self.isSend) {            self.isSend = YES;            NSError *err = nil;            [self.clientSocket connectToHost:HOST onPort:PORT error:&err];            if (err) {                //连接错误            }            //连接不成功--有网--发送了一次请求        }else if (self.daojishiCount == timeCount){            NSError *err = nil;            [self.clientSocket connectToHost:HOST onPort:PORT error:&err];            if (err) {                //连接错误            }            //连接不成功--有网--发送了一次请求;        }    }else if (![ToolOperation NetWorkCheck]){        //无网络        self.isSend = NO;    }    if (self.daojishiCount == timeCount) {        self.daojishiCount = 0;    }    if (self.NetWorkCount == timeCount) {        self.NetWorkCount = 0;    }}#pragma 接收数据-lzp-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{    //1.data 转 string    NSString *readStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    NSLog(@"%@",readStr);    // 每次读取完数据,都要调用下面的方式    [self.clientSocket readDataWithTimeout:-1 tag:0];}

——————————————(心跳)方法—————————————-

#pragma (心跳)方法-lzp-(void)timerFireMethod:(NSTimer *)theTimer{    NSInteger timeCount = 20;    self.daojishiCount = self.daojishiCount + 1;    //当每隔 time count 秒,并且有网和没有连接的时候才会重新请求长连接;        if (self.daojishiCount == timeCount && [ToolOperation NetWorkCheck] && !self.clientSocket.isConnected) {        NSError *err = nil;        [self.clientSocket connectToHost:HOST onPort:PORT error:&err];        if (err) {            //连接错误        }        //连接不成功--有网--发送了一次请求    }else if ([ToolOperation NetWorkCheck] && self.daojishiCount != timeCount && !self.clientSocket.isConnected){    //这种情况是,当网络突然中断,再有网的时候,立即请求一次长连接        self.NetWorkCount = self.NetWorkCount + 1;        if (!self.isSend) {//如果已经发过了网络请求, 就等下一个 timecount 才会请求长连接            self.isSend = YES;            NSError *err = nil;            [self.clientSocket connectToHost:HOST onPort:PORT error:&err];            if (err) {                //连接错误            }            //连接不成功--有网--发送了一次请求        }else if (self.daojishiCount == timeCount){            NSError *err = nil;            [self.clientSocket connectToHost:HOST onPort:PORT error:&err];            if (err) {                //连接错误            }            //连接不成功--有网--发送了一次请求;        }    }else if (![ToolOperation NetWorkCheck]){        //无网络        self.isSend = NO;    }    if (self.daojishiCount == timeCount) {        self.daojishiCount = 0;    }    if (self.NetWorkCount == timeCount) {        self.NetWorkCount = 0;    }}
1 0
原创粉丝点击