231 lines
5.7 KiB
Objective-C
231 lines
5.7 KiB
Objective-C
//
|
||
// 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
|