#import #import #import #import #import "XUDPServer.h" #import "UDPHandler.h" #define FALLBACK_PORT_START 6001 #define FALLBACK_PORT_END 7000 #define PORT 6001 #define SEND_TIMEOUT 5.0 // 发送超时时间 @interface XUDPServer() { @private GCDAsyncUdpSocket *serverSocket; dispatch_queue_t serverQueue; dispatch_source_t restartTimer; NSUInteger restartAttempts; uint16_t currentPort; NSTimer *healthCheckTimer; } @property (nonatomic, strong) NSMutableDictionary *pendingSends; @property (nonatomic, assign) long currentTag; @end @implementation XUDPServer #pragma mark - Singleton + (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; _currentTag = 0; _pendingSends = [NSMutableDictionary dictionary]; // 创建串行队列,避免并发问题 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- Starting UDP server on port %d", currentPort); // 避免重复创建 if (serverSocket && !serverSocket.isClosed) { NSLog(@"⚠️ UDP server already running on port %d", currentPort); 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); } // ⭐️ 设置所有必要的socket选项 [self _configureSocketOptions]; // 尝试绑定到指定端口 if (![serverSocket bindToPort:currentPort error:&error]) { NSLog(@"❌ Error binding to port %d: %@", currentPort, error); [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); [serverSocket close]; serverSocket = nil; [self _scheduleRestartWithBackoff]; return; } restartAttempts = 0; NSLog(@"✅ UDP server started successfully on port %d", currentPort); // ⭐️ 启动健康检查 [self _startHealthCheck]; } // ⭐️ 配置所有socket选项 - (void)_configureSocketOptions { if (!serverSocket) return; int fd = [serverSocket socketFD]; if (fd == -1) { NSLog(@"⚠️ Invalid socket file descriptor"); return; } // 1. 设置 SO_REUSEADDR - 允许快速重启,避免TIME_WAIT问题 int reuseAddr = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)) == -1) { NSLog(@"❌ Error setting SO_REUSEADDR: %s", strerror(errno)); } // 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)); } // 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); } // 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信号 #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)); } #endif // 6. ⭐️ 设置非阻塞模式(GCDAsyncUdpSocket通常已设置,但确保一下) int flags = fcntl(fd, F_GETFL, 0); if (flags != -1) { fcntl(fd, F_SETFL, flags | O_NONBLOCK); } } - (void)stop { dispatch_async(serverQueue, ^{ [self _stopInternal]; }); } - (void)_stopInternal { 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; } [_pendingSends removeAllObjects]; } #pragma mark - Health Check // ⭐️ 健康检查 - (void)_startHealthCheck { dispatch_async(dispatch_get_main_queue(), ^{ if (self->healthCheckTimer) { [self->healthCheckTimer invalidate]; } self->healthCheckTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(_performHealthCheck) userInfo:nil repeats:YES]; }); } - (void)_stopHealthCheck { dispatch_async(dispatch_get_main_queue(), ^{ if (self->healthCheckTimer) { [self->healthCheckTimer invalidate]; self->healthCheckTimer = nil; } }); } - (void)_performHealthCheck { dispatch_async(serverQueue, ^{ if (!self->serverSocket || self->serverSocket.isClosed) { NSLog(@"⚠️ Health check failed: socket is closed"); [self _startInternal]; return; } // ⭐️ 检查socket状态 int fd = [self->serverSocket socketFD]; if (fd == -1) { NSLog(@"⚠️ Health check failed: invalid socket"); [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; } NSLog(@"✅ Health check passed for port %d", self->currentPort); }); } #pragma mark - Restart Logic - (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++; NSTimeInterval delay = MIN(pow(2, restartAttempts - 1), 60.0); NSLog(@"⏰ Scheduling restart attempt %lu in %.1f seconds", (unsigned long)restartAttempts, delay); 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(@"🔍 Searching for available fallback port..."); for (uint16_t port = FALLBACK_PORT_START; port <= FALLBACK_PORT_END; port++) { // ⭐️ 检查端口是否真的可用 if ([self _isPortAvailable:port]) { currentPort = port; NSLog(@"✅ Found available port: %d", port); [self _startInternal]; return; } } NSLog(@"❌ No available fallback ports found in range %d-%d", FALLBACK_PORT_START, FALLBACK_PORT_END); [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 int reuseAddr = 1; setsockopt(testSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, sizeof(reuseAddr)); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; 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; } } - (void)scheduleRestart { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), serverQueue, ^{ [self start]; }); } #pragma mark - GCDAsyncUdpSocket Delegate - (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address { NSLog(@"✅ Connected to client"); } - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext { @autoreleasepool { // ⭐️ 检查数据大小,防止超大包 if (data.length > 65507) { // UDP最大包大小 NSLog(@"⚠️ Received 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); 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]; 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); 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]; }); }); } // ⭐️ 检查发送超时 - (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)]; } } - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error { NSLog(@"❌ Did not connect: %@", error); } - (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)"); [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); if (sock == serverSocket) { serverSocket = nil; } if (error) { NSLog(@"❌ Unexpected closure, scheduling restart"); [self scheduleRestart]; } } #pragma mark - Utility Methods // ⭐️ 将地址转换为可读字符串 - (NSString *)_addressToString:(NSData *)addressData { struct sockaddr_in *addr = (struct sockaddr_in *)addressData.bytes; char ipStr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &addr->sin_addr, ipStr, sizeof(ipStr)); return [NSString stringWithFormat:@"%s:%d", ipStr, ntohs(addr->sin_port)]; } - (void)dealloc { [self _cancelRestartTimer]; [self _stopInternal]; } @end