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"]]];

 

//如果有哪里不對的地方請多多包涵,共同研究, 可能有些括號不全之類的. 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM