ios-hooks/AppRunMan/server/XUDPServer.m
2025-11-04 10:06:44 +08:00

231 lines
5.7 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// XUDPServer.m
// xcmd
//
// Created by mac on 2025/2/17.
//
#import <Foundation/Foundation.h>
#import "XUDPServer.h"
#import "UDPHandler.h"
#define PORT 6001
@interface XUDPServer() {
@private
GCDAsyncUdpSocket *serverSocket;
dispatch_queue_t serverQueue; // 专用队列
dispatch_source_t restartTimer; // 重用的定时器
NSUInteger restartAttempts;
}
@end
@implementation XUDPServer
+(instancetype)sharedInstance
{
static XUDPServer* _sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once (&oncePredicate, ^{
_sharedInstance = [[XUDPServer alloc] init];
});
return _sharedInstance;
}
-(instancetype)init {
if (self = [super init]) {
restartAttempts = 0;
// 创建串行队列,避免并发问题
serverQueue = dispatch_queue_create("com.xudpserver.queue", DISPATCH_QUEUE_SERIAL);
return self;
}
return nil;
}
- (void)start {
// 使用专用队列,确保操作串行化
dispatch_async(serverQueue, ^{
[self _startInternal];
});
}
- (void) _startInternal {
NSLog(@"XS- start udp server");
// 避免重复创建
if (serverSocket && !serverSocket.isClosed) {
NSLog(@"UDP server already running");
return;
}
[self _stopInternal];
serverSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
delegateQueue:serverQueue];
NSError *error = nil;
if (![serverSocket bindToPort:PORT error:&error]) {
NSLog(@"❌ Error binding to port %d: %@", PORT, error);
if (error.code == 48) {
[self _tryFallbackPorts];
return;
}
[self _scheduleRestartWithBackoff];
return;
}
if (![serverSocket beginReceiving:&error]) {
NSLog(@"❌ Error starting server (recv): %@", error);
[self _scheduleRestartWithBackoff];
return;
}
restartAttempts = 0; // 重置重试计数
NSLog(@"✅ UDP server started successfully on port %d", PORT);
}
- (void)stop {
dispatch_async(serverQueue, ^{
[self _stopInternal];
});
}
- (void)_stopInternal {
// 取消待处理的重启定时器
[self _cancelRestartTimer];
if (serverSocket) {
NSLog(@"Stopping UDP server on port %d", PORT);
[serverSocket close];
serverSocket = nil;
}
}
#pragma mark - Restart Logic with Backoff
- (void)_cancelRestartTimer {
if (restartTimer) {
dispatch_source_cancel(restartTimer);
restartTimer = nil;
}
}
- (void)_scheduleRestartWithBackoff {
// 取消之前的定时器
[self _cancelRestartTimer];
// 限制重试次数
const NSUInteger maxAttempts = 10;
if (restartAttempts >= maxAttempts) {
NSLog(@"❌ Maximum restart attempts (%lu) reached, giving up",
(unsigned long)maxAttempts);
return;
}
restartAttempts++;
// 指数退避1s, 2s, 4s, 8s, 16s, 最大60s
NSTimeInterval delay = MIN(pow(2, restartAttempts - 1), 60.0);
NSLog(@"⏰ Scheduling restart attempt %lu in %.1f seconds",
(unsigned long)restartAttempts, delay);
// 使用 dispatch_source 创建可取消的定时器
restartTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, serverQueue);
dispatch_source_set_timer(restartTimer,
dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC),
DISPATCH_TIME_FOREVER,
0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(restartTimer, ^{
[self _startInternal];
});
dispatch_resume(restartTimer);
}
- (void)_tryFallbackPorts {
NSLog(@"❌ No available ports found");
[self _scheduleRestartWithBackoff];
}
- (void)scheduleRestart {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)),
serverQueue, ^{
[self start];
});
}
// 网络连接成功后 自动回调
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address
{
NSLog(@"已连接到用户:ip:%@",[[NSString alloc]initWithData:address encoding:NSUTF8StringEncoding]);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
@autoreleasepool {
// 安全的字符串转换
NSString *datastr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (!datastr) {
NSLog(@"Failed to decode received data");
return;
}
NSLog(@"XS- UDP Request>>>> %@", datastr);
UDPHandler *handle = [UDPHandler sharedInstance];
NSString *res = [handle handle:datastr];
if (res) {
NSData *responseData = [res dataUsingEncoding:NSUTF8StringEncoding];
[sock sendData:responseData toAddress:address withTimeout:10.0 tag:300];
}
}
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error
{
NSLog(@"didNotConnect:%@", error);
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
NSLog(@"didSendDataWithTag:%ld", tag);
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error
{
NSLog(@"didNotSendDataWithTag:%@", error);
}
- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError * _Nullable)error
{
NSLog(@"withError:%@", error);
if (error) {
[self scheduleRestart]; // 自动重连
}
}
- (void)dealloc {
[self _cancelRestartTimer];
[self _stopInternal];
}
@end