修改sleep 问题和udp服务问题
This commit is contained in:
parent
a8a122a6e0
commit
1fb432a784
@ -6,5 +6,5 @@ Author: XYZShell
|
||||
Section: Utilities
|
||||
Tag: role::developer
|
||||
Architecture: iphoneos-arm
|
||||
Version: 0.0.7-10-62+debug
|
||||
Installed-Size: 1604
|
||||
Version: 0.0.7-10-66+debug
|
||||
Installed-Size: 1624
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
./packages/com.xyzshell.ioscontrol_0.0.7-10-62+debug_iphoneos-arm.deb
|
||||
./packages/com.xyzshell.ioscontrol_0.0.7-10-66+debug_iphoneos-arm.deb
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
62
|
||||
66
|
||||
@ -157,7 +157,7 @@
|
||||
self.ip = [[XSPhoneInfo sharedInstance] IPAddress];
|
||||
self.remoteIp = [[XSPhoneInfo sharedInstance] remoteIp];
|
||||
self.diskSize = [[XSPhoneInfo sharedInstance] IPhoneStatus];
|
||||
self.message = @"251103-nt";
|
||||
self.message = @"251106-nt";
|
||||
// 构建请求数据
|
||||
NSDictionary *heartbeatData = [self constructHeartbeatData];
|
||||
if (!heartbeatData) {
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#import "XSPhoneInfo.h"
|
||||
#import "XSHelper.h"
|
||||
#import "XSHackIos.h"
|
||||
#import "MyScriptTask.h"
|
||||
#import "MyEventBus.h"
|
||||
#import "XSHttpHelper.h"
|
||||
#import "MyAdTask2.h"
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
//
|
||||
// MyAdTask2.m
|
||||
// nochange
|
||||
//
|
||||
// Created by mac on 2024/11/6.
|
||||
//
|
||||
|
||||
#include "XUDPServer.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "IPhoneHertbeat.h"
|
||||
@ -16,16 +9,13 @@
|
||||
#import "MyAdTask2.h"
|
||||
#import "MyEventBus.h"
|
||||
|
||||
|
||||
BOOL myadTaskManualStop = NO;
|
||||
|
||||
|
||||
@interface MyAdTask2Mangger()
|
||||
{
|
||||
@private
|
||||
BOOL running;
|
||||
// NSTimer *timer;
|
||||
dispatch_source_t _timer; // 使用GCD timer替代NSTimer
|
||||
dispatch_source_t _timer;
|
||||
NSDate *lastRun;
|
||||
NSDate *lastGetCountry;
|
||||
NSString *_lastIdfa;
|
||||
@ -39,7 +29,6 @@ BOOL myadTaskManualStop = NO;
|
||||
NSString *dataId;
|
||||
NSString *remoteIp;
|
||||
NSString *country;
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@ -49,11 +38,12 @@ BOOL myadTaskManualStop = NO;
|
||||
+(instancetype)sharedInstance {
|
||||
static MyAdTask2Mangger* _sharedInstance = nil;
|
||||
static dispatch_once_t oncePredicate;
|
||||
dispatch_once (&oncePredicate, ^{
|
||||
dispatch_once(&oncePredicate, ^{
|
||||
_sharedInstance = [[MyAdTask2Mangger alloc] init];
|
||||
});
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
-(instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self->running = NO;
|
||||
@ -70,7 +60,7 @@ BOOL myadTaskManualStop = NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (int) onShow: (NSDictionary *)dic {
|
||||
- (int)onShow:(NSDictionary *)dic {
|
||||
if(dic == nil || [dic isEqual:[NSNull null]] || [dic count] <= 0) {
|
||||
return 4000;
|
||||
}
|
||||
@ -80,35 +70,13 @@ BOOL myadTaskManualStop = NO;
|
||||
NSNumber *t_ecpm = [self getNum:@"ecpm" dic:dic];
|
||||
BOOL ad = dic[@"ad"];
|
||||
NSString *iphoneId = [[XSPhoneConfig sharedInstance] IPhoneName];
|
||||
// NSString *ipAddr = [[XSPhoneInfo sharedInstance] IPAddress];
|
||||
|
||||
/*
|
||||
saveAdTaskLog(@{
|
||||
@"title": @"广告展示",
|
||||
@"message": [XSHelper dic2Json:dic],
|
||||
@"level": @(1),
|
||||
@"appid": t_appid,
|
||||
@"idfa":t_idfa,
|
||||
@"adid": t_adId,
|
||||
@"ecpm": [NSString stringWithFormat:@"%@", t_ecpm],
|
||||
@"taskId": self->taskId,
|
||||
@"iphoneId":iphoneId,
|
||||
@"ipAddr":ipAddr
|
||||
|
||||
});
|
||||
*/
|
||||
|
||||
/* --
|
||||
if (self->current > -1 && self->current < (NSInteger)self->tasks.count) {
|
||||
cur = self->tasks[self->current];
|
||||
}
|
||||
*/
|
||||
if(!ad) {
|
||||
NSLog(@"XS- no ad show");
|
||||
return 0;
|
||||
}
|
||||
int close = 4000;
|
||||
//--new
|
||||
|
||||
AdLoadInfo adloadInfo = getAdLoadInfo(@{
|
||||
@"idfa": t_idfa ?: @"",
|
||||
@"adId": t_adId ?: @"",
|
||||
@ -117,11 +85,6 @@ BOOL myadTaskManualStop = NO;
|
||||
@"deviceId":iphoneId ?: @""
|
||||
});
|
||||
|
||||
// cur.loads = adloadInfo.loads;
|
||||
//cur.adTouchBeforeMs = adloadInfo.adTouchBeforeMs;
|
||||
// cur.adTime= adloadInfo.adTime;
|
||||
|
||||
|
||||
close = [adloadInfo.adTime intValue];
|
||||
int before = [adloadInfo.adTouchBeforeMs intValue];
|
||||
if (before >= close) {
|
||||
@ -134,19 +97,24 @@ BOOL myadTaskManualStop = NO;
|
||||
self->adAfter = 1000;
|
||||
}
|
||||
|
||||
NSInteger random1 = arc4random_uniform(101); // 0-100
|
||||
NSInteger random1 = arc4random_uniform(101);
|
||||
NSInteger tr = [adloadInfo.touchRate intValue];
|
||||
if (tr > random1) {
|
||||
[self touchAppTask: before / 1000];
|
||||
double beforeSec = before / 1000.0;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(beforeSec * NSEC_PER_SEC)),
|
||||
self.manQueue, ^{
|
||||
[weakSelf touchAppTask:0];
|
||||
});
|
||||
}
|
||||
return close;
|
||||
}
|
||||
|
||||
- (void) showStatus: (NSString *)data {
|
||||
- (void)showStatus:(NSString *)data {
|
||||
[[MyEventBus sharedInstance] postEvent:@"UpdateStatus" withObject:data];
|
||||
}
|
||||
|
||||
- (void) setRemoteInfo {
|
||||
- (void)setRemoteInfo {
|
||||
NSString *url = @"https://ipapi.co/json/";
|
||||
XSHttpHelper *http = [[XSHttpHelper alloc] init];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
@ -164,8 +132,10 @@ BOOL myadTaskManualStop = NO;
|
||||
}
|
||||
NSString *isoCode = dic[@"country_code"];
|
||||
NSString *ip = dic[@"ip"];
|
||||
// callback(ip, isoCode);
|
||||
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
strongSelf->remoteIp = [ip copy];
|
||||
strongSelf->country = [isoCode copy];
|
||||
[XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip];
|
||||
@ -176,8 +146,8 @@ BOOL myadTaskManualStop = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) setRemoteInfo1 {
|
||||
NSString *url = [[XSPhoneConfig sharedInstance] GetRemoteIPURL];
|
||||
- (void)setRemoteInfo1 {
|
||||
NSString *url = [[XSPhoneConfig sharedInstance] GetRemoteIPURL];
|
||||
[self showStatus:url];
|
||||
if (!url) {
|
||||
NSLog(@"!setRemoteInfo url");
|
||||
@ -207,9 +177,11 @@ BOOL myadTaskManualStop = NO;
|
||||
NSString *isoCode = _data[@"isoCode"];
|
||||
NSString *ip = _data[@"ip"];
|
||||
NSLog(@"setRemoteInfo:%@", _data);
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
strongSelf->remoteIp = [ip copy];
|
||||
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
strongSelf->remoteIp = [ip copy];
|
||||
strongSelf->country = [isoCode copy];
|
||||
[XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip];
|
||||
[weakSelf showStatus:[XSPhoneInfo sharedInstance].remoteIp];
|
||||
@ -217,13 +189,9 @@ BOOL myadTaskManualStop = NO;
|
||||
NSLog(@"!setRemoteInfo err:%@",err);
|
||||
[weakSelf showStatus:[NSString stringWithFormat:@"%@", err.description]];
|
||||
}];
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (BOOL) onEnd: (NSDictionary *)dic {
|
||||
- (BOOL)onEnd:(NSDictionary *)dic {
|
||||
NSNumber *maxEcpm = dic[@"max_ecpm"];
|
||||
int sec = self->adAfter / 1000;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
@ -244,11 +212,10 @@ BOOL myadTaskManualStop = NO;
|
||||
[weakSelf startApp];
|
||||
}
|
||||
});
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) startApp {
|
||||
- (void)startApp {
|
||||
@autoreleasepool {
|
||||
if(!self->running){
|
||||
return;
|
||||
@ -266,32 +233,24 @@ BOOL myadTaskManualStop = NO;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(![@"US" isEqual:self->country]) {
|
||||
[self showStatus:[NSString stringWithFormat:@"%@:%@, Not US", self->country, self->remoteIp]];
|
||||
return;
|
||||
}
|
||||
*/
|
||||
__weak typeof(self) weakSelf = self;
|
||||
getChangeInfo(self->_lastIdfa, ^(NSDictionary *dic) {
|
||||
dispatch_async(weakSelf.manQueue, ^{
|
||||
// 耗时操作
|
||||
[weakSelf onChangeInfo: dic];
|
||||
[weakSelf onChangeInfo:dic];
|
||||
});
|
||||
|
||||
}, ^(NSError *err) {
|
||||
NSLog(@"XS- startTask NSError is null, %@", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void) start {
|
||||
- (void)start {
|
||||
self->running = YES;
|
||||
myadTaskManualStop = NO;
|
||||
[[IPhoneHertbeat sharedInstance] setStatus: @"运行中"];
|
||||
[[IPhoneHertbeat sharedInstance] setStatus:@"运行中"];
|
||||
[self startApp];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
// 使用 dispatch_source 创建timer
|
||||
|
||||
if (!_timer) {
|
||||
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.manQueue);
|
||||
dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), 10 * NSEC_PER_SEC, 1 * NSEC_PER_SEC);
|
||||
@ -305,7 +264,7 @@ BOOL myadTaskManualStop = NO;
|
||||
[[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(YES)];
|
||||
}
|
||||
|
||||
- (void) proc {
|
||||
- (void)proc {
|
||||
if (!self->running) {
|
||||
return;
|
||||
}
|
||||
@ -315,19 +274,23 @@ BOOL myadTaskManualStop = NO;
|
||||
NSTimeInterval elapsed = [curRunTime timeIntervalSinceDate:self->lastRun];
|
||||
|
||||
if (elapsed >= 30) {
|
||||
//[self onStop];
|
||||
[self start];
|
||||
|
||||
} else {
|
||||
NSString* appId = [ self frontMostAppId];
|
||||
NSString* appId = [self frontMostAppId];
|
||||
|
||||
if (![appId isEqual: self->taskAppId]) {
|
||||
if (![appId isEqual:self->taskAppId]) {
|
||||
[self appKill:appId];
|
||||
sleep(1);
|
||||
[self appRun:self->taskAppId];
|
||||
|
||||
// ⭐️ 修复: 先转为strong再使用
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), self.manQueue, ^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
[strongSelf appRun:strongSelf->taskAppId];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (NSString *)toggle {
|
||||
@ -339,7 +302,7 @@ BOOL myadTaskManualStop = NO;
|
||||
return [IPhoneHertbeat sharedInstance].status;
|
||||
}
|
||||
|
||||
- (void) stop {
|
||||
- (void)stop {
|
||||
myadTaskManualStop = YES;
|
||||
self->running = NO;
|
||||
[IPhoneHertbeat sharedInstance].status = @"已停止";
|
||||
@ -350,14 +313,12 @@ BOOL myadTaskManualStop = NO;
|
||||
[[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(NO)];
|
||||
}
|
||||
|
||||
|
||||
- (void) onChangeInfo: (NSDictionary *)dic {
|
||||
NSString* t_idfa = [self getStr: @"idfa" dic:dic];
|
||||
- (void)onChangeInfo:(NSDictionary *)dic {
|
||||
NSString* t_idfa = [self getStr:@"idfa" dic:dic];
|
||||
if (![XSHelper strIsEmpty:t_idfa]) {
|
||||
self->_lastIdfa = [t_idfa copy];
|
||||
} else {
|
||||
NSLog(@"XS- startTask t_idfa is null");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -370,13 +331,11 @@ BOOL myadTaskManualStop = NO;
|
||||
self->dataId = [_dataId copy];
|
||||
}
|
||||
|
||||
|
||||
NSString* t_taskId = [self getStr:@"id" dic:dic];
|
||||
if (![XSHelper strIsEmpty:t_taskId]) {
|
||||
self->taskId = [t_taskId copy];
|
||||
} else {
|
||||
NSLog(@"XS- startTask t_taskId is null");
|
||||
|
||||
return;
|
||||
}
|
||||
NSString* t_taskAppId = [self getStr:@"appId" dic:dic];
|
||||
@ -385,7 +344,6 @@ BOOL myadTaskManualStop = NO;
|
||||
[IPhoneHertbeat sharedInstance].appId = self->taskAppId;
|
||||
} else {
|
||||
NSLog(@"XS- startTask t_taskAppId is null");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -394,7 +352,6 @@ BOOL myadTaskManualStop = NO;
|
||||
|
||||
} else {
|
||||
NSLog(@"XS- startTask workerInfo is null");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -403,29 +360,39 @@ BOOL myadTaskManualStop = NO;
|
||||
self->workType = t_workType;
|
||||
} else {
|
||||
NSLog(@"XS- startTask workType is null");
|
||||
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self restart:^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
saveChangeDataFile(dic);
|
||||
self->running = YES;
|
||||
sleep(1);
|
||||
[self appRun:self->taskAppId];
|
||||
strongSelf->running = YES;
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
|
||||
strongSelf.manQueue, ^{
|
||||
__strong typeof(weakSelf) strongSelf2 = weakSelf;
|
||||
if (!strongSelf2) return;
|
||||
|
||||
[strongSelf2 appRun:strongSelf2->taskAppId];
|
||||
});
|
||||
}];
|
||||
//sleep(1);
|
||||
}
|
||||
|
||||
|
||||
- (void) appKill: (NSString*) app {
|
||||
- (void)appKill:(NSString*)app {
|
||||
NSLog(@"XS- appKill");
|
||||
XSKillAppByName(app);
|
||||
}
|
||||
- (void) unlock {
|
||||
|
||||
- (void)unlock {
|
||||
NSLog(@"XS- unlock");
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
XSRemoteUnlock();
|
||||
});
|
||||
}
|
||||
- (void) resetApp: (NSString*) appId callback:(OnEndCallback) callback {
|
||||
|
||||
- (void)resetApp:(NSString*)appId callback:(OnEndCallback)callback {
|
||||
NSLog(@"XS- JSApi resetApp");
|
||||
NSString *appInfoPath = XSGetAppInfoPath(appId);
|
||||
NSDictionary *appInfo = XSGetAppInfo(appInfoPath);
|
||||
@ -443,19 +410,21 @@ BOOL myadTaskManualStop = NO;
|
||||
__weak typeof(self) weakSelf = self;
|
||||
getLowEcpm(^(NSNumber *lowEcpm) {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
BOOL washParam = strongSelf->workType == 0 ? YES : NO;
|
||||
[tempBfaceDictKey setValue:@(washParam) forKey:@"washParam"];
|
||||
[tempBfaceDictKey setValue:lowEcpm forKey:@"adbrush_ecpm"];
|
||||
if(strongSelf->linkId) {
|
||||
[tempBfaceDictKey setValue:strongSelf->linkId forKey:@"linkId"];
|
||||
} else {
|
||||
} else {
|
||||
NSString *linkId = [[NSUUID UUID] UUIDString];
|
||||
[tempBfaceDictKey setValue:linkId forKey:@"linkId"];
|
||||
}
|
||||
|
||||
if(strongSelf->dataId) {
|
||||
[tempBfaceDictKey setValue:strongSelf->dataId forKey:@"dataId"];
|
||||
} else {
|
||||
} else {
|
||||
NSString *dataId = @"0";
|
||||
[tempBfaceDictKey setValue:dataId forKey:@"dataId"];
|
||||
}
|
||||
@ -475,10 +444,12 @@ BOOL myadTaskManualStop = NO;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
- (NSString *) frontMostAppId {
|
||||
|
||||
- (NSString *)frontMostAppId {
|
||||
return XSFrontMostAppId();
|
||||
}
|
||||
- (void) appRun:(NSString *)appId {
|
||||
|
||||
- (void)appRun:(NSString *)appId {
|
||||
if ([XSHelper strIsEmpty:appId]) return;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@ -490,22 +461,27 @@ BOOL myadTaskManualStop = NO;
|
||||
});
|
||||
}
|
||||
|
||||
- (void) tap: (int) i x:(int)x y:(int)y {
|
||||
- (void)tap:(int)i x:(int)x y:(int)y {
|
||||
}
|
||||
|
||||
- (void) restart: (OnEndCallback) callback {
|
||||
- (void)restart:(OnEndCallback)callback {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_async(self.manQueue, ^{
|
||||
@try {
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
[weakSelf appKill:@"com.apple.AppStore"];
|
||||
[weakSelf appKill:@"com.apple.mobilesafari"];
|
||||
if (strongSelf->taskAppId && ![strongSelf->taskAppId isEqual:[NSNull null]] && [strongSelf->taskAppId length] > 0) {
|
||||
[weakSelf appKill:strongSelf->taskAppId];
|
||||
[weakSelf unlock];
|
||||
sleep(1);
|
||||
[weakSelf unlock];
|
||||
[weakSelf resetApp:strongSelf->taskAppId callback:callback];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
|
||||
strongSelf.manQueue, ^{
|
||||
[weakSelf unlock];
|
||||
[weakSelf resetApp:strongSelf->taskAppId callback:callback];
|
||||
});
|
||||
}
|
||||
} @catch (NSException *exception) {
|
||||
NSLog(@"Error in restart: %@", exception);
|
||||
@ -513,44 +489,73 @@ BOOL myadTaskManualStop = NO;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
- (void) rndTouchApp {
|
||||
- (void)rndTouchApp {
|
||||
int times = [XSHelper random:2 and:4];
|
||||
for (int i = 1; i <= times; i++) {
|
||||
int x = [XSHelper random:40 and:screen_w - 20];
|
||||
int y = [XSHelper random:50 and:screen_h - 240];
|
||||
NSString* appId = [self frontMostAppId];
|
||||
if ([appId isEqual:self->taskAppId]) {
|
||||
[self tap:1 x:x y:y];
|
||||
usleep(2000);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[self _rndTouchAppRecursive:0 totalTimes:times];
|
||||
}
|
||||
|
||||
- (void) touchAppTask: (int) beforeTouch {
|
||||
if (beforeTouch > 0) {
|
||||
sleep(beforeTouch);
|
||||
- (void)_rndTouchAppRecursive:(int)currentIndex totalTimes:(int)times {
|
||||
if (currentIndex >= times) {
|
||||
return;
|
||||
}
|
||||
[self rndTouchApp];
|
||||
sleep(2);
|
||||
|
||||
int x = [XSHelper random:40 and:screen_w - 20];
|
||||
int y = [XSHelper random:50 and:screen_h - 240];
|
||||
[self tap:1 x:x y:y];
|
||||
NSString* appId = [self frontMostAppId];
|
||||
|
||||
NSString* appId = [ self frontMostAppId];
|
||||
if ([appId isEqual:self->taskAppId]) {
|
||||
[self tap:1 x:x y:y];
|
||||
|
||||
if (![appId isEqual: self->taskAppId]) {
|
||||
// [self appKill:appId];
|
||||
sleep(1);
|
||||
__weak typeof(self) weakSelf = self;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.002 * NSEC_PER_SEC)),
|
||||
self.manQueue, ^{
|
||||
[weakSelf _rndTouchAppRecursive:currentIndex + 1 totalTimes:times];
|
||||
});
|
||||
}
|
||||
|
||||
[self appRun:self->taskAppId];
|
||||
}
|
||||
- (NSString*) getStr: (NSString *) key dic: (NSDictionary*) dic {
|
||||
|
||||
- (void)touchAppTask:(int)beforeTouch {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
double delaySec = beforeTouch;
|
||||
|
||||
void (^touchBlock)(void) = ^{
|
||||
[weakSelf rndTouchApp];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),
|
||||
weakSelf.manQueue, ^{
|
||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||
if (!strongSelf) return;
|
||||
|
||||
int x = [XSHelper random:40 and:strongSelf->screen_w - 20];
|
||||
int y = [XSHelper random:50 and:strongSelf->screen_h - 240];
|
||||
[strongSelf tap:1 x:x y:y];
|
||||
|
||||
NSString* appId = [strongSelf frontMostAppId];
|
||||
|
||||
if (![appId isEqual:strongSelf->taskAppId]) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
|
||||
strongSelf.manQueue, ^{
|
||||
__strong typeof(weakSelf) strongSelf2 = weakSelf;
|
||||
if (!strongSelf2) return;
|
||||
|
||||
[strongSelf2 appRun:strongSelf2->taskAppId];
|
||||
});
|
||||
} else {
|
||||
[strongSelf appRun:strongSelf->taskAppId];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (delaySec > 0) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delaySec * NSEC_PER_SEC)),
|
||||
self.manQueue, touchBlock);
|
||||
} else {
|
||||
touchBlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)getStr:(NSString *)key dic:(NSDictionary*)dic {
|
||||
NSLog(@"getStr: %@", key);
|
||||
id t_idfa = dic[key];
|
||||
if (t_idfa && ![t_idfa isEqual:[NSNull null]] && ![t_idfa isEqual:@""]) {
|
||||
@ -560,29 +565,29 @@ BOOL myadTaskManualStop = NO;
|
||||
return @"";
|
||||
}
|
||||
}
|
||||
- (NSNumber*) getNum: (NSString *) key dic: (NSDictionary*) dic {
|
||||
|
||||
- (NSNumber*)getNum:(NSString *)key dic:(NSDictionary*)dic {
|
||||
NSLog(@"getNum: %@", key);
|
||||
id t_idfa = dic[key];
|
||||
NSString *ecpm_str = [NSString stringWithFormat:@"%@" , t_idfa];
|
||||
NSString *ecpm_str = [NSString stringWithFormat:@"%@", t_idfa];
|
||||
|
||||
NSNumber *ecpm = @0.0;
|
||||
|
||||
if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) {
|
||||
ecpm = [XSHelper str2num: ecpm_str];
|
||||
ecpm = [XSHelper str2num:ecpm_str];
|
||||
}
|
||||
return ecpm;
|
||||
|
||||
}
|
||||
|
||||
- (int) getInt: (NSString *) key dic: (NSDictionary*) dic {
|
||||
- (int)getInt:(NSString *)key dic:(NSDictionary*)dic {
|
||||
NSLog(@"getInt: %@", key);
|
||||
id t_idfa = dic[key];
|
||||
NSString *ecpm_str = [NSString stringWithFormat:@"%@" , t_idfa];
|
||||
NSString *ecpm_str = [NSString stringWithFormat:@"%@", t_idfa];
|
||||
|
||||
NSNumber *ecpm = @(-99);
|
||||
|
||||
if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) {
|
||||
ecpm = [XSHelper str2num: ecpm_str];
|
||||
ecpm = [XSHelper str2num:ecpm_str];
|
||||
}
|
||||
return [ecpm intValue];
|
||||
}
|
||||
@ -592,4 +597,3 @@ BOOL myadTaskManualStop = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -9,16 +9,18 @@
|
||||
#define FALLBACK_PORT_START 6001
|
||||
#define FALLBACK_PORT_END 7000
|
||||
#define PORT 6001
|
||||
#define SEND_TIMEOUT 5.0 // 发送超时时间
|
||||
#define SEND_TIMEOUT 5.0
|
||||
|
||||
@interface XUDPServer() {
|
||||
@private
|
||||
GCDAsyncUdpSocket *serverSocket;
|
||||
dispatch_queue_t serverQueue;
|
||||
dispatch_source_t restartTimer;
|
||||
dispatch_source_t healthCheckTimer; // ⭐️ 改用dispatch_source
|
||||
NSUInteger restartAttempts;
|
||||
uint16_t currentPort;
|
||||
NSTimer *healthCheckTimer;
|
||||
BOOL isStarting;
|
||||
BOOL isStopping;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSDictionary *> *pendingSends;
|
||||
@ -34,24 +36,28 @@
|
||||
static XUDPServer* _sharedInstance = nil;
|
||||
static dispatch_once_t oncePredicate;
|
||||
dispatch_once(&oncePredicate, ^{
|
||||
_sharedInstance = [[XUDPServer alloc] init];
|
||||
_sharedInstance = [[super allocWithZone:NULL] init];
|
||||
});
|
||||
return _sharedInstance;
|
||||
}
|
||||
|
||||
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||
return [XUDPServer sharedInstance];
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
restartAttempts = 0;
|
||||
currentPort = PORT;
|
||||
_currentTag = 0;
|
||||
isStarting = NO;
|
||||
isStopping = NO;
|
||||
_pendingSends = [NSMutableDictionary dictionary];
|
||||
|
||||
// 创建串行队列,避免并发问题
|
||||
serverQueue = dispatch_queue_create("com.xudpserver.queue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
return self;
|
||||
}
|
||||
return nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
@ -61,42 +67,71 @@
|
||||
}
|
||||
|
||||
- (uint16_t)udp_port {
|
||||
return currentPort;
|
||||
__block uint16_t port = 0;
|
||||
// ⭐️ 避免死锁,使用异步读取
|
||||
if (dispatch_get_specific((__bridge const void *)serverQueue)) {
|
||||
// 已在serverQueue中
|
||||
port = currentPort;
|
||||
} else {
|
||||
dispatch_sync(serverQueue, ^{
|
||||
port = self->currentPort;
|
||||
});
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
- (void)_startInternal {
|
||||
NSLog(@"XS- Starting UDP server on port %d", currentPort);
|
||||
if (isStarting) {
|
||||
NSLog(@"⚠️ Server is already starting");
|
||||
return;
|
||||
}
|
||||
|
||||
// 避免重复创建
|
||||
if (serverSocket && !serverSocket.isClosed) {
|
||||
NSLog(@"⚠️ UDP server already running on port %d", currentPort);
|
||||
return;
|
||||
}
|
||||
|
||||
[self _stopInternal];
|
||||
isStarting = YES;
|
||||
|
||||
NSLog(@"XS- Starting UDP server on port %d (PID: %d)", currentPort, getpid());
|
||||
|
||||
[self _forceCloseSocket];
|
||||
|
||||
// ⭐️ 使用dispatch_after代替usleep,避免阻塞队列
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
|
||||
serverQueue, ^{
|
||||
[self _startInternalContinue];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_startInternalContinue {
|
||||
serverSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
|
||||
delegateQueue:serverQueue];
|
||||
|
||||
NSError *error = nil;
|
||||
|
||||
// ⭐️ 启用端口重用
|
||||
// ⭐️ 只使用IPv4
|
||||
[serverSocket setIPv4Enabled:YES];
|
||||
[serverSocket setIPv6Enabled:NO];
|
||||
|
||||
if (![serverSocket enableReusePort:YES error:&error]) {
|
||||
NSLog(@"❌ Error enabling reuse port: %@", error);
|
||||
}
|
||||
|
||||
// ⭐️ 设置所有必要的socket选项
|
||||
[self _configureSocketOptions];
|
||||
|
||||
// 尝试绑定到指定端口
|
||||
if (![serverSocket bindToPort:currentPort error:&error]) {
|
||||
NSLog(@"❌ Error binding to port %d: %@", currentPort, error);
|
||||
|
||||
[serverSocket close];
|
||||
serverSocket = nil;
|
||||
[self _forceCloseSocket];
|
||||
isStarting = NO;
|
||||
|
||||
if (error.code == 48) { // EADDRINUSE
|
||||
NSLog(@"⚠️ Port %d is in use (possibly TIME_WAIT)", currentPort);
|
||||
NSLog(@"⚠️ Port %d is in use", currentPort);
|
||||
|
||||
// ⭐️ 异步清理,避免阻塞
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
[self _cleanupZombieSockets];
|
||||
});
|
||||
|
||||
[self _tryFallbackPorts];
|
||||
return;
|
||||
}
|
||||
@ -105,75 +140,137 @@
|
||||
return;
|
||||
}
|
||||
|
||||
[self _configureSocketOptions];
|
||||
|
||||
if (![serverSocket beginReceiving:&error]) {
|
||||
NSLog(@"❌ Error starting server (recv): %@", error);
|
||||
|
||||
[serverSocket close];
|
||||
serverSocket = nil;
|
||||
[self _forceCloseSocket];
|
||||
isStarting = NO;
|
||||
|
||||
[self _scheduleRestartWithBackoff];
|
||||
return;
|
||||
}
|
||||
|
||||
isStarting = NO;
|
||||
restartAttempts = 0;
|
||||
|
||||
[self _logSocketInfo];
|
||||
|
||||
NSLog(@"✅ UDP server started successfully on port %d", currentPort);
|
||||
|
||||
// ⭐️ 启动健康检查
|
||||
[self _startHealthCheck];
|
||||
}
|
||||
|
||||
// ⭐️ 配置所有socket选项
|
||||
- (void)_forceCloseSocket {
|
||||
if (serverSocket) {
|
||||
int fd = [serverSocket socketFD];
|
||||
|
||||
if (fd != -1) {
|
||||
// ⭐️ 非阻塞方式关闭
|
||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
struct linger lingerOption = {1, 0};
|
||||
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lingerOption, sizeof(lingerOption));
|
||||
|
||||
// ⭐️ shutdown可能阻塞,使用dispatch_async
|
||||
int fdCopy = fd;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
shutdown(fdCopy, SHUT_RDWR);
|
||||
});
|
||||
}
|
||||
|
||||
[serverSocket close];
|
||||
serverSocket = nil;
|
||||
|
||||
NSLog(@"🔒 Socket closed (fd: %d)", fd);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_cleanupZombieSockets {
|
||||
NSLog(@"🧹 Cleaning up zombie sockets on port %d", currentPort);
|
||||
|
||||
int testSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (testSocket < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐️ 设置非阻塞
|
||||
fcntl(testSocket, F_SETFL, fcntl(testSocket, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
int reuseAddr = 1;
|
||||
setsockopt(testSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||
|
||||
struct linger lingerOption = {1, 0};
|
||||
setsockopt(testSocket, SOL_SOCKET, SO_LINGER, &lingerOption, sizeof(lingerOption));
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(currentPort);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
if (bind(testSocket, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
|
||||
shutdown(testSocket, SHUT_RDWR);
|
||||
NSLog(@"✅ Cleaned up zombie socket on port %d", currentPort);
|
||||
}
|
||||
|
||||
close(testSocket);
|
||||
}
|
||||
|
||||
- (void)_logSocketInfo {
|
||||
if (!serverSocket) return;
|
||||
|
||||
int fd = [serverSocket socketFD];
|
||||
if (fd == -1) return;
|
||||
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrLen = sizeof(addr);
|
||||
|
||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrLen) == 0) {
|
||||
char ipStr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &addr.sin_addr, ipStr, sizeof(ipStr));
|
||||
|
||||
NSLog(@"📊 Socket - FD: %d, Addr: %s:%d, PID: %d",
|
||||
fd, ipStr, ntohs(addr.sin_port), getpid());
|
||||
}
|
||||
|
||||
int recvBuf, sendBuf;
|
||||
socklen_t optLen = sizeof(int);
|
||||
getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvBuf, &optLen);
|
||||
getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendBuf, &optLen);
|
||||
|
||||
NSLog(@"📊 Buffers - Recv: %d, Send: %d", recvBuf, sendBuf);
|
||||
}
|
||||
|
||||
- (void)_configureSocketOptions {
|
||||
if (!serverSocket) return;
|
||||
|
||||
int fd = [serverSocket socketFD];
|
||||
if (fd == -1) {
|
||||
NSLog(@"⚠️ Invalid socket file descriptor");
|
||||
return;
|
||||
}
|
||||
if (fd == -1) return;
|
||||
|
||||
// 1. 设置 SO_REUSEADDR - 允许快速重启,避免TIME_WAIT问题
|
||||
// SO_REUSEADDR
|
||||
int reuseAddr = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1) {
|
||||
NSLog(@"❌ Error setting SO_REUSEADDR: %s", strerror(errno));
|
||||
}
|
||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||
|
||||
// 2. 设置 SO_REUSEPORT - 允许多个socket绑定同一端口(某些系统)
|
||||
int reusePort = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reusePort, sizeof(reusePort)) == -1) {
|
||||
NSLog(@"⚠️ SO_REUSEPORT not supported or error: %s", strerror(errno));
|
||||
}
|
||||
// 缓冲区
|
||||
int recvBufferSize = 256 * 1024;
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, sizeof(recvBufferSize));
|
||||
|
||||
// 3. ⭐️ 增加接收缓冲区大小,避免缓冲区溢出
|
||||
int recvBufferSize = 256 * 1024; // 256KB
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, sizeof(recvBufferSize)) == -1) {
|
||||
NSLog(@"⚠️ Failed to set receive buffer size: %s", strerror(errno));
|
||||
} else {
|
||||
// 验证实际设置的大小
|
||||
socklen_t optlen = sizeof(recvBufferSize);
|
||||
getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, &optlen);
|
||||
NSLog(@"✅ Receive buffer size set to: %d bytes", recvBufferSize);
|
||||
}
|
||||
int sendBufferSize = 256 * 1024;
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, sizeof(sendBufferSize));
|
||||
|
||||
// 4. ⭐️ 增加发送缓冲区大小
|
||||
int sendBufferSize = 256 * 1024; // 256KB
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, sizeof(sendBufferSize)) == -1) {
|
||||
NSLog(@"⚠️ Failed to set send buffer size: %s", strerror(errno));
|
||||
} else {
|
||||
socklen_t optlen = sizeof(sendBufferSize);
|
||||
getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, &optlen);
|
||||
NSLog(@"✅ Send buffer size set to: %d bytes", sendBufferSize);
|
||||
}
|
||||
|
||||
// 5. ⭐️ 设置 SO_NOSIGPIPE - 防止写入关闭的socket时产生SIGPIPE信号
|
||||
// SO_NOSIGPIPE
|
||||
#ifdef SO_NOSIGPIPE
|
||||
int noSigpipe = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &noSigpipe, sizeof(noSigpipe)) == -1) {
|
||||
NSLog(@"⚠️ Failed to set SO_NOSIGPIPE: %s", strerror(errno));
|
||||
}
|
||||
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &noSigpipe, sizeof(noSigpipe));
|
||||
#endif
|
||||
|
||||
// 6. ⭐️ 设置非阻塞模式(GCDAsyncUdpSocket通常已设置,但确保一下)
|
||||
// SO_LINGER
|
||||
struct linger lingerOption = {1, 0};
|
||||
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lingerOption, sizeof(lingerOption));
|
||||
|
||||
// 非阻塞
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags != -1) {
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
@ -187,84 +284,126 @@
|
||||
}
|
||||
|
||||
- (void)_stopInternal {
|
||||
if (isStopping) {
|
||||
NSLog(@"⚠️ Server is already stopping");
|
||||
return;
|
||||
}
|
||||
|
||||
isStopping = YES;
|
||||
|
||||
NSLog(@"XS- Stopping UDP server on port %d", currentPort);
|
||||
|
||||
[self _cancelRestartTimer];
|
||||
[self _stopHealthCheck];
|
||||
|
||||
if (serverSocket) {
|
||||
// ⭐️ 设置SO_LINGER为0,强制立即关闭,避免TIME_WAIT
|
||||
int fd = [serverSocket socketFD];
|
||||
if (fd != -1) {
|
||||
struct linger lingerOption = {1, 0}; // l_onoff=1, l_linger=0
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lingerOption, sizeof(lingerOption)) == -1) {
|
||||
NSLog(@"⚠️ Failed to set SO_LINGER: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (!serverSocket.isClosed) {
|
||||
[serverSocket close];
|
||||
}
|
||||
|
||||
serverSocket = nil;
|
||||
}
|
||||
[self _forceCloseSocket];
|
||||
|
||||
[_pendingSends removeAllObjects];
|
||||
|
||||
// ⭐️ 使用dispatch_after代替usleep
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
|
||||
serverQueue, ^{
|
||||
self->isStopping = NO;
|
||||
NSLog(@"✅ UDP server stopped");
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Health Check
|
||||
|
||||
// ⭐️ 健康检查
|
||||
- (void)_startHealthCheck {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->healthCheckTimer) {
|
||||
[self->healthCheckTimer invalidate];
|
||||
}
|
||||
// ⭐️ 使用dispatch_source代替NSTimer,避免主线程依赖
|
||||
if (healthCheckTimer) {
|
||||
dispatch_source_cancel(healthCheckTimer);
|
||||
healthCheckTimer = nil;
|
||||
}
|
||||
|
||||
self->healthCheckTimer = [NSTimer scheduledTimerWithTimeInterval:30.0
|
||||
target:self
|
||||
selector:@selector(_performHealthCheck)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
healthCheckTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, serverQueue);
|
||||
|
||||
dispatch_source_set_timer(healthCheckTimer,
|
||||
dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC),
|
||||
30 * NSEC_PER_SEC,
|
||||
1 * NSEC_PER_SEC);
|
||||
|
||||
dispatch_source_set_event_handler(healthCheckTimer, ^{
|
||||
[self _performHealthCheck];
|
||||
});
|
||||
|
||||
dispatch_resume(healthCheckTimer);
|
||||
}
|
||||
|
||||
- (void)_stopHealthCheck {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->healthCheckTimer) {
|
||||
[self->healthCheckTimer invalidate];
|
||||
self->healthCheckTimer = nil;
|
||||
}
|
||||
});
|
||||
if (healthCheckTimer) {
|
||||
dispatch_source_cancel(healthCheckTimer);
|
||||
healthCheckTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_performHealthCheck {
|
||||
dispatch_async(serverQueue, ^{
|
||||
if (!self->serverSocket || self->serverSocket.isClosed) {
|
||||
NSLog(@"⚠️ Health check failed: socket is closed");
|
||||
[self _startInternal];
|
||||
return;
|
||||
}
|
||||
if (isStarting || isStopping) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐️ 检查socket状态
|
||||
int fd = [self->serverSocket socketFD];
|
||||
if (fd == -1) {
|
||||
NSLog(@"⚠️ Health check failed: invalid socket");
|
||||
[self _startInternal];
|
||||
return;
|
||||
}
|
||||
if (!serverSocket || serverSocket.isClosed) {
|
||||
NSLog(@"⚠️ Health check failed: socket is closed");
|
||||
[self _startInternal];
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐️ 检查端口是否仍然绑定
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrLen = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrLen) == -1) {
|
||||
NSLog(@"⚠️ Health check failed: socket not bound");
|
||||
[self _startInternal];
|
||||
return;
|
||||
}
|
||||
int fd = [serverSocket socketFD];
|
||||
if (fd == -1) {
|
||||
NSLog(@"⚠️ Health check failed: invalid socket");
|
||||
[self _startInternal];
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"✅ Health check passed for port %d", self->currentPort);
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrLen = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrLen) == -1) {
|
||||
NSLog(@"⚠️ Health check failed: socket not bound");
|
||||
[self _startInternal];
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐️ 异步检查重复socket,避免阻塞
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
|
||||
[self _checkDuplicateSockets];
|
||||
});
|
||||
|
||||
NSLog(@"✅ Health check passed (Port: %d, FD: %d)", currentPort, fd);
|
||||
}
|
||||
|
||||
- (void)_checkDuplicateSockets {
|
||||
// ⭐️ 设置超时,防止popen阻塞过久
|
||||
NSString *command = [NSString stringWithFormat:@"timeout 2 lsof -i UDP:%d -n -P 2>/dev/null || echo timeout", currentPort];
|
||||
|
||||
FILE *pipe = popen([command UTF8String], "r");
|
||||
if (!pipe) {
|
||||
return;
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
int count = 0;
|
||||
BOOL timedOut = NO;
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
|
||||
if (strstr(buffer, "timeout") != NULL) {
|
||||
timedOut = YES;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
pclose(pipe);
|
||||
|
||||
if (timedOut) {
|
||||
NSLog(@"⚠️ Socket check timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
count = MAX(0, count - 1);
|
||||
|
||||
if (count > 1) {
|
||||
NSLog(@"⚠️ WARNING: Found %d sockets on port %d!", count, currentPort);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Restart Logic
|
||||
@ -281,17 +420,14 @@
|
||||
|
||||
const NSUInteger maxAttempts = 10;
|
||||
if (restartAttempts >= maxAttempts) {
|
||||
NSLog(@"❌ Maximum restart attempts (%lu) reached, giving up",
|
||||
(unsigned long)maxAttempts);
|
||||
NSLog(@"❌ Maximum restart attempts reached");
|
||||
return;
|
||||
}
|
||||
|
||||
restartAttempts++;
|
||||
|
||||
NSTimeInterval delay = MIN(pow(2, restartAttempts - 1), 60.0);
|
||||
|
||||
NSLog(@"⏰ Scheduling restart attempt %lu in %.1f seconds",
|
||||
(unsigned long)restartAttempts, delay);
|
||||
NSLog(@"⏰ Scheduling restart in %.1f seconds", delay);
|
||||
|
||||
restartTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, serverQueue);
|
||||
|
||||
@ -308,10 +444,12 @@
|
||||
}
|
||||
|
||||
- (void)_tryFallbackPorts {
|
||||
NSLog(@"🔍 Searching for available fallback port...");
|
||||
NSLog(@"🔍 Searching for fallback port...");
|
||||
|
||||
for (uint16_t port = FALLBACK_PORT_START; port <= FALLBACK_PORT_END; port++) {
|
||||
// ⭐️ 检查端口是否真的可用
|
||||
// ⭐️ 限制扫描范围,避免卡死
|
||||
uint16_t scanLimit = MIN(FALLBACK_PORT_END, FALLBACK_PORT_START + 100);
|
||||
|
||||
for (uint16_t port = FALLBACK_PORT_START; port <= scanLimit; port++) {
|
||||
if ([self _isPortAvailable:port]) {
|
||||
currentPort = port;
|
||||
NSLog(@"✅ Found available port: %d", port);
|
||||
@ -320,20 +458,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"❌ No available fallback ports found in range %d-%d",
|
||||
FALLBACK_PORT_START, FALLBACK_PORT_END);
|
||||
NSLog(@"❌ No available fallback ports");
|
||||
[self _scheduleRestartWithBackoff];
|
||||
}
|
||||
|
||||
// ⭐️ 检查端口是否可用(改进版)
|
||||
- (BOOL)_isPortAvailable:(uint16_t)port {
|
||||
int testSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (testSocket < 0) {
|
||||
NSLog(@"⚠️ Cannot create test socket");
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 设置SO_REUSEADDR
|
||||
// ⭐️ 设置非阻塞和超时
|
||||
fcntl(testSocket, F_SETFL, fcntl(testSocket, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
struct timeval timeout = {1, 0}; // 1秒超时
|
||||
setsockopt(testSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
setsockopt(testSocket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
|
||||
|
||||
int reuseAddr = 1;
|
||||
setsockopt(testSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||
|
||||
@ -346,14 +487,7 @@
|
||||
int result = bind(testSocket, (struct sockaddr *)&addr, sizeof(addr));
|
||||
close(testSocket);
|
||||
|
||||
if (result == 0) {
|
||||
return YES;
|
||||
} else {
|
||||
if (errno == EADDRINUSE) {
|
||||
NSLog(@"⚠️ Port %d is in use", port);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return (result == 0);
|
||||
}
|
||||
|
||||
- (void)scheduleRestart {
|
||||
@ -366,7 +500,7 @@
|
||||
#pragma mark - GCDAsyncUdpSocket Delegate
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
|
||||
NSLog(@"✅ Connected to client");
|
||||
NSLog(@"✅ Connected");
|
||||
}
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
||||
@ -374,75 +508,57 @@
|
||||
fromAddress:(NSData *)address
|
||||
withFilterContext:(id)filterContext {
|
||||
@autoreleasepool {
|
||||
// ⭐️ 检查数据大小,防止超大包
|
||||
if (data.length > 65507) { // UDP最大包大小
|
||||
NSLog(@"⚠️ Received oversized packet: %lu bytes", (unsigned long)data.length);
|
||||
if (data.length > 65507) {
|
||||
NSLog(@"⚠️ Oversized packet: %lu bytes", (unsigned long)data.length);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *datastr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (!datastr) {
|
||||
NSLog(@"⚠️ Failed to decode received data (length: %lu)", (unsigned long)data.length);
|
||||
NSLog(@"⚠️ Failed to decode data");
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"📨 UDP Request from %@: %@",
|
||||
[self _addressToString:address],
|
||||
[datastr substringToIndex:MIN(100, datastr.length)]);
|
||||
|
||||
// ⭐️ 异步处理请求,避免阻塞接收
|
||||
// ⭐️ 异步处理,避免阻塞接收
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
UDPHandler *handle = [UDPHandler sharedInstance];
|
||||
NSString *res = [handle handle:datastr];
|
||||
@autoreleasepool {
|
||||
UDPHandler *handle = [UDPHandler sharedInstance];
|
||||
NSString *res = [handle handle:datastr];
|
||||
|
||||
if (res) {
|
||||
[self _sendResponse:res toAddress:address fromSocket:sock];
|
||||
if (res) {
|
||||
[self _sendResponse:res toAddress:address fromSocket:sock];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ⭐️ 发送响应(带超时和重试逻辑)
|
||||
- (void)_sendResponse:(NSString *)response
|
||||
toAddress:(NSData *)address
|
||||
fromSocket:(GCDAsyncUdpSocket *)sock {
|
||||
dispatch_async(serverQueue, ^{
|
||||
if (!sock || sock.isClosed) {
|
||||
NSLog(@"⚠️ Cannot send response: socket is closed");
|
||||
return;
|
||||
}
|
||||
|
||||
NSData *responseData = [response dataUsingEncoding:NSUTF8StringEncoding];
|
||||
if (!responseData) {
|
||||
NSLog(@"⚠️ Failed to encode response");
|
||||
return;
|
||||
}
|
||||
|
||||
// ⭐️ 检查响应大小
|
||||
if (responseData.length > 65507) {
|
||||
NSLog(@"⚠️ Response too large: %lu bytes (max 65507)",
|
||||
(unsigned long)responseData.length);
|
||||
if (!responseData || responseData.length > 65507) {
|
||||
return;
|
||||
}
|
||||
|
||||
long tag = ++self->_currentTag;
|
||||
|
||||
// 保存待发送数据
|
||||
self->_pendingSends[@(tag)] = @{
|
||||
@"response": response,
|
||||
@"address": address,
|
||||
@"timestamp": @([[NSDate date] timeIntervalSince1970])
|
||||
};
|
||||
|
||||
NSLog(@"📤 Sending response (tag: %ld, size: %lu bytes)",
|
||||
tag, (unsigned long)responseData.length);
|
||||
|
||||
[sock sendData:responseData
|
||||
toAddress:address
|
||||
withTimeout:SEND_TIMEOUT
|
||||
tag:tag];
|
||||
|
||||
// ⭐️ 设置超时检查
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((SEND_TIMEOUT + 1.0) * NSEC_PER_SEC)),
|
||||
self->serverQueue, ^{
|
||||
[self _checkSendTimeout:tag];
|
||||
@ -450,14 +566,9 @@ withFilterContext:(id)filterContext {
|
||||
});
|
||||
}
|
||||
|
||||
// ⭐️ 检查发送超时
|
||||
- (void)_checkSendTimeout:(long)tag {
|
||||
NSDictionary *pendingData = _pendingSends[@(tag)];
|
||||
if (pendingData) {
|
||||
NSTimeInterval timestamp = [pendingData[@"timestamp"] doubleValue];
|
||||
NSTimeInterval elapsed = [[NSDate date] timeIntervalSince1970] - timestamp;
|
||||
|
||||
NSLog(@"⏱️ Send timeout for tag %ld (elapsed: %.1fs)", tag, elapsed);
|
||||
[_pendingSends removeObjectForKey:@(tag)];
|
||||
}
|
||||
}
|
||||
@ -467,47 +578,36 @@ withFilterContext:(id)filterContext {
|
||||
}
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
|
||||
NSLog(@"✅ Data sent successfully (tag: %ld)", tag);
|
||||
[_pendingSends removeObjectForKey:@(tag)];
|
||||
}
|
||||
|
||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
||||
didNotSendDataWithTag:(long)tag
|
||||
dueToError:(NSError *)error {
|
||||
NSLog(@"❌ Failed to send data (tag: %ld): %@", tag, error);
|
||||
|
||||
[_pendingSends removeObjectForKey:@(tag)];
|
||||
|
||||
// ⭐️ 处理各种发送错误
|
||||
if (error.code == 55) { // ENOBUFS
|
||||
NSLog(@"⚠️ Buffer full (ENOBUFS) - system may be overloaded");
|
||||
} else if (error.code == 57) { // ENOTCONN
|
||||
NSLog(@"⚠️ Socket disconnected (ENOTCONN)");
|
||||
if (error.code == 57) { // ENOTCONN
|
||||
[self _startInternal];
|
||||
} else if (error.code == 64) { // EHOSTDOWN
|
||||
NSLog(@"⚠️ Host is down (EHOSTDOWN)");
|
||||
} else if (error.code == 65) { // EHOSTUNREACH
|
||||
NSLog(@"⚠️ Host unreachable (EHOSTUNREACH)");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error {
|
||||
NSLog(@"⚠️ Socket closed. Error: %@", error);
|
||||
NSLog(@"⚠️ Socket closed: %@", error);
|
||||
|
||||
if (sock == serverSocket) {
|
||||
serverSocket = nil;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
NSLog(@"❌ Unexpected closure, scheduling restart");
|
||||
if (error && !isStopping) {
|
||||
[self scheduleRestart];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Utility Methods
|
||||
|
||||
// ⭐️ 将地址转换为可读字符串
|
||||
- (NSString *)_addressToString:(NSData *)addressData {
|
||||
if (addressData.length < sizeof(struct sockaddr_in)) {
|
||||
return @"unknown";
|
||||
}
|
||||
|
||||
struct sockaddr_in *addr = (struct sockaddr_in *)addressData.bytes;
|
||||
char ipStr[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &addr->sin_addr, ipStr, sizeof(ipStr));
|
||||
|
||||
BIN
packages/251105-nt.deb
Normal file
BIN
packages/251105-nt.deb
Normal file
Binary file not shown.
BIN
packages/251106-nt.deb
Normal file
BIN
packages/251106-nt.deb
Normal file
Binary file not shown.
BIN
packages/251106a-nt.deb
Normal file
BIN
packages/251106a-nt.deb
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user