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

522 lines
16 KiB
Objective-C

#import <Foundation/Foundation.h>
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#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<NSNumber *, NSDictionary *> *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