增加尝试其他端口

This commit is contained in:
xsean 2025-11-05 10:21:21 +08:00
parent fa49dcac96
commit a8a122a6e0
19 changed files with 2005 additions and 1723 deletions

View File

@ -6,5 +6,5 @@ Author: XYZShell
Section: Utilities
Tag: role::developer
Architecture: iphoneos-arm
Version: 0.0.7-10-61+debug
Installed-Size: 1600
Version: 0.0.7-10-62+debug
Installed-Size: 1604

View File

@ -1 +1 @@
./packages/com.xyzshell.ioscontrol_0.0.7-10-61+debug_iphoneos-arm.deb
./packages/com.xyzshell.ioscontrol_0.0.7-10-62+debug_iphoneos-arm.deb

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1 +1 @@
61
62

View File

@ -1,96 +1,97 @@
//
// XUDPServer.m
// xcmd
//
// Created by mac on 2025/2/17.
//
#import <Foundation/Foundation.h>
#include <complex.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; //
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
+(instancetype)sharedInstance
{
#pragma mark - Singleton
+ (instancetype)sharedInstance {
static XUDPServer* _sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once (&oncePredicate, ^{
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[XUDPServer alloc] init];
});
return _sharedInstance;
}
-(instancetype)init {
- (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 {
- (uint16_t)udp_port {
return currentPort;
}
- (void) _startInternal {
NSLog(@"XS- start udp server");
- (void)_startInternal {
NSLog(@"XS- Starting UDP server on port %d", currentPort);
//
if (serverSocket && !serverSocket.isClosed) {
NSLog(@"UDP server already running");
NSLog(@"⚠️ UDP server already running on port %d", currentPort);
return;
}
[self _stopInternal];
[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");
}
// socket
[self _configureSocketOptions];
//
if (![serverSocket bindToPort:currentPort error:&error]) {
NSLog(@"❌ Error binding to port %d: %@", currentPort, error);
// socket
[serverSocket close];
serverSocket = nil;
@ -105,20 +106,80 @@
}
if (![serverSocket beginReceiving:&error]) {
NSLog(@"❌ Error starting server (recv): %@", error);
// socket
[serverSocket close];
serverSocket = nil;
[self _scheduleRestartWithBackoff];
return;
}
restartAttempts = 0; //
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 - socketSIGPIPE
#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];
@ -126,20 +187,87 @@
}
- (void)_stopInternal {
[self _cancelRestartTimer];
NSLog(@"XS- Stopping UDP server on port %d", currentPort);
[self _cancelRestartTimer];
[self _stopHealthCheck];
if (serverSocket) {
NSLog(@"Stopping UDP server on port %d", currentPort);
// SO_LINGER0,,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 - Restart Logic with Backoff
#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) {
@ -149,10 +277,8 @@
}
- (void)_scheduleRestartWithBackoff {
//
[self _cancelRestartTimer];
//
const NSUInteger maxAttempts = 10;
if (restartAttempts >= maxAttempts) {
NSLog(@"❌ Maximum restart attempts (%lu) reached, giving up",
@ -162,13 +288,11 @@
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,
@ -184,34 +308,53 @@
}
- (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;
}
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;
}
[testSocket close];
}
NSLog(@"❌ No available ports found");
}
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)),
@ -220,65 +363,160 @@
});
}
#pragma mark - GCDAsyncUdpSocket Delegate
//
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address
{
NSLog(@"已连接到用户:ip:%@",[[NSString alloc]initWithData:address encoding:NSUTF8StringEncoding]);
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
NSLog(@"✅ Connected to client");
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
- (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");
// ,
if (data.length > 65507) { // UDP
NSLog(@"⚠️ Received oversized packet: %lu bytes", (unsigned long)data.length);
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];
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)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error
{
NSLog(@"didNotConnect:%@", error);
// ()
- (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)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
NSLog(@"didSendDataWithTag:%ld", 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 didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error
{
NSLog(@"didNotSendDataWithTag:%@", error);
- (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)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError * _Nullable)error
{
NSLog(@"withError:%@", error);
- (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) {
[self scheduleRestart]; //
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
@end