// // JMAirKissConnection.m // JMAirKiss // // Created by shengxiao on 16/3/2. // Copyright © 2016年 shengxiao. All rights reserved. // #import "JMAirKissConnection.h" #import "JMAirKissEncoder.h" #import "GCDAsyncUdpSocket.h" #import "ESPTouchTaskParameter.h" #import "ESP_NetUtil.h" #include #import #include #define kAirKiss_Port 10000 #define kAirKiss_Host @"255.255.255.255" #define kAirKiss_Limit_Return_Random_Num 5 #define IOS_CELLULAR @"pdp_ip0" #define IOS_WIFI @"en0" #define IOS_VPN @"utun0" #define IP_ADDR_IPv4 @"ipv4" #define IP_ADDR_IPv6 @"ipv6" @interface JMAirKissConnection() { JMAirKissEncoder *_airKissEncoder; NSTimer *_timer; // 超过1分钟未连接成功则表示失败 GCDAsyncUdpSocket *_clientUdpSocket; GCDAsyncUdpSocket *_serverUdpSocket; long _tag; int _returnRandomNum; BOOL _connectionDone; NSString* _localIP; } @property (nonatomic,strong) ESPTaskParameter *_parameter; @end @implementation JMAirKissConnection - (instancetype)init { self = [super init]; if (self) { _airKissEncoder = [[JMAirKissEncoder alloc] init]; _tag = 0; _returnRandomNum = 0; _connectionDone = false; // self._parameter = [[ESPTaskParameter alloc]init]; // // // check whether IPv4 and IPv6 is supported // NSString *localInetAddr4 = [ESP_NetUtil getLocalIPv4]; // if (![ESP_NetUtil isIPv4PrivateAddr:localInetAddr4]) { // localInetAddr4 = nil; // } // NSString *localInetAddr6 = [ESP_NetUtil getLocalIPv6]; // [self._parameter setIsIPv4Supported:localInetAddr4!=nil]; // [self._parameter setIsIPv6Supported:localInetAddr6!=nil]; [self setupClientUdpSocket]; [self setupServerUdpSocket]; } return self; } #pragma mark - Connection /** * AirKiss连接 * * @param ssidStr ssid * @param pswStr psw */ - (void)connectAirKissWithSSID:(NSString *)ssidStr password:(NSString *)password { NSMutableArray *dataArray = [_airKissEncoder createAirKissEncorderWithSSID:ssidStr ? :@"" password:password ? :@""]; _tag = 0; _returnRandomNum = 0; _connectionDone = false; _localIP = [self.class getIPAddress:YES]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_main_queue(), ^{ _timer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(connectFailure) userInfo:nil repeats:NO]; }); for (int i = 0;i < dataArray.count;i++) { if (_connectionDone == true) { break; } UInt16 length = [dataArray[i] unsignedShortValue]; NSMutableData *mData = [NSMutableData data]; UInt8 value = 0; for (int j = 0; j < length; j++) { [mData appendBytes:&value length:1]; } NSString*host = kAirKiss_Host; // if (@available(iOS 16.0, *)) // { // NSArray *arr = [_localIP componentsSeparatedByString:@"."]; // host=[NSString stringWithFormat:@"%@.%@.%@.255",arr[0], arr[1], arr[2]]; // } [_clientUdpSocket sendData:mData toHost:host port:kAirKiss_Port withTimeout:-1 tag:_tag]; [NSThread sleepForTimeInterval:0.004]; _tag++; } }); } - (void)closeConnection { _connectionDone = true; [_timer invalidate]; _timer = nil; [_clientUdpSocket close]; [_serverUdpSocket close]; _clientUdpSocket = nil; _serverUdpSocket = nil; } - (BOOL)validateContainsChinese:(NSString *)content { // ^[\u4e00-\u9fa5] 以中文开头 的字符串 // [\u4e00-\u9fa5] 包含中文 NSRegularExpression *regularexpression = [[NSRegularExpression alloc] initWithPattern:@"^[\u4e00-\u9fa5]" options:NSRegularExpressionCaseInsensitive error:nil]; return ([regularexpression numberOfMatchesInString:content options:NSMatchingReportProgress range:NSMakeRange(0, content.length)] > 0); } #pragma mark - Set up udp socket - (void)setupClientUdpSocket { NSError *error = nil; if (!_clientUdpSocket) { _clientUdpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; [_clientUdpSocket enableBroadcast:YES error:&error]; } BOOL bind = YES; // bind =[_clientUdpSocket bindToPort:0 error:&error] if (!bind) { } else { } if (![_clientUdpSocket beginReceiving:&error]) { } else { } if(error) { NSLog(@"airkiss socket error= %@",error.description); } } - (void)setupServerUdpSocket { NSError *error = nil; if (!_serverUdpSocket) { _serverUdpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()]; [_serverUdpSocket enableBroadcast:YES error:&error]; } if (![_serverUdpSocket bindToPort:kAirKiss_Port error:&error]) { } else { } if (![_serverUdpSocket beginReceiving:&error]) { } else { } if(error) { NSLog(@"airkiss socket error= %@",error.description); } } #pragma mark - Event Response - (void)connectFailure { [_timer invalidate]; _timer = nil; _connectionDone = true; if (_connectionFailure) { _connectionFailure(); [self closeConnection]; } } #pragma mark - GCDAsyncUdpSocketDelegate - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag { // You could add checks here } - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error { // You could add checks here } - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext { if (_serverUdpSocket == sock) { if (_connectionDone) { return; } NSString* serIp = [GCDAsyncUdpSocket hostFromAddress:address]; serIp= [serIp stringByReplacingOccurrencesOfString:@"::ffff:" withString:@""]; NSLog(@"serIp=%@,length=%ld",serIp,data.length); //uint16_t serPort = (int)[GCDAsyncUdpSocket portFromAddress:address]; if ([serIp isEqualToString:_localIP]) { return;//是本机的数据直接返回 } else { NSLog(@"收到回包=%@",data); } // 设备连接WIFI成功后会像10000端口发送至少20个UDP广播包所附带的随机数 if (data != nil) { UInt8 *bytes = (UInt8 *) [data bytes]; if (bytes[0] == _airKissEncoder.randomChar) { _returnRandomNum ++; if (_returnRandomNum >= kAirKiss_Limit_Return_Random_Num) { // 成功 [_timer invalidate]; _timer = nil; if (_returnRandomNum == kAirKiss_Limit_Return_Random_Num) { _connectionDone = true; NSData*MacData = [data subdataWithRange:NSMakeRange(1, data.length-1)]; NSString*macstring = [self.class dataToHexString:MacData]; if (_connectionSuccess) { _connectionSuccess(macstring); [self closeConnection]; } } } } } } } /* * 获取设备IP地址 */ + (NSString *)getIPAddress:(BOOL)preferIPv4 { NSArray *searchArray = preferIPv4 ? @[ IOS_VPN @"/" IP_ADDR_IPv4, /*IOS_VPN @"/" IP_ADDR_IPv6, */IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] : @[ IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ]; NSDictionary *addresses = [self getIPAddresses]; NSLog(@"addresses: %@", addresses); __block NSString *address; [searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) { address = addresses[key]; if(address) *stop = YES; } ]; return address ? address : @"0.0.0.0"; } + (NSDictionary *)getIPAddresses { NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8]; // retrieve the current interfaces - returns 0 on success struct ifaddrs *interfaces; if(!getifaddrs(&interfaces)) { // Loop through linked list of interfaces struct ifaddrs *interface; for(interface=interfaces; interface; interface=interface->ifa_next) { if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) { continue; // deeply nested code harder to read } const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr; char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ]; if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) { NSString *name = [NSString stringWithUTF8String:interface->ifa_name]; NSString *type; if(addr->sin_family == AF_INET) { if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) { type = IP_ADDR_IPv4; } } else { const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr; if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) { type = IP_ADDR_IPv6; } } if(type) { NSString *key = [NSString stringWithFormat:@"%@/%@", name, type]; addresses[key] = [NSString stringWithUTF8String:addrBuf]; } } } // Free memory freeifaddrs(interfaces); } return [addresses count] ? addresses : nil; } // NSdata进制转十六进制字符串 +(NSString *) dataToHexString:(NSData*)data { NSUInteger len = [data length]; char * chars = (char *)[data bytes]; NSMutableString * hexString = [[NSMutableString alloc] init]; for(NSUInteger i = 0; i < len; i++ ) [hexString appendString:[NSString stringWithFormat:@"%0.2hhx", chars[i]]]; return hexString; } // 十六进制字符串转NSdata进制 +(NSData *) stringToHexData:(NSString*)string{ NSInteger len = [string length] / 2; // Target length unsigned char *buf = malloc(len); unsigned char *whole_byte = buf; char byte_chars[3] = {'\0','\0','\0'}; int i; for (i=0; i < [string length] / 2; i++) { byte_chars[0] = [string characterAtIndex:i*2]; byte_chars[1] = [string characterAtIndex:i*2+1]; *whole_byte = strtol(byte_chars, NULL, 16); whole_byte++; } NSData *data = [NSData dataWithBytes:buf length:len]; free( buf ); return data; } -(void)dealloc { NSLog(@"cnn dealloc"); } @end