Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/ThreemaFramework/Network/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 8 kB image not shown  

Quelle  ChatTcpSocket.m   Sprache: Unknown

 

Spracherkennung für: .m vermutete Sprache: Abap {ABAP[45] Lisp[45] Masm[45]} [Methode: maximale Elemente, drei Dimensionen]

#import <Foundation/Foundation.h>
#import "ChatTcpSocket.h"
#import "GCDAsyncSocketFactory.h"
#import "SocketProtocolDelegate.h"

#ifdef DEBUG
  static const DDLogLevel ddLogLevel = DDLogLevelAll;
#else
  static const DDLogLevel ddLogLevel = DDLogLevelWarning;
#endif

// Tracks the index of the last port that won a connection race. Shared across all instances
// so each new connection starts from the previously successful port.
static NSUInteger _lastConnectedPortIndex = 0;

@implementation ChatTcpSocket {
    NSString *_server;
    NSArray<NSNumber *> *_ports;
    id<SocketProtocolDelegate> _delegate;
    dispatch_queue_t _queue;
    BOOL _preferIPv6;

    // Sockets still racing to connect, with their corresponding port at the same index.
    // Cleared once a winner is chosen or all candidates fail.
    NSMutableArray<GCDAsyncSocket *> *pendingSockets;
    NSMutableArray<NSNumber *> *pendingPorts;

    // Set once a socket wins the race; nil until then.
    GCDAsyncSocket *activeSocket;
}

- (nullable instancetype)initWithServer:(NSString * _Nonnull)server
                                  ports:(NSArray<NSNumber *> * _Nonnull)ports
                             preferIPv6:(BOOL)preferIPv6
                               delegate:(id<SocketProtocolDelegate> _Nonnull)delegate
                                  queue:(dispatch_queue_t _Nonnull)queue
                                  error:(NSError * _Nullable __autoreleasing * _Nullable)error {
    self = [super init];
    if (self) {
        self->_server = server;
        self->_ports = ports;
        self->_delegate = delegate;
        self->_queue = queue;
        self->_preferIPv6 = preferIPv6;

        pendingSockets = [NSMutableArray array];
        pendingPorts = [NSMutableArray array];

        // Rotate from the last known-good port so it enters the race first.
        NSUInteger count = _ports.count;
        NSUInteger startPortIndex = (count > 0) ? (_lastConnectedPortIndex % count) : 0;
        for (NSUInteger i = 0; i < count; i++) {
            NSUInteger portIndex = (startPortIndex + i) % count;
            NSNumber *port = _ports[portIndex];
            GCDAsyncSocket *socket = [GCDAsyncSocketFactory proxyAwareAsyncSocketForHost:server
                                                                                  port:port
                                                                              delegate:self
                                                                         delegateQueue:queue];
            [socket setIPv4PreferredOverIPv6:!_preferIPv6];
            [pendingSockets addObject:socket];
            [pendingPorts addObject:port];
        }

        // All sockets share the same proxy configuration, so the first one is representative.
        GCDAsyncSocket *firstSocket = pendingSockets.firstObject;
        _isProxyConnection = (firstSocket != nil && ![firstSocket isMemberOfClass:[GCDAsyncSocket class]]);
    }
    return self;
}

- (BOOL)isIPv6 {
    return [activeSocket isIPv6];
}

- (BOOL)connect {
    DDLogInfo(@"[ChatTcpSocket] Connecting to %@ on ports %@ simultaneously...", _server, pendingPorts);

    NSMutableIndexSet *failedItems = [NSMutableIndexSet indexSet];
    for (NSUInteger i = 0; i < pendingSockets.count; i++) {
        NSError *err;
        if (![pendingSockets[i] connectToHost:_server
                                       onPort:[pendingPorts[i] unsignedShortValue]
                                  withTimeout:kConnectTimeout
                                        error:&err]) {
            DDLogWarn(@"[ChatTcpSocket] Could not start connect on port %@: %@", pendingPorts[i], err);
            [failedItems addIndex:i];
        }
    }

    [pendingSockets removeObjectsAtIndexes:failedItems];
    [pendingPorts removeObjectsAtIndexes:failedItems];

    if (pendingSockets.count == 0) {
        DDLogWarn(@"[ChatTcpSocket] All connect attempts failed to start");
        return NO;
    }
    return YES;
}

- (void)disconnect {
    // Give the socket time for pending writes, but force disconnect if it takes too long for them to complete
    if (activeSocket != nil) {
        [activeSocket disconnectAfterWriting];

        GCDAsyncSocket *socketToDisconnect = activeSocket;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kDisconnectTimeout * NSEC_PER_SEC)), _queue, ^{
            if (self->activeSocket == socketToDisconnect) {
                DDLogInfo(@"[ChatTcpSocket] Socket still not disconnected - forcing disconnect now");
                [self->activeSocket disconnect];
            }
        });
    } else {
        // Race not yet decided — cancel all pending sockets.
        NSArray<GCDAsyncSocket *> *toCancel = [pendingSockets copy];
        [pendingSockets removeAllObjects];
        [pendingPorts removeAllObjects];
        for (GCDAsyncSocket *socket in toCancel) {
            [socket disconnect];
        }
    }
}

- (void)readWithLength:(uint32_t)length timeout:(int16_t)timeout tag:(int16_t)tag {
    [activeSocket readDataToLength:length withTimeout:timeout tag:tag];
}

- (void)writeWithData:(NSData * _Nonnull)data tag:(int16_t)tag {
    [activeSocket writeData:data withTimeout:kWriteTimeout tag:tag];
}

- (void)writeWithData:(NSData * _Nonnull)data {
}

#pragma mark - GCDAsyncSocketDelegate

- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port {
    if (activeSocket != nil) {
        // Another socket already won the race — disconnect this latecomer silently.
        [sender disconnect];
        return;
    }

    NSUInteger winnerIndex = [pendingSockets indexOfObject:sender];
    if (winnerIndex == NSNotFound) {
        [sender disconnect];
        return;
    }

    activeSocket = sender;

    // Persist the winning port index so the next instance starts its race from here.
    NSNumber *winningPort = pendingPorts[winnerIndex];
    NSUInteger globalPortIndex = [_ports indexOfObject:winningPort];
    _lastConnectedPortIndex = (globalPortIndex != NSNotFound) ? globalPortIndex : 0;

    // Disconnect all other pending sockets. Their socketDidDisconnect callbacks
    // will be ignored because they are no longer in pendingSockets.
    NSArray<GCDAsyncSocket *> *defeatedSockets = [pendingSockets copy];
    [pendingSockets removeAllObjects];
    [pendingPorts removeAllObjects];
    for (GCDAsyncSocket *defeated in defeatedSockets) {
        if (defeated != activeSocket) {
            [defeated disconnect];
        }
    }

    DDLogInfo(@"[ChatTcpSocket] Connected to %@:%d", host, port);
    [_delegate didConnect];
}

- (void)socketDidDisconnect:(GCDAsyncSocket *)sender withError:(NSError *)error {
    if (sender == activeSocket) {
        NSInteger code = 0;
        if (error != nil) {
            DDLogError(@"[ChatTcpSocket] Disconnect from chat server with error: %@", error);
            code = error.code;
        }
        activeSocket = nil;
        DDLogInfo(@"[ChatTcpSocket] Disconnected from %@:%d", [sender connectedHost], [sender connectedPort]);
        [_delegate didDisconnectWithErrorCode:code];
        return;
    }

    NSUInteger idx = [pendingSockets indexOfObject:sender];
    if (idx != NSNotFound) {
        DDLogWarn(@"[ChatTcpSocket] Connect attempt on port %@ failed: %@", pendingPorts[idx], error);
        [pendingSockets removeObjectAtIndex:idx];
        [pendingPorts removeObjectAtIndex:idx];

        if (pendingSockets.count == 0 && activeSocket == nil) {
            // All candidates exhausted without a successful connection.
            DDLogInfo(@"[ChatTcpSocket] All connection attempts failed");
            [_delegate didDisconnectWithErrorCode:(error != nil ? error.code : 0)];
        }
        return;
    }

    // Defeated socket being cleaned up after the race — nothing to do.
}

- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag {
    if (sender != activeSocket) {
        DDLogWarn(@"[ChatTcpSocket] didReadData from non-active socket");
        return;
    }
    [_delegate didReadData:data tag:(int16_t)tag];
}

- (NSTimeInterval)socket:(GCDAsyncSocket *)sender shouldTimeoutReadWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length {
    if (sender != activeSocket) {
        return 0;
    }
    DDLogInfo(@"[ChatTcpSocket] Read timeout, tag = %ld", tag);
    [activeSocket disconnect];
    return 0;
}

- (NSTimeInterval)socket:(GCDAsyncSocket *)sender shouldTimeoutWriteWithTag:(long)tag elapsed:(NSTimeInterval)elapsed bytesDone:(NSUInteger)length {
    if (sender != activeSocket) {
        return 0;
    }
    DDLogInfo(@"[ChatTcpSocket] Write timeout, tag = %ld", tag);
    [activeSocket disconnect];
    return 0;
}

@end

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.