iOS - XMPP 재연결 및 기타 질문(완료)

7701 단어
실제 응용 장면에서 각종 연결 문제에 부딪힐 수 있다. 예를 들어 네트워크 차이로 인해 socket 연결이 끊어지고 ping 패키지 서버가 응답하지 않으며 서버가 자발적으로 하선을 제거하고 앞뒤로 데이터 패키지를 해석할 수 없는 등 사용자의 체험에 영향을 줄 수 있다.
이런 상황에서 보통 재연결 메커니즘을 설계한다. 긴 연결이 끊어질 때마다 어떤 방식에 따라 재연결 조작을 시작하고 사용자가 아무런 감지도 하지 않은 상황에서 다시 긴 연결을 구축하는 것이야말로 좋은 체험이다.XMPP 프레임워크에서 당신은 스스로 중련을 실현할 필요가 없다. 프레임워크 안에서 중련의 방법을 제공했다. 이런 종류는 바로 XMPPReconnect이다. 당신은 몇 가지 절차만 거치면 실현할 수 있다.
@interface XMPPManager ()

@property (nonatomic, strong) XMPPStream *xmppStream;
/**   */
@property (nonatomic, strong) XMPPAutoPing *xmppAutoPing;
/**   */
@property (nonatomic, strong) XMPPReconnect *xmppReconnect;

@end

@implementation XMPPManager {
    /**  , 0 */
    NSInteger reconnectCount;
    /** ping , 0 */
    NSInteger pingTimeoutCount;
    /**  ,  */
    dispatch_queue_t _streamQueue;
}

//  , ...
//  init , setupStream , ...

 /** Setup the XMPP stream */
- (void)setupStream {
    _xmppStream = [[XMPPStream alloc] init];
    [_xmppStream addDelegate:self delegateQueue: _streamQueue];
        
    _xmppAutoPing = [[XMPPAutoPing alloc] init];
    _xmppAutoPing.pingInterval = 20.f; //  
    [_xmppAutoPing activate:_xmppStream];
    [_xmppAutoPing addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];

    _xmppReconnect = [[XMPPReconnect alloc] init];
    _xmppReconnect.autoReconnect = YES;
    _xmppReconnect.reconnectDelay = 0.f;//  , , 
    _xmppReconnect.reconnectTimerInterval = 3.f;//  3 
    [_xmppReconnect activate:_xmppStream];
    [_xmppReconnect addDelegate:self delegateQueue:dispatch_get_global_queue(0, 0)];
}

이렇게 다시 연결된 클래스는 초기화되고, 이어서 프록시 리셋을 쓰고, 연결을 끊으면 리셋 방법을 사용합니다.
......
#pragma mark - XMPPReconnectDelegate

- (void)xmppReconnect:(XMPPReconnect *)sender didDetectAccidentalDisconnect:(SCNetworkConnectionFlags)connectionFlags {
    QCLog(@"xmpp 。");
}

- (BOOL)xmppReconnect:(XMPPReconnect *)sender shouldAttemptAutoReconnect:(SCNetworkConnectionFlags)connectionFlags {
    reconnectCount++;
    self.isReconnecting = YES;
    QCLog(@"xmpp ... %@ ", @(reconnectCount));

    if (reconnectCount < 5) {
    }
    else if (reconnectCount >= 5 && reconnectCount <= 10) {
        [self.xmppReconnect resSetupReconnectTimerWithTimerInterval:9.f];
    }
    else if (reconnectCount > 10 && reconnectCount <= 15) {
        [self.xmppReconnect resSetupReconnectTimerWithTimerInterval:15.f];
    }
    else {
        [self reconnectImmediately];
    }
    return YES;
}
......

- (void)reconnectImmediately {    
    self.xmppReconnect.reconnectTimerInterval = 3.f;
    reconnectCount = 0;

    [self.xmppReconnect stop];
    [self.xmppReconnect manualStart];
}

여기에 설명이 필요합니다. 연결을 잃었을 때 즉시 재연결을 시작합니다. 앞의 5회는 3초마다 다시 연결합니다. 뒤의 5회는 9초마다 다시 연결합니다. 뒤의 5회는 15초마다 다시 연결합니다. 만약 이 15번이 모두 실패한다면 이 규칙에 따라 다시 연결합니다.나는 중복 접속을 몇 번 하지 않고 버렸다. 이렇게 하면 서버에 일정한 압력을 가할 수 있고 이해득실을 스스로 고려할 수 있다.생각해 보니 이렇게 여러 번 실패했으니 연결이 안 되겠지, 다시 연결할 필요는 없을 거야.열 번의 연결이 실패하면 다시 연결하지 않도록 할 수 있다.사용자가 자발적으로 어떤 조작을 촉발할 때 다시 연결을 시작하거나 앱 앞 백그라운드 전환할 때 다시 연결을 합니다.이것들은 단지 개인적인 생각일 뿐이다.XMPP 클래스에서 동적 재연결 시간 간격을 조정하는 방법을 확장했습니다. 방법은 다음과 같습니다.
- (void)resSetupReconnectTimerWithTimerInterval:(NSTimeInterval)interval
{
    //  
    if (reconnectTimer != NULL)
    {
        if (reconnectTimer)
        {
            dispatch_source_cancel(reconnectTimer);
            reconnectTimer = NULL;
        }
    }

    if ((reconnectDelay <= 0.0) && (reconnectTimerInterval <= 0.0))
    {
        // All timed reconnect attempts are disabled
        return;
    }

    reconnectTimerInterval = interval;
    reconnectTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, moduleQueue);

    dispatch_source_set_event_handler(reconnectTimer, ^{ @autoreleasepool {
        [self maybeAttemptReconnect];
    }});

#if !OS_OBJECT_USE_OBJC
    dispatch_source_t theReconnectTimer = reconnectTimer;

    dispatch_source_set_cancel_handler(reconnectTimer, ^{
        XMPPLogVerbose(@"dispatch_release(reconnectTimer)");
        dispatch_release(theReconnectTimer);
    });
#endif

    dispatch_time_t startTime;
    if (reconnectDelay > 0.0) {
        startTime = dispatch_time(DISPATCH_TIME_NOW, (reconnectDelay * NSEC_PER_SEC));
    } else {
        startTime = dispatch_time(DISPATCH_TIME_NOW, (reconnectTimerInterval * NSEC_PER_SEC));
    }

    uint64_t intervalTime;
    if (reconnectTimerInterval > 0.0) {
        intervalTime = reconnectTimerInterval * NSEC_PER_SEC;
    } else {
        intervalTime = 0.0;
    }

    dispatch_source_set_timer(reconnectTimer, startTime, intervalTime, 0.25);
    dispatch_resume(reconnectTimer);
}
XMPPReconnect에 대해 제 이쪽의 방법은 클라이언트가 20s마다 ping 패키지를 보내고 클라이언트가 두 번ping 패키지를 서버에 보낸 후에 서버가 되돌아오는 ping 패키지를 받지 못하면 수동으로 연결을 끊습니다(즉시 다시 연결됩니다).
......
#pragma mark - XMPPAutoPingDelegate

- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender{    
    //  1 , ping , 
    if (pingTimeoutCount > 0) {
        pingTimeoutCount = 0;
    }
}

- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender {    
    //  , disconnect 
    pingTimeoutCount++;
    if (pingTimeoutCount >= 2) {
        [self.xmppStream disconnect];
    }
}
......

여러 개의 장치 로그인에 대해서도 한 네티즌이 질문한 것이다. 한 장치에 어떤 계정pong이 로그인한 후에 한 장치에 이 계정을 로그인하면 원래의 계정은 서버에서 보낸 오류 메시지를 받을 수 있다. 물론 전제는 리셋을 쓰는 것이다.다음과 같은 방법으로 뺏겼는지 확인할 수 있다.
- (void)xmppStream:(XMPPStream *)sender didReceiveError:(NSXMLElement *)error {
    // 
    NSString *conflict = [[error elementForName:@"conflict"] stringValue];
    if (conflict) {
        //  , ....
        //  , , 
    }
}
XMPP의 디버깅 로그에 대해 메시지를 받을 때 로그가 없는 것은 답답한 일이다. 로그를 정확하게 출력하는 것은 각종 난치병을 해결하는 데 매우 유리하다.기억하십니까? 이 글에서 XMPP 프레임워크는 세 개의 소스 라이브러리를 인용했는데 그 중 하나가 바로 Cocoa Lumberjack입니다. 이것이 바로 우리가 사용해야 할 디버깅 도구입니다. 여러 번 시도해 보았지만 틀림없습니다.지금 그것을 어떻게 사용하는지 봅시다.(이 일지의 사용도 오랫동안 괴롭혔다...)먼저 XMPP류의 XMPPManager 파일의 머리 위에 다음과 같이 쓰십시오.
#import "DDLog.h"
#import "DDTTYLogger.h"
#import "DDFileLogger.h"    

#ifndef QCConsoleLoggingEnabled
// 1 ,0 
#define QCConsoleLoggingEnabled 0
#endif

#define QCContext           102333
#define QCLogAsync          YES

#if QCConsoleLoggingEnabled

#define QCLogObjc(flg, frmt, ...) LOG_OBJC_MAYBE(QCLogAsync, logLevelQC, flg, QCContext, frmt, ##__VA_ARGS__)
#define QCLogTrace()              QCLogObjc(LOG_FLAG_VERBOSE, @"%@: %@", THIS_FILE, THIS_METHOD)

#ifndef QCLogLevelll
#define QCLogLevelll LOG_LEVEL_VERBOSE
#endif
// Log levels : off, error, warn, info, verbose
static const int logLevelQC = QCLogLevelll;

#else

#define QCLogObjc(flg, frmt, ...) {}
#define QCLogTrace() {}

#endif

로그를 인쇄할지 여부를 제어하는 방법을 하나 더 추가합니다. 초기화.m할 때 호출하면 됩니다.
- (void)addLogs {
#if QCConsoleLoggingEnabled
    //  log
    DDTTYLogger *logger = [DDTTYLogger sharedInstance];
    [DDLog addLogger:logger withLevel:DDLogLevelAll];
#endif
    //  log, 
    DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; 
    fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
    fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
    [DDLog addLogger:fileLogger];
}

백그라운드 실행 정보메시지 영수증에 관해서?(사실은 간단하게 말할 수 있는...)메시지 재발급에 대해서?미안하지만, 나는 하지 않았다. 왜냐하면 이미 늦었기 때문이다. 사실 이전에 나는 할 시간이 매우 많았다. 단지... 됐어, 말하지 않겠다.
XMPP, 사랑했었어... 미워했었어...

좋은 웹페이지 즐겨찾기