美文网首页
socket偶现未连接

socket偶现未连接

作者: 某非著名程序员 | 来源:发表于2021-05-24 21:53 被阅读0次

背景

项目是基于MQTT,但重连逻辑被重写过。项目中会先请求LBS接口返回ip地址,再进行socket链接。

需求

之前是只有一个LBS地址,失败之后内部重试链接。现在服务端会返回两个ip地址,第一个失败之后再进行第二个连接。

socket连接逻辑

- (void)open {
    self.state = DOLPTransportOpening;

    NSError* connectError;

    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;

    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)self.host, self.port, &readStream, &writeStream);
    
    CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
    CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
    ...
    if (!connectError) {
        self.encoder = [[DOLPSSLSecurityPolicyEncoder alloc] init];
        CFWriteStreamSetDispatchQueue(writeStream, self.queue);
        self.encoder.stream = CFBridgingRelease(writeStream);
        self.encoder.securityPolicy = self.tls ? self.securityPolicy : nil;
        self.encoder.securityDomain = self.tls ? self.host : nil;
        self.encoder.delegate = self;
        if (self.voip) {
            [self.encoder.stream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
        }
        [self.encoder open];
        
        self.decoder = [[DOLPSSLSecurityPolicyDecoder alloc] init];
        CFReadStreamSetDispatchQueue(readStream, self.queue);
        self.decoder.stream =  CFBridgingRelease(readStream);
        self.decoder.securityPolicy = self.tls ? self.securityPolicy : nil;
        self.decoder.securityDomain = self.tls ? self.host : nil;
        self.decoder.delegate = self;
        if (self.voip) {
            [self.decoder.stream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType];
        }
        [self.decoder open];
        
    } else {
        [self close];
    }
}

socket连接使用CFStreamCreatePairWithSocketToHost,超时由系统控制。
socket连接坏的IP地址,超时时间在75s,无法控制超时时间。

解决方案

在[self.encoder open];时dispatch_after控制超时时间,如果在规定时间内没有回调,则认为ip连接失败。返回给上层,进行下一个ip连接。

改造后引起新问题

源码阅读参考MQTT源码阅读及问题分析
偶现socket未连接,复现概率比较低。只有结合日志,阅读源码。

- (void)createClient:(NSString *)host port:(int32_t)port callback:(void (^)(BOOL, NSError *))callback {
    NSString *bundlePath = [[NSBundle bundleForClass:[self class]].resourcePath stringByAppendingPathComponent:@"/DOLPCertificate.bundle"];
    NSBundle *bundle = [NSBundle bundleWithPath:bundlePath];
    NSString *caPath = [bundle pathForResource:@"ca_chain" ofType:@"der"];

    // 设置tls认证
    DOLPSSLSecurityPolicy *securityPolicy = [DOLPSSLSecurityPolicy policyWithPinningMode:SSLPinningModeCertificate];
    securityPolicy.allowInvalidCertificates = YES;
    securityPolicy.validatesDomainName = NO;
    securityPolicy.validatesCertificateChain = NO;
    securityPolicy.pinnedCertificates = @[[NSData dataWithContentsOfFile:caPath]];

    DolpBaseData *connData = [DolpProtoDataUtil createWithConnection:self.token deviceId:self.deviceId];
    NSString *clientId = [DolpDeviceUtil deviceId];
    self.sessionManager = [[DOLPSessionManager alloc] initWithQueue:self.dolpCodecQueue];
    
    [self.sessionManager connectTo:host port:port tls:YES keepalive:DOLP_KEEP_ALIVE clean:YES payload:[connData data] withClientId:clientId securityPolicy:securityPolicy certificates:nil protocolLevel:DOLPProtocolVersion10 connectHandler:^(NSError *error) {
        if (error) {
            callback(false, error);
            BLLogError(@"【socket】服务器连接失败 %@ | host:%@ | port:%@", error,host,@(port));
        } else {
            callback(true, nil);
            BLLogInfo(@"【socket】服务器连接成功host:%@ | port:%@",host,@(port));
        }
    }];
    self.sessionManager.delegate = self;
}

突破点在于connectHandler这个block;为什么没有回调。

解决问题

  1. 经过日志排查,源码阅读;排查发现mqtt在进前台时,也进行了session重连,把lbs的block吞掉了。只有当mqtt的进前台的连接在lbs后面才会发生。
//ForegroundReconnection
- (void)appDidBecomeActive {
    [self.sessionManager connectToLast:nil];
    [self endBackgroundTask];
}
  1. 修改:依靠lbs接口逻辑进行重连,进前台不走mqtt的重连,统一调用。
    a. 仔细分析发现:以前就一个ip地址,重连也是原来的ip。
    b. 现在可能重连是坏的ip地址。

小结

  1. 由于mqtt源码在接手之前就被改过,不确定改了哪些代码。然后对mqtt的源码不熟悉,排查问题产生一定的难度。对于第三方库,如果要修改,建议使用runtime方式,后来开发者能知道修改的代码。
  2. 经过不断测试,尝试寻找复现路径;结合日志,阅读源码,看看为什么被吞掉了。代码分析花了两天半,解决问题花了两小时。

相关文章

  • socket偶现未连接

    背景 项目是基于MQTT,但重连逻辑被重写过。项目中会先请求LBS接口返回ip地址,再进行socket链接。 需求...

  • socket

    Socket连接与HTTP连接的不同 通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信...

  • 第二篇:socket编程

    目录一、socket是什么?二、socket连接与HTTP连接的区别三、如何建立一个socket连接?四、使用OC...

  • 传输层-Socket

    Socket TCP Socket UDP Socket 连接处理方式 一连接一进程 一连接一线程 多连接一线程 ...

  • iOS - HTTP、Socket、TCP的区别

    目录 TCP连接与HTTP连接的区别TCP连接与Socket连接的区别HTTP连接与Socket连接的区别什么时候...

  • mysql 两种连接方式和SSL连接、x509认证

    mysql连接可分为socket连接和TCP|IP连接两种。 1、本地socket连接 [root@localho...

  • TCP HTTP Soket

    1、TCP连接 2、HTTP连接 3、SOCKET原理 3.1套接字(socket)概念 3.2 建立socket...

  • connect timed out问题排查

    最近观察到日志上偶现一个错误: 该问题很清晰,就是发起http请求时,构建TCP连接时发生了超时问题。 由于是偶现...

  • 2018-04-23

    LVS+Keepalived 实现socket连接,socket client无法连接lvs? VIP:10.5....

  • (二)即时通讯系列之TCP Socket

    即时通讯系列之Socket简介 一、Socket连接 1.连接过程 建立Socket连接至少需要一对套接字,其中一...

网友评论

      本文标题:socket偶现未连接

      本文链接:https://www.haomeiwen.com/subject/ygjxsltx.html