【iOS逆向与安全】好用的一套 TCP 类

 初始化

//页面
%hook xxxxxxxViewController

//- (void)viewWillAppear:(BOOL)animated{
//NSLog(@"View Will Appear,再次进入刷新");
- (void)viewDidLoad{
    
    //启动tcp
    [[Xddtcp sharedTcpManager] connectServer] ;

}

 发送数据

//发送数据 

[[Xddtcp sharedTcpManager] sendDataToServer:[@"MyiPhone" dataUsingEncoding:NSUTF8StringEncoding]] ;


源码类

//
//  Xddtcp.m
//  YFX_ScoketForClientDemo
//
//  Created by adminxdd on 2020/8/20.
//  Copyright © 2020 fangxue. All rights reserved.
//

#import "Xddtcp.h"


#define WSELF __weak typeof(self) wself = self;

/** 主线程异步队列 */
#define Dispatch_main_async_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_async(dispatch_get_main_queue(), block);\
}

//@implementation Xddtcp
@interface Xddtcp() 

/** 心跳计时器 */
@property (nonatomic, strong) NSTimer *heartBeatTimer;

/** 没有网络的时候检测网络定时器 */
@property(nonatomic,strong)NSTimer*netWorkTestingTimer;

/** 存储要发送给服务端的数据 */
@property (nonatomic, strong) NSMutableArray *sendDataArray;

/** 数据请求队列(串行队列) */
@property (nonatomic, strong) dispatch_queue_t queue;

/** 重连时间 */
@property (nonatomic, assign) NSTimeInterval reConnectTime;

/** 用于判断是否主动关闭长连接,如果是主动断开连接,连接失败的代理中,就不用执行 重新连接方法 */
@property (nonatomic, assign) BOOL isActivelyClose;

/** 缓存区域 */
@property (nonatomic, strong) NSMutableData *readBuf;

@end

@implementation Xddtcp

#pragma mark - 
#pragma mark ----单利------

/** 单利 */
+ (instancetype)sharedTcpManager{
    static Xddtcp*_instace =nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,^{
        _instace = [[self alloc]init];
        NSLog(@"TCP 单利实例化!");
    });
    return _instace;
}
#pragma mark ----初始化------

/** 初始化 */
- (instancetype)init{
    self= [super init];
    if(self){
        self.reConnectTime = 0;
        self.isActivelyClose = NO;
        self.queue = dispatch_queue_create("BF",NULL);
        self.sendDataArray = [[NSMutableArray alloc] init];
        //        self.mRecvPacket = [JHreceivePacket sharedManager];
        //        self.deviceListArr = [[NSMutableArray alloc] init];
        //        DLogInfo(@"初始化!");
    }
    return self;
}

#pragma mark - 

#pragma mark --- 心跳计时器处理 -----

/** 心跳计时器初始化 */
- (void)initHeartBeat{
    //心跳没有被关闭
    if(self.heartBeatTimer)
    {
        return;
    }
    [self destoryHeartBeat];
    
    //连接成功  先发一次 心跳数据 在打开计时器
    //[[JHSocketData sharedManager] heartbeat];
    
    //判断是否打开
    if([xddTools keyGet:@"isOpenTCP"]&&[[xddTools keyGet:@"isOpenTCP"] isEqualToString:@"已打开"]){
        
        [self.asyncTcpSocket writeData:[@"MyiPhone" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];
        
    }
    
    WSELF
    Dispatch_main_async_safe(^{
        wself.heartBeatTimer  = [NSTimer timerWithTimeInterval:30 target:wself selector:@selector(senderheartBeat) userInfo:nil repeats:true];
        
        [[NSRunLoop currentRunLoop]addTimer:wself.heartBeatTimer forMode:NSRunLoopCommonModes];
        
    });
    
}

#pragma mark --- 取消心跳 -----

/** 取消心跳 */
- (void)destoryHeartBeat
{
    WSELF
    Dispatch_main_async_safe(^{
        if(wself.heartBeatTimer){
            [wself.heartBeatTimer invalidate];
            wself.heartBeatTimer =nil;
        }
    });
}

#pragma mark --- 没有网络的时候开始定时 -- 用于网络检测 -----

/** 没有网络,进行网络监测 */
- (void)noNetWorkStartTestingTimer{
    WSELF
    Dispatch_main_async_safe(^{
        wself.netWorkTestingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:wself selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:wself.netWorkTestingTimer forMode:NSDefaultRunLoopMode];
        
    });
}

#pragma mark --- 取消网络监测 ----

/** 取消网络监测 */
- (void)destoryNetWorkStartTesting
{
    WSELF
    Dispatch_main_async_safe(^{
        if(wself.netWorkTestingTimer){
            [wself.netWorkTestingTimer invalidate];
            wself.netWorkTestingTimer =nil;
        }
    });
}

#pragma mark - 

#pragma mark --- 发送心跳 ---

/** 发送心跳 */
- (void)senderheartBeat{
    //  和服务端约定好发送什么作为心跳标识,尽可能的减小心跳包大小
    [self.asyncTcpSocket writeData:[@"ok" dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:0];
    
    //    WSELF;
    //    Dispatch_main_async_safe(^{
    //
    //    });
}

#pragma mark --- 定时检查网络 ---

/** 定时检查网络 */
- (void)noNetWorkStartTesting{
    //有网络
    if(AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus != AFNetworkReachabilityStatusNotReachable)
    {
        //关闭网络检测定时器
        [self destoryNetWorkStartTesting];
        //开始重连
        [self reConnectServer];
    }
    
}
#pragma mark - 

#pragma mark ----建立连接------

/**  建立连接 */

-(void)connectServer{
    
    self.isActivelyClose = NO;
    
    NSError*error =nil;
    
    _asyncTcpSocket= [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    
    NSString* ip       = [xddTools keyGet:@"TCP_IP"];
    NSString* port_str = [xddTools keyGet:@"TCP_Port"];
    
    if([ip isEqualToString:@""]){
        ip = @"192.168.1.2";
        [xddTools keySet:@"TCP_IP" v:ip];
    }
    
    if([port_str isEqualToString:@""]){
        port_str = @"12485";
        [xddTools keySet:@"TCP_Port" v:port_str];
    }
    
    UInt16 port = [port_str integerValue];
    
    //你的ip地址和端口号
    [_asyncTcpSocket connectToHost:ip onPort:port withTimeout:60 error:&error];
    
    if(error) {
        NSLog(@"TCP连接错误:%@", error);
    }
    [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
    
}

#pragma mark --- 重连服务器 ---

/** 重连服务器 */
- (void)reConnectServer{
    if (self.asyncTcpSocket.isConnected) {
        return;
    }
    if (self.reConnectTime > 10240) {
        self.reConnectTime = 0;
        return;
    }
    WSELF;
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reConnectTime *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if(wself.asyncTcpSocket.isConnected)
        {
            return;
        }
        [wself connectServer];
        NSLog(@"正在重连%f",wself.reConnectTime);
        if(wself.reConnectTime==0){ //重连时间2的指数级增长
            wself.reConnectTime=2;
        } else {
            wself.reConnectTime*=2;
        }
    });
    
    NSLog(@"重连服务器!");
    
}

#pragma mark ----关闭连接------
/** 关闭连接 */
- (void)disConnectServer {
    
    NSLog(@"dealloc");
    // 关闭套接字
    self.isActivelyClose = YES;
    [self.asyncTcpSocket disconnect];
    //关闭心跳定时器
    [self destoryHeartBeat];
    //关闭网络检测定时器
    [self destoryNetWorkStartTesting];
    self.asyncTcpSocket = nil;
}

#pragma mark ---代理 连接成功 ---
//连接成功
-(void)socket:(GCDAsyncSocket*)sock didConnectToHost:(NSString*)host port:(uint16_t)port
{
    NSLog(@"TCP连接成功!");
    //    [self.asyncTcpSocket readDataWithTimeout:-1 tag:index];
    //      [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
    
    // 存储接收数据的缓存区,处理数据的粘包和断包
    self.readBuf = [[NSMutableData alloc]init];
    
    [self initHeartBeat]; //开启心跳
    
    //如果有尚未发送的数据,继续向服务端发送数据
    
    if([self.sendDataArray count] >0){
        [self sendeDataToServer];
    }
}

#pragma mark ---代理 连接失败 ---
//连接失败 进行重连操作
-(void)socketDidDisconnect:(GCDAsyncSocket*)sock withError:(NSError*)err{
    //用户主动断开连接,就不去进行重连
    if(self.isActivelyClose)
    {
        return;
    }
    [self destoryHeartBeat]; //断开连接时销毁心跳
    //判断网络环境
    if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) //没有网络
    {
        [self noNetWorkStartTestingTimer];//开启网络检测定时器
    }
    else//有网络
    {
        [self reConnectServer];//连接失败就重连
    }
    NSLog(@"TCP连接失败!");
}

#pragma mark --- 消息发送成功 ---
-(void)socket:(GCDAsyncSocket*)sock didWriteDataWithTag:(long)tag{
    NSLog(@"TCP发送成功");
}

#pragma mark - 

#pragma mark --- 发送数据 ---
-(void)sendDataToServer:(id)data{
    
    [self.sendDataArray addObject:data];
    [self sendeDataToServer];
    //      [self.asyncTcpSocket  writeData:data withTimeout:60 tag:0];
}

#pragma mark --- 发送数据给服务器 详细处理 ---

/** 发送数据的详细处理 */
-(void)sendeDataToServer{
    
    WSELF
    //把数据放到一个请求队列中
    dispatch_async(self.queue, ^{
        //网络判断  没有网络的情况
        if (AFNetworkReachabilityManager.sharedManager.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
            //开启网络检测定时器
            [wself noNetWorkStartTestingTimer];
        }else{ //有网络情况
            //判断对象是否存在
            if(wself.asyncTcpSocket != nil) {
                //判断TCP是否处于连接状态
                if(wself.asyncTcpSocket.isConnected) {
                    //判断数据  是否存在
                    if(wself.sendDataArray.count>0) {
                        id sendData = wself.sendDataArray[0];
                        NSLog(@"TCP发送出去的数据: %@",sendData);
                        [self.asyncTcpSocket writeData:sendData withTimeout:30  tag:0];
                        [wself.sendDataArray removeObjectAtIndex:0];
                        if([wself.sendDataArray count] >0){
                            [wself sendeDataToServer];
                        }
                    }
                }else{
                    //TCP 处于断开状态
                    //主动去重连服务器  连接成功后 继续发送数据
                    [wself reConnectServer];
                }
            }else{
                [wself reConnectServer];
            }
        }
    });
}

#pragma mark - 

#pragma mark --- 接收的数据 ---
-(void)socket:(GCDAsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag
{
    //      sleep(1);
    //此方法处理数据的黏包或者断包
    NSLog(@"原始数据%@",data);
    
    NSString*msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"xdd接收的数据 = %@",msg);
    
    //断包处理
    [self didReadData:data];
    
    [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
    
    
    NSDictionary*retDic = [xddTools dictionaryWithJsonString:msg];
    if(retDic == nil || ! [retDic isKindOfClass:[NSDictionary class]]) {
        return ;
    }
    
    NSString*type_ = retDic[@"type"];
    NSString*data_ = retDic[@"data"];
    NSString*msgid_ = retDic[@"msgid"]?retDic[@"msgid"]:@"test";
    
    NSLog(@"xdd接收的数据type_=%@,type_=%@",type_,data_);
   //开始主线程
//    dispatch_async(dispatch_get_main_queue(), ^{

        //开始子线程
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
        if(type_ && data_ && [type_ isEqualToString:@"1"]){
            //获取森林信息
            NSDictionary*dic = [xddTools getMYinfo:data_];
            
            BOOL success =  [dic[@"success"] boolValue]; //是否成功
            NSString*resultDesc = dic[@"resultDesc"];//信息提示
            NSString*resultCode = dic[@"resultCode"];//返回代码
            
            NSString*currentEnergy = dic[@"treeEnergy"][@"currentEnergy"];//当前能量
            
            NSDictionary*userEnergy = dic[@"userEnergy"];
            NSString*loginId = userEnergy[@"loginId"];//[email protected]
            NSString*userId = userEnergy[@"userId"];//2088*****
            NSString*headPortrait = userEnergy[@"headPortrait"];//头像
            NSString*displayName = userEnergy[@"displayName"];//昵称 茶
            NSString*energySummation = userEnergy[@"energySummation"];//合计能量
            NSString*treeAmount = userEnergy[@"treeAmount"];//证书个数
            
            NSDictionary *dicRetInfo = [NSDictionary dictionaryWithObjectsAndKeys:
            msgid_,@"msgid"
            ,type_,@"type"
            ,resultCode,@"code"
            ,currentEnergy,@"current"
            ,energySummation,@"summation"
            ,userId,@"userId"
            ,loginId,@"loginId"
            ,headPortrait,@"png"
            ,treeAmount,@"tree"
            ,displayName,@"name"
            ,nil];
            
            NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dicRetInfo] dataUsingEncoding:NSUTF8StringEncoding];
            //NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dicRetInfo options:NSJSONWritingPrettyPrinted error:nil];
            [self sendDataToServer:jsonData];
            
        }else if(type_ && data_ && ( [type_ isEqualToString:@"2"] || [type_ isEqualToString:@"4"])){
            NSDictionary*dic;
            
            if([type_ isEqualToString:@"2"]){
                // 查询用户手机 昵称信息
                dic = [xddTools getiPhoneInfo:data_];
                
            }else if([type_ isEqualToString:@"4"]){
                // 查询用户ID昵称信息
                dic = [xddTools getuseriDInfo:data_];
            }
            
            NSString*account = dic[@"userAccount"];
            NSString*userID = dic[@"userID"];
            NSString*nickl = dic[@"userNicklName"];
            NSString*name = dic[@"userName"];
            NSString*status = dic[@"resultStatus"];
            NSString*Suffix = dic[@"userNameSuffix"];
            NSString*RealName = dic[@"userRealName"];
            NSString*gender = dic[@"gender"];
            //userNameSuffix 实名名字
            //userRealName 注册名字
            //gender 性别
            
            NSDictionary *dicRetInfo = [NSDictionary dictionaryWithObjectsAndKeys:
            msgid_,@"msgid"
            ,type_,@"type"
            ,status,@"status"
            ,account,@"account"
            ,userID,@"userID"
            ,nickl,@"nickl"
            ,name,@"name"
            ,Suffix,@"Suffix"
            ,RealName,@"RealName"
            ,gender,@"gender"
            ,nil];
                       
            
            //625 对方账户存在异常,不能进行当前操作 100 ok
            NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dicRetInfo] dataUsingEncoding:NSUTF8StringEncoding];
            //NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dicRetInfo options:NSJSONWritingPrettyPrinted error:nil];
            [self sendDataToServer:jsonData];
            
        }else if(type_ && data_ && [type_ isEqualToString:@"3"]){
            //获取合种二维码信息
            NSDictionary*dic = [xddTools gethezhongqrCode:data_];
            
            
//            BOOL success =  [dic[@"success"] boolValue]; //是否成功
//            NSString*resultDesc = dic[@"resultDesc"];//信息提示
//            NSString*resultCode = dic[@"resultCode"];//返回代码
            
            // 可变字典 转可变字典
            NSMutableDictionary *dict002 = [NSMutableDictionary dictionaryWithDictionary:dic];
            [dict002 setObject:msgid_ forKey:@"msgid"];
            [dict002 setObject:type_ forKey:@"type"];
            
            [dict002 removeObjectForKey:@"extInfo"];
            
//            NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dict002] dataUsingEncoding:NSUTF8StringEncoding];
            NSData * jsonData = [NSJSONSerialization dataWithJSONObject:dict002 options:NSJSONWritingPrettyPrinted error:nil];
            [self sendDataToServer:jsonData];
            
        
        }
        else if(type_ && data_ && [type_ isEqualToString:@"5"]){
            //获取签名
             NSDictionary*dic = [xddTools getAlipaysign:retDic];
            
            
            NSMutableDictionary *dict002 = [NSMutableDictionary dictionaryWithDictionary:dic];
            [dict002 setObject:msgid_ forKey:@"msgid"];
            [dict002 setObject:type_ forKey:@"type"];

            NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dict002] dataUsingEncoding:NSUTF8StringEncoding];
            [self sendDataToServer:jsonData];
            
        }
        else if(type_ && data_ && [type_ isEqualToString:@"6"]){
            //获取签名
             NSDictionary*dic = [xddTools getAlipaysignV1:retDic];
            
            
            NSMutableDictionary *dict002 = [NSMutableDictionary dictionaryWithDictionary:dic];
            [dict002 setObject:msgid_ forKey:@"msgid"];
            [dict002 setObject:type_ forKey:@"type"];

            NSData * jsonData = [[xddTools returnJSONStringWithDictionary:dict002] dataUsingEncoding:NSUTF8StringEncoding];
            [self sendDataToServer:jsonData];
            
        }
        else if(type_ && data_ && [type_ isEqualToString:@"xxx"]){
            
        }
        
            
            
    });
    
}
#pragma mark - 
#pragma mark --- 黏包 断包处理 ---
-(void) didReadData:(NSData*)data {
    
    //断包处理 要根据 你的 数据的 长度标识位的数据 来判断 读到什么地方 才是你完整的数据。根据协议去走
    
    _readBuf = [NSMutableData dataWithData:data];
    
    // 取出4-8位保存的数据长度,计算数据包长度
    
    while(_readBuf.length>=5) {// 头数据为5个字节
        // 得到数据的ID 和 整个数据的长度
        NSData *dataLength = [_readBuf subdataWithRange:NSMakeRange(3, 2)];
        Byte*ByteLength = (Byte*)[dataLength bytes];
        int headLen = (ByteLength[0] &0x00ff) + ((ByteLength[1] &0x00ff) <<8);
        NSInteger lengthInteger =0;
        
        lengthInteger = (NSInteger)headLen;
        NSInteger complateDataLength = lengthInteger +6;//算出一个包完整的长度(内容长度+头长度)
        NSLog(@"已读取数据:缓冲区长度:%ld, 接收长度:%lu  数据长度:%ld ", (long)_readBuf.length, (unsigned long)[data length], (long)complateDataLength);
        
        //        NSInteger dataLength = length + 2;
        if(_readBuf.length>= complateDataLength) {//如果缓存中的数据 够  一个整包的长度
            NSData*msgData = [_readBuf subdataWithRange:NSMakeRange(0, complateDataLength)];
            
            // 处理消息数据
            NSLog(@"得到完整包数据:%@",msgData);
            // 从缓存中截掉处理完的数据,继续循环
            _readBuf= [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(complateDataLength,_readBuf.length- complateDataLength)]];
            
            //            [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
        }else{// 断包情况,继续读取
                
                //            [self.asyncTcpSocket readDataWithTimeout:-1 tag:0];
                
                //            [sock readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//继续读取数据
                
            [self.asyncTcpSocket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];
            return;
        }
        [self.asyncTcpSocket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//继续读取数据
    }
    
}

@end
//
//  Xddtcp.h
//  YFX_ScoketForClientDemo
//
//  Created by adminxdd on 2020/8/20.
//  Copyright © 2020 fangxue. All rights reserved.
//

#import 
#import "AFNetworkReachabilityManager.h"
NS_ASSUME_NONNULL_BEGIN



NS_ASSUME_NONNULL_END


#import 

#import "GCDAsyncSocket.h"

#import "xddTools.h"

//@interface SingleTcpCase :NSObject
@interface Xddtcp : NSObject

@property(strong,nonatomic)GCDAsyncSocket *asyncTcpSocket;

/**  单利初始化 */

+(instancetype)sharedTcpManager;

/**  建立连接 */

-(void)connectServer;

/**  关闭连接 */

-(void)disConnectServer;

/**  发送数据给服务器 */

-(void)sendDataToServer:(id)data;



@end

配置端口

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请输入配置信息" message:@"++++++" preferredStyle:UIAlertControllerStyleAlert];
            
            //增加取消按钮;
            [alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
            //增加确定按钮;
            [alertController addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
                //获取第1个输入框;
                UITextField *userNameTextField = alertController.textFields.firstObject;
                
                //获取第2个输入框;
                UITextField *passwordTextField = alertController.textFields.lastObject;
                //NSLog(@"用户名 = %@,密码 = %@",userNameTextField.text,passwordTextField.text);
                
                if(userNameTextField.text && ![userNameTextField.text isEqualToString:@""]){
                    [xddTools keySet:@"TCP_IP" v:userNameTextField.text];
                }
                if(passwordTextField.text && ![passwordTextField.text isEqualToString:@""]){
                    [xddTools keySet:@"TCP_Port" v:passwordTextField.text];
                }
            }]];
            
            //定义第一个输入框;
            [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
                textField.placeholder = @"请输入配置IP";
                textField.text = [xddTools keyGet:@"TCP_IP"];
            }];
            //定义第二个输入框;
            [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
                textField.placeholder = @"请输入配置端口";
                textField.text = [xddTools keyGet:@"TCP_Port"];
            }];
            
            [weakSelf presentViewController:alertController animated:true completion:nil];
+(NSString*)keyGet:(NSString*)k
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    return [userDefault objectForKey:k]?[userDefault objectForKey:k]:@"";
//    - objectForKey:
//    - URLForKey:
//    - arrayForKey:
//    - dictionaryForKey:
//    - stringForKey:
//    - stringArrayForKey:
//    - dataForKey:
//    - boolForKey:
//    - integerForKey:
//    - floatForKey:
//    - doubleForKey:
//    - dictionaryRepresentation
}
+(void)keySet:(NSString*)k v:(NSString*)v
{
    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    [userDefault setObject:v forKey:k];
    [userDefault synchronize];
//        - setObject:forKey:
//       - setFloat:forKey:
//       - setDouble:forKey:
//       - setInteger:forKey:
//       - setBool:forKey:
//       - setURL:forKey:
}

你可能感兴趣的:(五花肉科技,IOS分析,教程专区,ios,安全,tcp/ip)