402 lines
12 KiB
Objective-C
402 lines
12 KiB
Objective-C
//
|
|
// 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 <ifaddrs.h>
|
|
#import <arpa/inet.h>
|
|
#include <net/if.h>
|
|
|
|
#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()<GCDAsyncUdpSocketDelegate>
|
|
{
|
|
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
|