iOS开发 socket, 全局socket

 

因为项目的要求是全局的socket,  哪里都有可能使用到socket去发消息, 所以我把socket写在了单利里面

项目用的是 pod 'CocoaAsyncSocket'  三方库, 是异步的, 如果没有cocopods  那就去guthub下载一个

特别需要注意一点, 如果服务器一下连着发了好几条数据, 消息会阻塞. 明确来说是大部分的文章在发出一条数据之后只调用了一次[sock readDataWithTimeout:-1 tag:0]; 这个方法去手动接收. 类似于发一条才能收一条这种概念,所以我在每收到一条数据处理完后再次调用一次这个方法

1.建立单例类 (单利还不会的,那真的要去好好查了, 这里的单例可能说的不太详细)

.h文件

 

#import <Foundation/Foundation.h>

//导入头文件

#import "GCDAsyncSocket.h"

//遵循代理

@interface Singleton : NSObject<GCDAsyncSocketDelegate>

//全局socket

@property(nonatomic,strong) GCDAsyncSocket *socket;

 

//单例创建方法

//此类方法, 不管调用多少次. 都只会使用一个, 单利请谨慎使用

+(Singleton *)shareSingleton;

//socket连接

- (void)connectToServerWithHost:(NSString *)host AndPort:(int)port;

 

//socket发送消息

- (void)sendMassageWithData:(NSString *)data;

 

@end

.m文件

static Singleton * shareS = nil;

 

//单例实现方法

+ (Singleton *)shareSingleton{

    if (shareS == nil) {

        shareS = [[Singleton alloc]init];       

    }

    return shareS;

}

//实现.h连接方法

- (void)connectToServerWithHost:(NSString *)host AndPort:(int)port{

//这个方法在下面 , host 是后台给的服务器地址,  port是端口

    [self StartLiveBtnWithHost:host AndPort:port];

    

}

 //实现发送消息方法

- (void)sendMassageWithData:(NSString *)str{

 //这个方法是CocoaAsyncSocket 的方法,  str是其他地方调用的时候传来, 注意: 如果后台对socket消息有不同的要求,那么要沟通好, 比如消息头,消息体之类的

  NSData * data = [str dataUsingEncoding:NSUTF8StringEncoding];

    [self.socket writeData:data withTimeout:1 tag:200];

 

}

#pragma mark 建立Socket连接

- (void)StartLiveBtnWithHost:(NSString *)host AndPort:(int )port{

    NSLog(@"建立长连接");

    

//getProperIPWithAddress 是针对ipv6后, 做的处理 , 别急, 在下面 

    NSString * ipaddr = [self getProperIPWithAddress:host port:port];

    

    //创建一个socket对象

    GCDAsyncSocket * socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];

    

    //连接

    NSError *error = nil;

    [socket connectToHost:ipaddr onPort:port error:&error];

 

    self.socket = socket;

    

    if (error) {

        NSLog(@"%@",error);

    }

    

}

//针对ipv6网络环境下适配,ipv4环境直接使用原来的地址

- (NSString *)getProperIPWithAddress:(NSString *)ipAddr port:(UInt32)port

{

    NSError *addresseError = nil;

    NSArray *addresseArray = [GCDAsyncSocket lookupHost:ipAddr

                                                   port:port

                                                  error:&addresseError];

    if (addresseError) {

        NSLog(@"");

    }

    

    NSString *ipv6Addr = [[NSString alloc]init];

    for (NSData *addrData in addresseArray) {

        if ([GCDAsyncSocket isIPv6Address:addrData]) {

            ipv6Addr = [GCDAsyncSocket hostFromAddress:addrData];

        }

    }

    

    if (ipv6Addr.length == 0) {

        ipv6Addr = ipAddr;

    }

    

    return ipv6Addr;

}

 

#pragma mark 连接成功

-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port{

    

    NSLog(@"%s",__func__);

//只要走了这个代理方法, 就说明连接已经成功   

   NSLog(@"连接成功");

}

 

#pragma mark 断开连接

-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{

 //这里有两种情况,网络不畅时间太长连接失败, 或者用户退出正常断开, 我这里做了断开提示用户,并且相应的断线重连

    if (err) {

        NSLog(@"连接失败");

        [self.socket disconnect];

    //弹出提示框;

          UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"网络不畅断开连接,请检查重新连接" preferredStyle: UIAlertControllerStyleAlert];

        

        UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

     

        }];

        UIAlertAction * action2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

//这里就是重新调一下socket连接的方法, 以及一些其他操作,根据自己的需求来  

            [HttpRequest ReconnectOnloss];

        }];

        

        [alert addAction:action1];

        [alert addAction:action2];

        

        //弹出提示框;

        [[self appRootViewController] presentViewController:alert animated:true completion:nil];

        }

    }else{

        

        NSLog(@"正常断开");

 

        [self.timer invalidate];

        [self.socket disconnect];

        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"网络不畅断开连接,请检查重新连接" preferredStyle: UIAlertControllerStyleAlert];

        

        UIAlertAction * action1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

                    }];

        UIAlertAction * action2 = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

//这里就是重新调一下socket连接的方法, 以及一些其他操作,根据自己的需求来           

       [HttpRequest ReconnectOnloss];

            

        }];

        

        [alert addAction:action1];

        [alert addAction:action2];

        

        //弹出提示框;

        [[self appRootViewController] presentViewController:alert animated:true completion:nil];

  

    }

}

 

#pragma mark 数据发送成功

-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{

    NSLog(@"%s",__func__);

 

    //发送完数据手动读取,-1不设置超时

    [sock readDataWithTimeout:-1 tag:0];

 }

 

#pragma mark 读取数据

-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{

    //这里的data,就是实时收到的后台发来的消息, 如果服务器用了各种方式的加密, 还需要跟后台人员及时沟通

    NSLog(@"-------------data:%@",data);

    

    NSString *receiverStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    

    NSLog(@"--------------%@",receiverStr);

//在这里,或者在你的消息处理类里面调用, 这样就不用发, 也可以实时一直收到数据

  [sock readDataWithTimeout:-1 tag:0];

 

}

--------------------------------------------------------------------------------

//顺便加上调用发消息

//如果在单例里面发

 [self sendMassageWithData:[NSString stringWithFormat:@"8014,%@",dic[@"mRoleID"]]];

//如果在其他类发

 [[Singleton shareSingleton] sendMassageWithData:[NSString stringWithFormat:@"8014,%@",dic[@"mRoleID"]]];

 

//如果有哪里不对的地方请多多包涵,共同研究, 可能有些括号不全之类的. 

 

posted @ 2017-04-11 18:59  陈伟的博园  阅读(1594)  评论(1编辑  收藏  举报