// // XUDPServer.m // xcmd // // Created by mac on 2025/2/17. // #import #include #import #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