Compare commits
3 Commits
log
...
lux-main-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bebdfa0794 | ||
|
|
b2cfb485af | ||
|
|
1fb432a784 |
@ -6,5 +6,5 @@ Author: XYZShell
|
|||||||
Section: Utilities
|
Section: Utilities
|
||||||
Tag: role::developer
|
Tag: role::developer
|
||||||
Architecture: iphoneos-arm
|
Architecture: iphoneos-arm
|
||||||
Version: 0.0.7-10-62+debug
|
Version: 0.0.7-10-68+debug
|
||||||
Installed-Size: 1604
|
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-68+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
|
68
|
||||||
@ -11,6 +11,7 @@ TWEAK_NAME = AppRunMan
|
|||||||
AppRunMan_FRAMEWORKS = CoreLocation CFNetwork CoreTelephony Security UIKit IOSurface JavaScriptCore network
|
AppRunMan_FRAMEWORKS = CoreLocation CFNetwork CoreTelephony Security UIKit IOSurface JavaScriptCore network
|
||||||
AppRunMan_PRIVATE_FRAMEWORKS = IOKit
|
AppRunMan_PRIVATE_FRAMEWORKS = IOKit
|
||||||
AppRunMan_FILES = Tweak.x $(wildcard server/*.m) $(wildcard server/GCD/*.m)
|
AppRunMan_FILES = Tweak.x $(wildcard server/*.m) $(wildcard server/GCD/*.m)
|
||||||
|
AppRunMan_LDFLAGS = -v
|
||||||
AppRunMan_CFLAGS = -Wno-error -Wno-module-import-in-extern-c -fobjc-arc
|
AppRunMan_CFLAGS = -Wno-error -Wno-module-import-in-extern-c -fobjc-arc
|
||||||
|
|
||||||
include $(THEOS_MAKE_PATH)/tweak.mk
|
include $(THEOS_MAKE_PATH)/tweak.mk
|
||||||
|
|||||||
@ -157,7 +157,7 @@
|
|||||||
self.ip = [[XSPhoneInfo sharedInstance] IPAddress];
|
self.ip = [[XSPhoneInfo sharedInstance] IPAddress];
|
||||||
self.remoteIp = [[XSPhoneInfo sharedInstance] remoteIp];
|
self.remoteIp = [[XSPhoneInfo sharedInstance] remoteIp];
|
||||||
self.diskSize = [[XSPhoneInfo sharedInstance] IPhoneStatus];
|
self.diskSize = [[XSPhoneInfo sharedInstance] IPhoneStatus];
|
||||||
self.message = @"251103-nt";
|
self.message = @"251106-nt";
|
||||||
// 构建请求数据
|
// 构建请求数据
|
||||||
NSDictionary *heartbeatData = [self constructHeartbeatData];
|
NSDictionary *heartbeatData = [self constructHeartbeatData];
|
||||||
if (!heartbeatData) {
|
if (!heartbeatData) {
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
#import "XSPhoneInfo.h"
|
#import "XSPhoneInfo.h"
|
||||||
#import "XSHelper.h"
|
#import "XSHelper.h"
|
||||||
#import "XSHackIos.h"
|
#import "XSHackIos.h"
|
||||||
#import "MyScriptTask.h"
|
|
||||||
#import "MyEventBus.h"
|
#import "MyEventBus.h"
|
||||||
#import "XSHttpHelper.h"
|
#import "XSHttpHelper.h"
|
||||||
#import "MyAdTask2.h"
|
#import "MyAdTask2.h"
|
||||||
|
|||||||
@ -1,10 +1,3 @@
|
|||||||
//
|
|
||||||
// MyAdTask2.m
|
|
||||||
// nochange
|
|
||||||
//
|
|
||||||
// Created by mac on 2024/11/6.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "XUDPServer.h"
|
#include "XUDPServer.h"
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "IPhoneHertbeat.h"
|
#import "IPhoneHertbeat.h"
|
||||||
@ -16,16 +9,13 @@
|
|||||||
#import "MyAdTask2.h"
|
#import "MyAdTask2.h"
|
||||||
#import "MyEventBus.h"
|
#import "MyEventBus.h"
|
||||||
|
|
||||||
|
|
||||||
BOOL myadTaskManualStop = NO;
|
BOOL myadTaskManualStop = NO;
|
||||||
|
|
||||||
|
|
||||||
@interface MyAdTask2Mangger()
|
@interface MyAdTask2Mangger()
|
||||||
{
|
{
|
||||||
@private
|
@private
|
||||||
BOOL running;
|
BOOL running;
|
||||||
// NSTimer *timer;
|
dispatch_source_t _timer;
|
||||||
dispatch_source_t _timer; // 使用GCD timer替代NSTimer
|
|
||||||
NSDate *lastRun;
|
NSDate *lastRun;
|
||||||
NSDate *lastGetCountry;
|
NSDate *lastGetCountry;
|
||||||
NSString *_lastIdfa;
|
NSString *_lastIdfa;
|
||||||
@ -39,7 +29,6 @@ BOOL myadTaskManualStop = NO;
|
|||||||
NSString *dataId;
|
NSString *dataId;
|
||||||
NSString *remoteIp;
|
NSString *remoteIp;
|
||||||
NSString *country;
|
NSString *country;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
@ -49,11 +38,12 @@ BOOL myadTaskManualStop = NO;
|
|||||||
+(instancetype)sharedInstance {
|
+(instancetype)sharedInstance {
|
||||||
static MyAdTask2Mangger* _sharedInstance = nil;
|
static MyAdTask2Mangger* _sharedInstance = nil;
|
||||||
static dispatch_once_t oncePredicate;
|
static dispatch_once_t oncePredicate;
|
||||||
dispatch_once (&oncePredicate, ^{
|
dispatch_once(&oncePredicate, ^{
|
||||||
_sharedInstance = [[MyAdTask2Mangger alloc] init];
|
_sharedInstance = [[MyAdTask2Mangger alloc] init];
|
||||||
});
|
});
|
||||||
return _sharedInstance;
|
return _sharedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(instancetype)init {
|
-(instancetype)init {
|
||||||
if (self = [super init]) {
|
if (self = [super init]) {
|
||||||
self->running = NO;
|
self->running = NO;
|
||||||
@ -70,7 +60,7 @@ BOOL myadTaskManualStop = NO;
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int) onShow: (NSDictionary *)dic {
|
- (int)onShow:(NSDictionary *)dic {
|
||||||
if(dic == nil || [dic isEqual:[NSNull null]] || [dic count] <= 0) {
|
if(dic == nil || [dic isEqual:[NSNull null]] || [dic count] <= 0) {
|
||||||
return 4000;
|
return 4000;
|
||||||
}
|
}
|
||||||
@ -80,35 +70,13 @@ BOOL myadTaskManualStop = NO;
|
|||||||
NSNumber *t_ecpm = [self getNum:@"ecpm" dic:dic];
|
NSNumber *t_ecpm = [self getNum:@"ecpm" dic:dic];
|
||||||
BOOL ad = dic[@"ad"];
|
BOOL ad = dic[@"ad"];
|
||||||
NSString *iphoneId = [[XSPhoneConfig sharedInstance] IPhoneName];
|
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) {
|
if(!ad) {
|
||||||
NSLog(@"XS- no ad show");
|
NSLog(@"XS- no ad show");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int close = 4000;
|
int close = 4000;
|
||||||
//--new
|
|
||||||
AdLoadInfo adloadInfo = getAdLoadInfo(@{
|
AdLoadInfo adloadInfo = getAdLoadInfo(@{
|
||||||
@"idfa": t_idfa ?: @"",
|
@"idfa": t_idfa ?: @"",
|
||||||
@"adId": t_adId ?: @"",
|
@"adId": t_adId ?: @"",
|
||||||
@ -117,11 +85,6 @@ BOOL myadTaskManualStop = NO;
|
|||||||
@"deviceId":iphoneId ?: @""
|
@"deviceId":iphoneId ?: @""
|
||||||
});
|
});
|
||||||
|
|
||||||
// cur.loads = adloadInfo.loads;
|
|
||||||
//cur.adTouchBeforeMs = adloadInfo.adTouchBeforeMs;
|
|
||||||
// cur.adTime= adloadInfo.adTime;
|
|
||||||
|
|
||||||
|
|
||||||
close = [adloadInfo.adTime intValue];
|
close = [adloadInfo.adTime intValue];
|
||||||
int before = [adloadInfo.adTouchBeforeMs intValue];
|
int before = [adloadInfo.adTouchBeforeMs intValue];
|
||||||
if (before >= close) {
|
if (before >= close) {
|
||||||
@ -134,19 +97,24 @@ BOOL myadTaskManualStop = NO;
|
|||||||
self->adAfter = 1000;
|
self->adAfter = 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSInteger random1 = arc4random_uniform(101); // 0-100
|
NSInteger random1 = arc4random_uniform(101);
|
||||||
NSInteger tr = [adloadInfo.touchRate intValue];
|
NSInteger tr = [adloadInfo.touchRate intValue];
|
||||||
if (tr > random1) {
|
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;
|
return close;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) showStatus: (NSString *)data {
|
- (void)showStatus:(NSString *)data {
|
||||||
[[MyEventBus sharedInstance] postEvent:@"UpdateStatus" withObject:data];
|
[[MyEventBus sharedInstance] postEvent:@"UpdateStatus" withObject:data];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setRemoteInfo {
|
- (void)setRemoteInfo {
|
||||||
NSString *url = @"https://ipapi.co/json/";
|
NSString *url = @"https://ipapi.co/json/";
|
||||||
XSHttpHelper *http = [[XSHttpHelper alloc] init];
|
XSHttpHelper *http = [[XSHttpHelper alloc] init];
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
@ -164,8 +132,10 @@ BOOL myadTaskManualStop = NO;
|
|||||||
}
|
}
|
||||||
NSString *isoCode = dic[@"country_code"];
|
NSString *isoCode = dic[@"country_code"];
|
||||||
NSString *ip = dic[@"ip"];
|
NSString *ip = dic[@"ip"];
|
||||||
// callback(ip, isoCode);
|
|
||||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) return;
|
||||||
|
|
||||||
strongSelf->remoteIp = [ip copy];
|
strongSelf->remoteIp = [ip copy];
|
||||||
strongSelf->country = [isoCode copy];
|
strongSelf->country = [isoCode copy];
|
||||||
[XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip];
|
[XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip];
|
||||||
@ -176,8 +146,8 @@ BOOL myadTaskManualStop = NO;
|
|||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) setRemoteInfo1 {
|
- (void)setRemoteInfo1 {
|
||||||
NSString *url = [[XSPhoneConfig sharedInstance] GetRemoteIPURL];
|
NSString *url = [[XSPhoneConfig sharedInstance] GetRemoteIPURL];
|
||||||
[self showStatus:url];
|
[self showStatus:url];
|
||||||
if (!url) {
|
if (!url) {
|
||||||
NSLog(@"!setRemoteInfo url");
|
NSLog(@"!setRemoteInfo url");
|
||||||
@ -207,9 +177,11 @@ BOOL myadTaskManualStop = NO;
|
|||||||
NSString *isoCode = _data[@"isoCode"];
|
NSString *isoCode = _data[@"isoCode"];
|
||||||
NSString *ip = _data[@"ip"];
|
NSString *ip = _data[@"ip"];
|
||||||
NSLog(@"setRemoteInfo:%@", _data);
|
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];
|
strongSelf->country = [isoCode copy];
|
||||||
[XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip];
|
[XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip];
|
||||||
[weakSelf showStatus:[XSPhoneInfo sharedInstance].remoteIp];
|
[weakSelf showStatus:[XSPhoneInfo sharedInstance].remoteIp];
|
||||||
@ -217,13 +189,9 @@ BOOL myadTaskManualStop = NO;
|
|||||||
NSLog(@"!setRemoteInfo err:%@",err);
|
NSLog(@"!setRemoteInfo err:%@",err);
|
||||||
[weakSelf showStatus:[NSString stringWithFormat:@"%@", err.description]];
|
[weakSelf showStatus:[NSString stringWithFormat:@"%@", err.description]];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)onEnd:(NSDictionary *)dic {
|
||||||
- (BOOL) onEnd: (NSDictionary *)dic {
|
|
||||||
NSNumber *maxEcpm = dic[@"max_ecpm"];
|
NSNumber *maxEcpm = dic[@"max_ecpm"];
|
||||||
int sec = self->adAfter / 1000;
|
int sec = self->adAfter / 1000;
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
@ -244,11 +212,10 @@ BOOL myadTaskManualStop = NO;
|
|||||||
[weakSelf startApp];
|
[weakSelf startApp];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) startApp {
|
- (void)startApp {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
if(!self->running){
|
if(!self->running){
|
||||||
return;
|
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;
|
__weak typeof(self) weakSelf = self;
|
||||||
getChangeInfo(self->_lastIdfa, ^(NSDictionary *dic) {
|
getChangeInfo(self->_lastIdfa, ^(NSDictionary *dic) {
|
||||||
dispatch_async(weakSelf.manQueue, ^{
|
dispatch_async(weakSelf.manQueue, ^{
|
||||||
// 耗时操作
|
[weakSelf onChangeInfo:dic];
|
||||||
[weakSelf onChangeInfo: dic];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}, ^(NSError *err) {
|
}, ^(NSError *err) {
|
||||||
NSLog(@"XS- startTask NSError is null, %@", err);
|
NSLog(@"XS- startTask NSError is null, %@", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) start {
|
- (void)start {
|
||||||
self->running = YES;
|
self->running = YES;
|
||||||
myadTaskManualStop = NO;
|
myadTaskManualStop = NO;
|
||||||
[[IPhoneHertbeat sharedInstance] setStatus: @"运行中"];
|
[[IPhoneHertbeat sharedInstance] setStatus:@"运行中"];
|
||||||
[self startApp];
|
[self startApp];
|
||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
// 使用 dispatch_source 创建timer
|
|
||||||
if (!_timer) {
|
if (!_timer) {
|
||||||
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.manQueue);
|
_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);
|
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)];
|
[[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(YES)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) proc {
|
- (void)proc {
|
||||||
if (!self->running) {
|
if (!self->running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -314,20 +273,24 @@ BOOL myadTaskManualStop = NO;
|
|||||||
NSDate* curRunTime = [NSDate date];
|
NSDate* curRunTime = [NSDate date];
|
||||||
NSTimeInterval elapsed = [curRunTime timeIntervalSinceDate:self->lastRun];
|
NSTimeInterval elapsed = [curRunTime timeIntervalSinceDate:self->lastRun];
|
||||||
|
|
||||||
if (elapsed >= 30) {
|
if (elapsed >= 100) {
|
||||||
//[self onStop];
|
|
||||||
[self start];
|
[self start];
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
NSString* appId = [ self frontMostAppId];
|
NSString* appId = [self frontMostAppId];
|
||||||
|
|
||||||
if (![appId isEqual: self->taskAppId]) {
|
if (![appId isEqual:self->taskAppId]) {
|
||||||
[self appKill:appId];
|
[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 {
|
- (NSString *)toggle {
|
||||||
@ -339,7 +302,7 @@ BOOL myadTaskManualStop = NO;
|
|||||||
return [IPhoneHertbeat sharedInstance].status;
|
return [IPhoneHertbeat sharedInstance].status;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) stop {
|
- (void)stop {
|
||||||
myadTaskManualStop = YES;
|
myadTaskManualStop = YES;
|
||||||
self->running = NO;
|
self->running = NO;
|
||||||
[IPhoneHertbeat sharedInstance].status = @"已停止";
|
[IPhoneHertbeat sharedInstance].status = @"已停止";
|
||||||
@ -350,14 +313,12 @@ BOOL myadTaskManualStop = NO;
|
|||||||
[[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(NO)];
|
[[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(NO)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)onChangeInfo:(NSDictionary *)dic {
|
||||||
- (void) onChangeInfo: (NSDictionary *)dic {
|
NSString* t_idfa = [self getStr:@"idfa" dic:dic];
|
||||||
NSString* t_idfa = [self getStr: @"idfa" dic:dic];
|
|
||||||
if (![XSHelper strIsEmpty:t_idfa]) {
|
if (![XSHelper strIsEmpty:t_idfa]) {
|
||||||
self->_lastIdfa = [t_idfa copy];
|
self->_lastIdfa = [t_idfa copy];
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"XS- startTask t_idfa is null");
|
NSLog(@"XS- startTask t_idfa is null");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,13 +331,11 @@ BOOL myadTaskManualStop = NO;
|
|||||||
self->dataId = [_dataId copy];
|
self->dataId = [_dataId copy];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
NSString* t_taskId = [self getStr:@"id" dic:dic];
|
NSString* t_taskId = [self getStr:@"id" dic:dic];
|
||||||
if (![XSHelper strIsEmpty:t_taskId]) {
|
if (![XSHelper strIsEmpty:t_taskId]) {
|
||||||
self->taskId = [t_taskId copy];
|
self->taskId = [t_taskId copy];
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"XS- startTask t_taskId is null");
|
NSLog(@"XS- startTask t_taskId is null");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NSString* t_taskAppId = [self getStr:@"appId" dic:dic];
|
NSString* t_taskAppId = [self getStr:@"appId" dic:dic];
|
||||||
@ -385,7 +344,6 @@ BOOL myadTaskManualStop = NO;
|
|||||||
[IPhoneHertbeat sharedInstance].appId = self->taskAppId;
|
[IPhoneHertbeat sharedInstance].appId = self->taskAppId;
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"XS- startTask t_taskAppId is null");
|
NSLog(@"XS- startTask t_taskAppId is null");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,7 +352,6 @@ BOOL myadTaskManualStop = NO;
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"XS- startTask workerInfo is null");
|
NSLog(@"XS- startTask workerInfo is null");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,29 +360,39 @@ BOOL myadTaskManualStop = NO;
|
|||||||
self->workType = t_workType;
|
self->workType = t_workType;
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"XS- startTask workType is null");
|
NSLog(@"XS- startTask workType is null");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__weak typeof(self) weakSelf = self;
|
||||||
[self restart:^{
|
[self restart:^{
|
||||||
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) return;
|
||||||
|
|
||||||
saveChangeDataFile(dic);
|
saveChangeDataFile(dic);
|
||||||
self->running = YES;
|
strongSelf->running = YES;
|
||||||
sleep(1);
|
|
||||||
[self appRun:self->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];
|
||||||
|
});
|
||||||
}];
|
}];
|
||||||
//sleep(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)appKill:(NSString*)app {
|
||||||
- (void) appKill: (NSString*) app {
|
|
||||||
NSLog(@"XS- appKill");
|
NSLog(@"XS- appKill");
|
||||||
XSKillAppByName(app);
|
XSKillAppByName(app);
|
||||||
}
|
}
|
||||||
- (void) unlock {
|
|
||||||
|
- (void)unlock {
|
||||||
NSLog(@"XS- unlock");
|
NSLog(@"XS- unlock");
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
XSRemoteUnlock();
|
XSRemoteUnlock();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
- (void) resetApp: (NSString*) appId callback:(OnEndCallback) callback {
|
|
||||||
|
- (void)resetApp:(NSString*)appId callback:(OnEndCallback)callback {
|
||||||
NSLog(@"XS- JSApi resetApp");
|
NSLog(@"XS- JSApi resetApp");
|
||||||
NSString *appInfoPath = XSGetAppInfoPath(appId);
|
NSString *appInfoPath = XSGetAppInfoPath(appId);
|
||||||
NSDictionary *appInfo = XSGetAppInfo(appInfoPath);
|
NSDictionary *appInfo = XSGetAppInfo(appInfoPath);
|
||||||
@ -443,19 +410,21 @@ BOOL myadTaskManualStop = NO;
|
|||||||
__weak typeof(self) weakSelf = self;
|
__weak typeof(self) weakSelf = self;
|
||||||
getLowEcpm(^(NSNumber *lowEcpm) {
|
getLowEcpm(^(NSNumber *lowEcpm) {
|
||||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) return;
|
||||||
|
|
||||||
BOOL washParam = strongSelf->workType == 0 ? YES : NO;
|
BOOL washParam = strongSelf->workType == 0 ? YES : NO;
|
||||||
[tempBfaceDictKey setValue:@(washParam) forKey:@"washParam"];
|
[tempBfaceDictKey setValue:@(washParam) forKey:@"washParam"];
|
||||||
[tempBfaceDictKey setValue:lowEcpm forKey:@"adbrush_ecpm"];
|
[tempBfaceDictKey setValue:lowEcpm forKey:@"adbrush_ecpm"];
|
||||||
if(strongSelf->linkId) {
|
if(strongSelf->linkId) {
|
||||||
[tempBfaceDictKey setValue:strongSelf->linkId forKey:@"linkId"];
|
[tempBfaceDictKey setValue:strongSelf->linkId forKey:@"linkId"];
|
||||||
} else {
|
} else {
|
||||||
NSString *linkId = [[NSUUID UUID] UUIDString];
|
NSString *linkId = [[NSUUID UUID] UUIDString];
|
||||||
[tempBfaceDictKey setValue:linkId forKey:@"linkId"];
|
[tempBfaceDictKey setValue:linkId forKey:@"linkId"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strongSelf->dataId) {
|
if(strongSelf->dataId) {
|
||||||
[tempBfaceDictKey setValue:strongSelf->dataId forKey:@"dataId"];
|
[tempBfaceDictKey setValue:strongSelf->dataId forKey:@"dataId"];
|
||||||
} else {
|
} else {
|
||||||
NSString *dataId = @"0";
|
NSString *dataId = @"0";
|
||||||
[tempBfaceDictKey setValue:dataId forKey:@"dataId"];
|
[tempBfaceDictKey setValue:dataId forKey:@"dataId"];
|
||||||
}
|
}
|
||||||
@ -475,10 +444,12 @@ BOOL myadTaskManualStop = NO;
|
|||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
- (NSString *) frontMostAppId {
|
|
||||||
|
- (NSString *)frontMostAppId {
|
||||||
return XSFrontMostAppId();
|
return XSFrontMostAppId();
|
||||||
}
|
}
|
||||||
- (void) appRun:(NSString *)appId {
|
|
||||||
|
- (void)appRun:(NSString *)appId {
|
||||||
if ([XSHelper strIsEmpty:appId]) return;
|
if ([XSHelper strIsEmpty:appId]) return;
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
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;
|
__weak typeof(self) weakSelf = self;
|
||||||
dispatch_async(self.manQueue, ^{
|
dispatch_async(self.manQueue, ^{
|
||||||
@try {
|
@try {
|
||||||
__strong typeof(weakSelf) strongSelf = weakSelf;
|
__strong typeof(weakSelf) strongSelf = weakSelf;
|
||||||
|
if (!strongSelf) return;
|
||||||
|
|
||||||
[weakSelf appKill:@"com.apple.AppStore"];
|
[weakSelf appKill:@"com.apple.AppStore"];
|
||||||
[weakSelf appKill:@"com.apple.mobilesafari"];
|
[weakSelf appKill:@"com.apple.mobilesafari"];
|
||||||
if (strongSelf->taskAppId && ![strongSelf->taskAppId isEqual:[NSNull null]] && [strongSelf->taskAppId length] > 0) {
|
if (strongSelf->taskAppId && ![strongSelf->taskAppId isEqual:[NSNull null]] && [strongSelf->taskAppId length] > 0) {
|
||||||
[weakSelf appKill:strongSelf->taskAppId];
|
[weakSelf appKill:strongSelf->taskAppId];
|
||||||
[weakSelf unlock];
|
[weakSelf unlock];
|
||||||
sleep(1);
|
|
||||||
[weakSelf unlock];
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC),
|
||||||
[weakSelf resetApp:strongSelf->taskAppId callback:callback];
|
strongSelf.manQueue, ^{
|
||||||
|
[weakSelf unlock];
|
||||||
|
[weakSelf resetApp:strongSelf->taskAppId callback:callback];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} @catch (NSException *exception) {
|
} @catch (NSException *exception) {
|
||||||
NSLog(@"Error in restart: %@", exception);
|
NSLog(@"Error in restart: %@", exception);
|
||||||
@ -513,44 +489,73 @@ BOOL myadTaskManualStop = NO;
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)rndTouchApp {
|
||||||
- (void) rndTouchApp {
|
|
||||||
int times = [XSHelper random:2 and:4];
|
int times = [XSHelper random:2 and:4];
|
||||||
for (int i = 1; i <= times; i++) {
|
[self _rndTouchAppRecursive:0 totalTimes:times];
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) touchAppTask: (int) beforeTouch {
|
- (void)_rndTouchAppRecursive:(int)currentIndex totalTimes:(int)times {
|
||||||
if (beforeTouch > 0) {
|
if (currentIndex >= times) {
|
||||||
sleep(beforeTouch);
|
return;
|
||||||
}
|
}
|
||||||
[self rndTouchApp];
|
|
||||||
sleep(2);
|
|
||||||
|
|
||||||
int x = [XSHelper random:40 and:screen_w - 20];
|
int x = [XSHelper random:40 and:screen_w - 20];
|
||||||
int y = [XSHelper random:50 and:screen_h - 240];
|
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]) {
|
__weak typeof(self) weakSelf = self;
|
||||||
// [self appKill:appId];
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.002 * NSEC_PER_SEC)),
|
||||||
sleep(1);
|
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);
|
NSLog(@"getStr: %@", key);
|
||||||
id t_idfa = dic[key];
|
id t_idfa = dic[key];
|
||||||
if (t_idfa && ![t_idfa isEqual:[NSNull null]] && ![t_idfa isEqual:@""]) {
|
if (t_idfa && ![t_idfa isEqual:[NSNull null]] && ![t_idfa isEqual:@""]) {
|
||||||
@ -560,29 +565,29 @@ BOOL myadTaskManualStop = NO;
|
|||||||
return @"";
|
return @"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
- (NSNumber*) getNum: (NSString *) key dic: (NSDictionary*) dic {
|
|
||||||
|
- (NSNumber*)getNum:(NSString *)key dic:(NSDictionary*)dic {
|
||||||
NSLog(@"getNum: %@", key);
|
NSLog(@"getNum: %@", key);
|
||||||
id t_idfa = dic[key];
|
id t_idfa = dic[key];
|
||||||
NSString *ecpm_str = [NSString stringWithFormat:@"%@" , t_idfa];
|
NSString *ecpm_str = [NSString stringWithFormat:@"%@", t_idfa];
|
||||||
|
|
||||||
NSNumber *ecpm = @0.0;
|
NSNumber *ecpm = @0.0;
|
||||||
|
|
||||||
if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) {
|
if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) {
|
||||||
ecpm = [XSHelper str2num: ecpm_str];
|
ecpm = [XSHelper str2num:ecpm_str];
|
||||||
}
|
}
|
||||||
return ecpm;
|
return ecpm;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (int) getInt: (NSString *) key dic: (NSDictionary*) dic {
|
- (int)getInt:(NSString *)key dic:(NSDictionary*)dic {
|
||||||
NSLog(@"getInt: %@", key);
|
NSLog(@"getInt: %@", key);
|
||||||
id t_idfa = dic[key];
|
id t_idfa = dic[key];
|
||||||
NSString *ecpm_str = [NSString stringWithFormat:@"%@" , t_idfa];
|
NSString *ecpm_str = [NSString stringWithFormat:@"%@", t_idfa];
|
||||||
|
|
||||||
NSNumber *ecpm = @(-99);
|
NSNumber *ecpm = @(-99);
|
||||||
|
|
||||||
if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) {
|
if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) {
|
||||||
ecpm = [XSHelper str2num: ecpm_str];
|
ecpm = [XSHelper str2num:ecpm_str];
|
||||||
}
|
}
|
||||||
return [ecpm intValue];
|
return [ecpm intValue];
|
||||||
}
|
}
|
||||||
@ -592,4 +597,3 @@ BOOL myadTaskManualStop = NO;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@ -9,16 +9,18 @@
|
|||||||
#define FALLBACK_PORT_START 6001
|
#define FALLBACK_PORT_START 6001
|
||||||
#define FALLBACK_PORT_END 7000
|
#define FALLBACK_PORT_END 7000
|
||||||
#define PORT 6001
|
#define PORT 6001
|
||||||
#define SEND_TIMEOUT 5.0 // 发送超时时间
|
#define SEND_TIMEOUT 5.0
|
||||||
|
|
||||||
@interface XUDPServer() {
|
@interface XUDPServer() {
|
||||||
@private
|
@private
|
||||||
GCDAsyncUdpSocket *serverSocket;
|
GCDAsyncUdpSocket *serverSocket;
|
||||||
dispatch_queue_t serverQueue;
|
dispatch_queue_t serverQueue;
|
||||||
dispatch_source_t restartTimer;
|
dispatch_source_t restartTimer;
|
||||||
|
dispatch_source_t healthCheckTimer; // ⭐️ 改用dispatch_source
|
||||||
NSUInteger restartAttempts;
|
NSUInteger restartAttempts;
|
||||||
uint16_t currentPort;
|
uint16_t currentPort;
|
||||||
NSTimer *healthCheckTimer;
|
BOOL isStarting;
|
||||||
|
BOOL isStopping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSDictionary *> *pendingSends;
|
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, NSDictionary *> *pendingSends;
|
||||||
@ -34,24 +36,28 @@
|
|||||||
static XUDPServer* _sharedInstance = nil;
|
static XUDPServer* _sharedInstance = nil;
|
||||||
static dispatch_once_t oncePredicate;
|
static dispatch_once_t oncePredicate;
|
||||||
dispatch_once(&oncePredicate, ^{
|
dispatch_once(&oncePredicate, ^{
|
||||||
_sharedInstance = [[XUDPServer alloc] init];
|
_sharedInstance = [[super allocWithZone:NULL] init];
|
||||||
});
|
});
|
||||||
return _sharedInstance;
|
return _sharedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
|
||||||
|
return [XUDPServer sharedInstance];
|
||||||
|
}
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
if (self = [super init]) {
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
restartAttempts = 0;
|
restartAttempts = 0;
|
||||||
currentPort = PORT;
|
currentPort = PORT;
|
||||||
_currentTag = 0;
|
_currentTag = 0;
|
||||||
|
isStarting = NO;
|
||||||
|
isStopping = NO;
|
||||||
_pendingSends = [NSMutableDictionary dictionary];
|
_pendingSends = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
// 创建串行队列,避免并发问题
|
|
||||||
serverQueue = dispatch_queue_create("com.xudpserver.queue", DISPATCH_QUEUE_SERIAL);
|
serverQueue = dispatch_queue_create("com.xudpserver.queue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
return nil;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)start {
|
- (void)start {
|
||||||
@ -61,42 +67,71 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (uint16_t)udp_port {
|
- (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 {
|
- (void)_startInternal {
|
||||||
NSLog(@"XS- Starting UDP server on port %d", currentPort);
|
if (isStarting) {
|
||||||
|
NSLog(@"⚠️ Server is already starting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 避免重复创建
|
|
||||||
if (serverSocket && !serverSocket.isClosed) {
|
if (serverSocket && !serverSocket.isClosed) {
|
||||||
NSLog(@"⚠️ UDP server already running on port %d", currentPort);
|
NSLog(@"⚠️ UDP server already running on port %d", currentPort);
|
||||||
return;
|
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
|
serverSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
|
||||||
delegateQueue:serverQueue];
|
delegateQueue:serverQueue];
|
||||||
|
|
||||||
NSError *error = nil;
|
NSError *error = nil;
|
||||||
|
|
||||||
// ⭐️ 启用端口重用
|
// ⭐️ 只使用IPv4
|
||||||
|
[serverSocket setIPv4Enabled:YES];
|
||||||
|
[serverSocket setIPv6Enabled:NO];
|
||||||
|
|
||||||
if (![serverSocket enableReusePort:YES error:&error]) {
|
if (![serverSocket enableReusePort:YES error:&error]) {
|
||||||
NSLog(@"❌ Error enabling reuse port: %@", error);
|
NSLog(@"❌ Error enabling reuse port: %@", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐️ 设置所有必要的socket选项
|
|
||||||
[self _configureSocketOptions];
|
|
||||||
|
|
||||||
// 尝试绑定到指定端口
|
|
||||||
if (![serverSocket bindToPort:currentPort error:&error]) {
|
if (![serverSocket bindToPort:currentPort error:&error]) {
|
||||||
NSLog(@"❌ Error binding to port %d: %@", currentPort, error);
|
NSLog(@"❌ Error binding to port %d: %@", currentPort, error);
|
||||||
|
|
||||||
[serverSocket close];
|
[self _forceCloseSocket];
|
||||||
serverSocket = nil;
|
isStarting = NO;
|
||||||
|
|
||||||
if (error.code == 48) { // EADDRINUSE
|
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];
|
[self _tryFallbackPorts];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -105,75 +140,137 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[self _configureSocketOptions];
|
||||||
|
|
||||||
if (![serverSocket beginReceiving:&error]) {
|
if (![serverSocket beginReceiving:&error]) {
|
||||||
NSLog(@"❌ Error starting server (recv): %@", error);
|
NSLog(@"❌ Error starting server (recv): %@", error);
|
||||||
|
|
||||||
[serverSocket close];
|
[self _forceCloseSocket];
|
||||||
serverSocket = nil;
|
isStarting = NO;
|
||||||
|
|
||||||
[self _scheduleRestartWithBackoff];
|
[self _scheduleRestartWithBackoff];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isStarting = NO;
|
||||||
restartAttempts = 0;
|
restartAttempts = 0;
|
||||||
|
|
||||||
|
[self _logSocketInfo];
|
||||||
|
|
||||||
NSLog(@"✅ UDP server started successfully on port %d", currentPort);
|
NSLog(@"✅ UDP server started successfully on port %d", currentPort);
|
||||||
|
|
||||||
// ⭐️ 启动健康检查
|
|
||||||
[self _startHealthCheck];
|
[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 {
|
- (void)_configureSocketOptions {
|
||||||
if (!serverSocket) return;
|
if (!serverSocket) return;
|
||||||
|
|
||||||
int fd = [serverSocket socketFD];
|
int fd = [serverSocket socketFD];
|
||||||
if (fd == -1) {
|
if (fd == -1) return;
|
||||||
NSLog(@"⚠️ Invalid socket file descriptor");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. 设置 SO_REUSEADDR - 允许快速重启,避免TIME_WAIT问题
|
// SO_REUSEADDR
|
||||||
int reuseAddr = 1;
|
int reuseAddr = 1;
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1) {
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||||
NSLog(@"❌ Error setting SO_REUSEADDR: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 设置 SO_REUSEPORT - 允许多个socket绑定同一端口(某些系统)
|
// 缓冲区
|
||||||
int reusePort = 1;
|
int recvBufferSize = 256 * 1024;
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reusePort, sizeof(reusePort)) == -1) {
|
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvBufferSize, sizeof(recvBufferSize));
|
||||||
NSLog(@"⚠️ SO_REUSEPORT not supported or error: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. ⭐️ 增加接收缓冲区大小,避免缓冲区溢出
|
int sendBufferSize = 256 * 1024;
|
||||||
int recvBufferSize = 256 * 1024; // 256KB
|
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendBufferSize, sizeof(sendBufferSize));
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. ⭐️ 增加发送缓冲区大小
|
// SO_NOSIGPIPE
|
||||||
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信号
|
|
||||||
#ifdef SO_NOSIGPIPE
|
#ifdef SO_NOSIGPIPE
|
||||||
int noSigpipe = 1;
|
int noSigpipe = 1;
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &noSigpipe, sizeof(noSigpipe)) == -1) {
|
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &noSigpipe, sizeof(noSigpipe));
|
||||||
NSLog(@"⚠️ Failed to set SO_NOSIGPIPE: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
#endif
|
#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);
|
int flags = fcntl(fd, F_GETFL, 0);
|
||||||
if (flags != -1) {
|
if (flags != -1) {
|
||||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
@ -187,84 +284,126 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)_stopInternal {
|
- (void)_stopInternal {
|
||||||
|
if (isStopping) {
|
||||||
|
NSLog(@"⚠️ Server is already stopping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isStopping = YES;
|
||||||
|
|
||||||
NSLog(@"XS- Stopping UDP server on port %d", currentPort);
|
NSLog(@"XS- Stopping UDP server on port %d", currentPort);
|
||||||
|
|
||||||
[self _cancelRestartTimer];
|
[self _cancelRestartTimer];
|
||||||
[self _stopHealthCheck];
|
[self _stopHealthCheck];
|
||||||
|
|
||||||
if (serverSocket) {
|
[self _forceCloseSocket];
|
||||||
// ⭐️ 设置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;
|
|
||||||
}
|
|
||||||
|
|
||||||
[_pendingSends removeAllObjects];
|
[_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
|
#pragma mark - Health Check
|
||||||
|
|
||||||
// ⭐️ 健康检查
|
|
||||||
- (void)_startHealthCheck {
|
- (void)_startHealthCheck {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
// ⭐️ 使用dispatch_source代替NSTimer,避免主线程依赖
|
||||||
if (self->healthCheckTimer) {
|
if (healthCheckTimer) {
|
||||||
[self->healthCheckTimer invalidate];
|
dispatch_source_cancel(healthCheckTimer);
|
||||||
}
|
healthCheckTimer = nil;
|
||||||
|
}
|
||||||
|
|
||||||
self->healthCheckTimer = [NSTimer scheduledTimerWithTimeInterval:30.0
|
healthCheckTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, serverQueue);
|
||||||
target:self
|
|
||||||
selector:@selector(_performHealthCheck)
|
dispatch_source_set_timer(healthCheckTimer,
|
||||||
userInfo:nil
|
dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC),
|
||||||
repeats:YES];
|
30 * NSEC_PER_SEC,
|
||||||
|
1 * NSEC_PER_SEC);
|
||||||
|
|
||||||
|
dispatch_source_set_event_handler(healthCheckTimer, ^{
|
||||||
|
[self _performHealthCheck];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dispatch_resume(healthCheckTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_stopHealthCheck {
|
- (void)_stopHealthCheck {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
if (healthCheckTimer) {
|
||||||
if (self->healthCheckTimer) {
|
dispatch_source_cancel(healthCheckTimer);
|
||||||
[self->healthCheckTimer invalidate];
|
healthCheckTimer = nil;
|
||||||
self->healthCheckTimer = nil;
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)_performHealthCheck {
|
- (void)_performHealthCheck {
|
||||||
dispatch_async(serverQueue, ^{
|
if (isStarting || isStopping) {
|
||||||
if (!self->serverSocket || self->serverSocket.isClosed) {
|
return;
|
||||||
NSLog(@"⚠️ Health check failed: socket is closed");
|
}
|
||||||
[self _startInternal];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⭐️ 检查socket状态
|
if (!serverSocket || serverSocket.isClosed) {
|
||||||
int fd = [self->serverSocket socketFD];
|
NSLog(@"⚠️ Health check failed: socket is closed");
|
||||||
if (fd == -1) {
|
[self _startInternal];
|
||||||
NSLog(@"⚠️ Health check failed: invalid socket");
|
return;
|
||||||
[self _startInternal];
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⭐️ 检查端口是否仍然绑定
|
int fd = [serverSocket socketFD];
|
||||||
struct sockaddr_in addr;
|
if (fd == -1) {
|
||||||
socklen_t addrLen = sizeof(addr);
|
NSLog(@"⚠️ Health check failed: invalid socket");
|
||||||
if (getsockname(fd, (struct sockaddr *)&addr, &addrLen) == -1) {
|
[self _startInternal];
|
||||||
NSLog(@"⚠️ Health check failed: socket not bound");
|
return;
|
||||||
[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
|
#pragma mark - Restart Logic
|
||||||
@ -281,17 +420,14 @@
|
|||||||
|
|
||||||
const NSUInteger maxAttempts = 10;
|
const NSUInteger maxAttempts = 10;
|
||||||
if (restartAttempts >= maxAttempts) {
|
if (restartAttempts >= maxAttempts) {
|
||||||
NSLog(@"❌ Maximum restart attempts (%lu) reached, giving up",
|
NSLog(@"❌ Maximum restart attempts reached");
|
||||||
(unsigned long)maxAttempts);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
restartAttempts++;
|
restartAttempts++;
|
||||||
|
|
||||||
NSTimeInterval delay = MIN(pow(2, restartAttempts - 1), 60.0);
|
NSTimeInterval delay = MIN(pow(2, restartAttempts - 1), 60.0);
|
||||||
|
|
||||||
NSLog(@"⏰ Scheduling restart attempt %lu in %.1f seconds",
|
NSLog(@"⏰ Scheduling restart in %.1f seconds", delay);
|
||||||
(unsigned long)restartAttempts, delay);
|
|
||||||
|
|
||||||
restartTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, serverQueue);
|
restartTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, serverQueue);
|
||||||
|
|
||||||
@ -308,10 +444,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)_tryFallbackPorts {
|
- (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]) {
|
if ([self _isPortAvailable:port]) {
|
||||||
currentPort = port;
|
currentPort = port;
|
||||||
NSLog(@"✅ Found available port: %d", port);
|
NSLog(@"✅ Found available port: %d", port);
|
||||||
@ -320,20 +458,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLog(@"❌ No available fallback ports found in range %d-%d",
|
NSLog(@"❌ No available fallback ports");
|
||||||
FALLBACK_PORT_START, FALLBACK_PORT_END);
|
|
||||||
[self _scheduleRestartWithBackoff];
|
[self _scheduleRestartWithBackoff];
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐️ 检查端口是否可用(改进版)
|
|
||||||
- (BOOL)_isPortAvailable:(uint16_t)port {
|
- (BOOL)_isPortAvailable:(uint16_t)port {
|
||||||
int testSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
int testSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
if (testSocket < 0) {
|
if (testSocket < 0) {
|
||||||
NSLog(@"⚠️ Cannot create test socket");
|
|
||||||
return NO;
|
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;
|
int reuseAddr = 1;
|
||||||
setsockopt(testSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
setsockopt(testSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr));
|
||||||
|
|
||||||
@ -346,14 +487,7 @@
|
|||||||
int result = bind(testSocket, (struct sockaddr *)&addr, sizeof(addr));
|
int result = bind(testSocket, (struct sockaddr *)&addr, sizeof(addr));
|
||||||
close(testSocket);
|
close(testSocket);
|
||||||
|
|
||||||
if (result == 0) {
|
return (result == 0);
|
||||||
return YES;
|
|
||||||
} else {
|
|
||||||
if (errno == EADDRINUSE) {
|
|
||||||
NSLog(@"⚠️ Port %d is in use", port);
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)scheduleRestart {
|
- (void)scheduleRestart {
|
||||||
@ -366,7 +500,7 @@
|
|||||||
#pragma mark - GCDAsyncUdpSocket Delegate
|
#pragma mark - GCDAsyncUdpSocket Delegate
|
||||||
|
|
||||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
|
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
|
||||||
NSLog(@"✅ Connected to client");
|
NSLog(@"✅ Connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
||||||
@ -374,75 +508,57 @@
|
|||||||
fromAddress:(NSData *)address
|
fromAddress:(NSData *)address
|
||||||
withFilterContext:(id)filterContext {
|
withFilterContext:(id)filterContext {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
// ⭐️ 检查数据大小,防止超大包
|
if (data.length > 65507) {
|
||||||
if (data.length > 65507) { // UDP最大包大小
|
NSLog(@"⚠️ Oversized packet: %lu bytes", (unsigned long)data.length);
|
||||||
NSLog(@"⚠️ Received oversized packet: %lu bytes", (unsigned long)data.length);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *datastr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
NSString *datastr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
if (!datastr) {
|
if (!datastr) {
|
||||||
NSLog(@"⚠️ Failed to decode received data (length: %lu)", (unsigned long)data.length);
|
NSLog(@"⚠️ Failed to decode data");
|
||||||
return;
|
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), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
UDPHandler *handle = [UDPHandler sharedInstance];
|
@autoreleasepool {
|
||||||
NSString *res = [handle handle:datastr];
|
UDPHandler *handle = [UDPHandler sharedInstance];
|
||||||
|
NSString *res = [handle handle:datastr];
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
[self _sendResponse:res toAddress:address fromSocket:sock];
|
[self _sendResponse:res toAddress:address fromSocket:sock];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐️ 发送响应(带超时和重试逻辑)
|
|
||||||
- (void)_sendResponse:(NSString *)response
|
- (void)_sendResponse:(NSString *)response
|
||||||
toAddress:(NSData *)address
|
toAddress:(NSData *)address
|
||||||
fromSocket:(GCDAsyncUdpSocket *)sock {
|
fromSocket:(GCDAsyncUdpSocket *)sock {
|
||||||
dispatch_async(serverQueue, ^{
|
dispatch_async(serverQueue, ^{
|
||||||
if (!sock || sock.isClosed) {
|
if (!sock || sock.isClosed) {
|
||||||
NSLog(@"⚠️ Cannot send response: socket is closed");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSData *responseData = [response dataUsingEncoding:NSUTF8StringEncoding];
|
NSData *responseData = [response dataUsingEncoding:NSUTF8StringEncoding];
|
||||||
if (!responseData) {
|
if (!responseData || responseData.length > 65507) {
|
||||||
NSLog(@"⚠️ Failed to encode response");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⭐️ 检查响应大小
|
|
||||||
if (responseData.length > 65507) {
|
|
||||||
NSLog(@"⚠️ Response too large: %lu bytes (max 65507)",
|
|
||||||
(unsigned long)responseData.length);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long tag = ++self->_currentTag;
|
long tag = ++self->_currentTag;
|
||||||
|
|
||||||
// 保存待发送数据
|
|
||||||
self->_pendingSends[@(tag)] = @{
|
self->_pendingSends[@(tag)] = @{
|
||||||
@"response": response,
|
@"response": response,
|
||||||
@"address": address,
|
@"address": address,
|
||||||
@"timestamp": @([[NSDate date] timeIntervalSince1970])
|
@"timestamp": @([[NSDate date] timeIntervalSince1970])
|
||||||
};
|
};
|
||||||
|
|
||||||
NSLog(@"📤 Sending response (tag: %ld, size: %lu bytes)",
|
|
||||||
tag, (unsigned long)responseData.length);
|
|
||||||
|
|
||||||
[sock sendData:responseData
|
[sock sendData:responseData
|
||||||
toAddress:address
|
toAddress:address
|
||||||
withTimeout:SEND_TIMEOUT
|
withTimeout:SEND_TIMEOUT
|
||||||
tag:tag];
|
tag:tag];
|
||||||
|
|
||||||
// ⭐️ 设置超时检查
|
|
||||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((SEND_TIMEOUT + 1.0) * NSEC_PER_SEC)),
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((SEND_TIMEOUT + 1.0) * NSEC_PER_SEC)),
|
||||||
self->serverQueue, ^{
|
self->serverQueue, ^{
|
||||||
[self _checkSendTimeout:tag];
|
[self _checkSendTimeout:tag];
|
||||||
@ -450,14 +566,9 @@ withFilterContext:(id)filterContext {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐️ 检查发送超时
|
|
||||||
- (void)_checkSendTimeout:(long)tag {
|
- (void)_checkSendTimeout:(long)tag {
|
||||||
NSDictionary *pendingData = _pendingSends[@(tag)];
|
NSDictionary *pendingData = _pendingSends[@(tag)];
|
||||||
if (pendingData) {
|
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)];
|
[_pendingSends removeObjectForKey:@(tag)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -467,47 +578,36 @@ withFilterContext:(id)filterContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
|
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
|
||||||
NSLog(@"✅ Data sent successfully (tag: %ld)", tag);
|
|
||||||
[_pendingSends removeObjectForKey:@(tag)];
|
[_pendingSends removeObjectForKey:@(tag)];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
- (void)udpSocket:(GCDAsyncUdpSocket *)sock
|
||||||
didNotSendDataWithTag:(long)tag
|
didNotSendDataWithTag:(long)tag
|
||||||
dueToError:(NSError *)error {
|
dueToError:(NSError *)error {
|
||||||
NSLog(@"❌ Failed to send data (tag: %ld): %@", tag, error);
|
|
||||||
|
|
||||||
[_pendingSends removeObjectForKey:@(tag)];
|
[_pendingSends removeObjectForKey:@(tag)];
|
||||||
|
|
||||||
// ⭐️ 处理各种发送错误
|
if (error.code == 57) { // ENOTCONN
|
||||||
if (error.code == 55) { // ENOBUFS
|
|
||||||
NSLog(@"⚠️ Buffer full (ENOBUFS) - system may be overloaded");
|
|
||||||
} else if (error.code == 57) { // ENOTCONN
|
|
||||||
NSLog(@"⚠️ Socket disconnected (ENOTCONN)");
|
|
||||||
[self _startInternal];
|
[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 {
|
- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error {
|
||||||
NSLog(@"⚠️ Socket closed. Error: %@", error);
|
NSLog(@"⚠️ Socket closed: %@", error);
|
||||||
|
|
||||||
if (sock == serverSocket) {
|
if (sock == serverSocket) {
|
||||||
serverSocket = nil;
|
serverSocket = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error && !isStopping) {
|
||||||
NSLog(@"❌ Unexpected closure, scheduling restart");
|
|
||||||
[self scheduleRestart];
|
[self scheduleRestart];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Utility Methods
|
|
||||||
|
|
||||||
// ⭐️ 将地址转换为可读字符串
|
|
||||||
- (NSString *)_addressToString:(NSData *)addressData {
|
- (NSString *)_addressToString:(NSData *)addressData {
|
||||||
|
if (addressData.length < sizeof(struct sockaddr_in)) {
|
||||||
|
return @"unknown";
|
||||||
|
}
|
||||||
|
|
||||||
struct sockaddr_in *addr = (struct sockaddr_in *)addressData.bytes;
|
struct sockaddr_in *addr = (struct sockaddr_in *)addressData.bytes;
|
||||||
char ipStr[INET_ADDRSTRLEN];
|
char ipStr[INET_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, &addr->sin_addr, ipStr, sizeof(ipStr));
|
inet_ntop(AF_INET, &addr->sin_addr, ipStr, sizeof(ipStr));
|
||||||
|
|||||||
13
ips.txt
13
ips.txt
@ -1,3 +1,10 @@
|
|||||||
172.28.4.29
|
192.168.1.15
|
||||||
172.28.7.13
|
192.168.1.19
|
||||||
172.28.7.22
|
192.168.1.18
|
||||||
|
192.168.1.20
|
||||||
|
192.168.1.11
|
||||||
|
192.168.1.21
|
||||||
|
192.168.1.14
|
||||||
|
192.168.1.16
|
||||||
|
192.168.1.13
|
||||||
|
192.168.1.17
|
||||||
BIN
packages/20251109.deb
Normal file
BIN
packages/20251109.deb
Normal file
Binary file not shown.
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.
BIN
packages/251106b-nt.deb
Normal file
BIN
packages/251106b-nt.deb
Normal file
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user