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

285 lines
7.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>
#include <complex.h>
#import <sys/socket.h>
#import "XUDPServer.h"
#import "UDPHandler.h"
#define FALLBACK_PORT_START 6001
#define FALLBACK_PORT_END 7000
#define PORT 6001
@interface XUDPServer() {
@private
GCDAsyncUdpSocket *serverSocket;
dispatch_queue_t serverQueue; // 专用队列
dispatch_source_t restartTimer; // 重用的定时器
NSUInteger restartAttempts;
uint16_t currentPort;
}
@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;
// 创建串行队列,避免并发问题
currentPort = PORT;
serverQueue = dispatch_queue_create("com.xudpserver.queue", DISPATCH_QUEUE_SERIAL);
return self;
}
return nil;
}
- (void)start {
// 使用专用队列,确保操作串行化
dispatch_async(serverQueue, ^{
[self _startInternal];
});
}
- (uint16_t) udp_port {
return currentPort;
}
- (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 enableReusePort:YES error:&error]) {
NSLog(@"❌ Error enabling reuse port: %@", error);
}
// ⭐️ 设置 SO_REUSEADDR
int reuseOn = 1;
if (setsockopt([serverSocket socketFD], SOL_SOCKET, SO_REUSEADDR,
&reuseOn, sizeof(reuseOn)) == -1) {
NSLog(@"❌ Error setting SO_REUSEADDR");
}
if (![serverSocket bindToPort:currentPort error:&error]) {
NSLog(@"❌ Error binding to port %d: %@", currentPort, error);
// ⭐️ 绑定失败后清理 socket
[serverSocket close];
serverSocket = nil;
if (error.code == 48) { // EADDRINUSE
NSLog(@"⚠️ Port %d is in use (possibly TIME_WAIT)", currentPort);
[self _tryFallbackPorts];
return;
}
[self _scheduleRestartWithBackoff];
return;
}
if (![serverSocket beginReceiving:&error]) {
NSLog(@"❌ Error starting server (recv): %@", error);
// ⭐️ 接收失败后清理 socket
[serverSocket close];
serverSocket = nil;
[self _scheduleRestartWithBackoff];
return;
}
restartAttempts = 0; // 重置重试计数
NSLog(@"✅ UDP server started successfully on port %d", currentPort);
}
- (void)stop {
dispatch_async(serverQueue, ^{
[self _stopInternal];
});
}
- (void)_stopInternal {
[self _cancelRestartTimer];
if (serverSocket) {
NSLog(@"Stopping UDP server on port %d", currentPort);
// ⭐️ 确保完全关闭
if (!serverSocket.isClosed) {
[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 {
for (uint16_t port = FALLBACK_PORT_START; port <= FALLBACK_PORT_END; port++) {
GCDAsyncUdpSocket *testSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:nil
delegateQueue:serverQueue];
NSError *error = nil;
if ([testSocket bindToPort:port error:&error]) {
[testSocket close];
// 找到可用端口
serverSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self
delegateQueue:serverQueue];
if ([serverSocket bindToPort:port error:&error] &&
[serverSocket beginReceiving:&error]) {
currentPort = port;
restartAttempts = 0;
NSLog(@"✅ UDP server started on fallback port %d", currentPort);
return;
}
}
[testSocket close];
}
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