Воспроизводимость iOS/iPhone - как проверить, когда интернет потерян/недоступен с помощью Reachability.m/.h

В настоящее время я использую класс с помощью достижимости apple.m/.h, и он работает, за исключением того, что он уведомляет меня о любых изменениях, где я хотел бы только уведомить пользователя, если сеть недоступна. В настоящее время, если у меня есть подключение к Интернету, а затем потеряйте сеть, это говорит мне. Однако, когда вы снова подключаетесь к сети, это также говорит мне, чего я не хочу. Я хочу, чтобы он говорил только, когда есть сеть потерь/нет.

Я считаю, что это имеет какое-то отношение к вызову:

- (void)viewWillAppear:(BOOL)animated
{
    // check for internet connection
    [[NSNotificationCenter defaultCenter]
          addObserver:self
             selector:@selector(checkNetworkStatus:)
                 name:kReachabilityChangedNotification
               object:nil];

    internetReachable = [[Reachability
                         reachabilityForInternetConnection] retain];
    [internetReachable startNotifier];

    // check if a pathway to a random host exists
    hostReachable = [[Reachability reachabilityWithHostName:
                     @"www.google.ca"] retain];
    [hostReachable startNotifier];

    // now patiently wait for the notification
}

при вызове -[NSNotificationCenter addObserver:selector:name:object:], имеет ли имя какая-либо другая функция, а значит, это буквально имя? это мой первый раз, используя NSNotificationCenter, поэтому я не очень разбираюсь в этом вопросе.

EDIT:

Вот моя функция checkNetworkStatus: (Проблема в том, что я получаю "NotReachable", когда сетевое соединение возвращается и NSAlert отключается несколько раз)

- (void) checkNetworkStatus:(NSNotification *)notice
{
        // called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)

{
    case NotReachable:
    {
        UIAlertView * alert  = [[UIAlertView alloc] initWithTitle:@"Network Failed" message:@"Please check your connection and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil ];
        [alert show];
        NSLog(@"The internet is down.");

        break;

    }
    case ReachableViaWiFi:
    {               
        NSLog(@"The internet is working via WIFI.");

        break;

    }
    case ReachableViaWWAN:
    {
        NSLog(@"The internet is working via WWAN.");

        break;

    }
}

NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
switch (hostStatus)

{
    case NotReachable:
    {
        NSLog(@"A gateway to the host server is down.");

        break;

    }
    case ReachableViaWiFi:
    {
        NSLog(@"A gateway to the host server is working via WIFI.");

        break;

    }
    case ReachableViaWWAN:
    {
        NSLog(@"A gateway to the host server is working via WWAN.");

        break;

    }
}

}

Ответ 1

Достижимость отправит уведомление, когда статус изменился, но то, что вы делаете с этим уведомлением, полностью зависит от вас. Если вы не хотите сообщать пользователю, что сеть вернулась, вам не нужно.

Параметр "имя" в методе NSNotificationCenter указывает, к какому уведомлению вы подписываетесь. Когда объект отправляет уведомление, он делает это с определенным именем.

Ответ 2

Если вы замените www.hostname.com только IP-адресом, он будет предупреждать только один раз, а не несколько раз.

Ответ 3

Я только начал играть с Reachability, и, надеюсь, то, что я обнаружил, вам полезно.

Что касается множественного "Не достижимого" при повторном подключении, можно ли связать его с этим? Здесь плакат поднял определение "достижимый" для удаленного хоста. Я предполагаю, что при повторном подключении пакета не удается пройти успешно?

Другая возможность - в Reachability Readme.txt

ВАЖНО: Досягаемость должна использовать DNS для разрешения имени хоста перед может определить Досягаемость этого хоста, и на это может потребоваться время определенных сетевых подключений. Из-за этого API вернется NotReachable до завершения разрешения имен. Эта задержка может быть видимый в интерфейсе в некоторых сетях.

Может быть, дать ему IP-адрес напрямую и посмотреть, помогает ли он?

Ответ 4

С Reachability 2.2 вы можете добавить

[hostReach connectionRequired];

перед

[internetReachable startNotifier];

чтобы решить эту проблему.

runmad ответил на эту проблему: fooobar.com/questions/517837/...

Ответ 5

Я бы выполнил весь класс Reachability, привязал его к вашему коду по мере необходимости и удалил или прокомментировал части Reachability. Просто удалите, один за другим, то, о чем вы не хотите получать уведомления. Очевидно, вам нужно хорошее понимание obj-c, класса Reachability, уведомлений и т.д., Но это можно сделать.

Ответ 6

Одна вещь, которую вы можете сделать, - отказаться от подписки на измененное уведомление NSNotificationCenter removeObserver... пока вы обрабатываете один в обратном вызове. Добавьте обратно наблюдателя перед возвратом.

Ответ 7

Мы можем проверить возможность повторного использования с помощью этого кода

добавить класс Reachability.h

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

typedef enum {
NotReachable = 0,
ReachableViaWiFi,
ReachableViaWWAN
} NetworkStatus;
#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification"

@interface Reachability: NSObject
{
BOOL localWiFiRef;
SCNetworkReachabilityRef reachabilityRef;
}

//reachabilityWithHostName- Use to check the reachability of a particular host name. 
+ (Reachability*) reachabilityWithHostName: (NSString*) hostName;

//reachabilityWithAddress- Use to check the reachability of a particular IP address. 
+ (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;

//reachabilityForInternetConnection- checks whether the default route is available.  
//  Should be used by applications that do not connect to a particular host
+ (Reachability*) reachabilityForInternetConnection;

//reachabilityForLocalWiFi- checks whether a local wifi connection is available.
+ (Reachability*) reachabilityForLocalWiFi;

//Start listening for reachability notifications on the current run loop
- (BOOL) startNotifier;
- (void) stopNotifier;

- (NetworkStatus) currentReachabilityStatus;
//WWAN may be available, but not active until a connection has been established.
//WiFi may require a connection for VPN on Demand.
- (BOOL) connectionRequired;
 @end

Достижимость .m

#import <sys/socket.h>
            #import <netinet/in.h>
            #import <netinet6/in6.h>
            #import <arpa/inet.h>
            #import <ifaddrs.h>
            #import <netdb.h>

            #import <CoreFoundation/CoreFoundation.h>

            #import "Reachability.h"

            #define kShouldPrintReachabilityFlags 1

            static void PrintReachabilityFlags(SCNetworkReachabilityFlags    flags, const char* comment)
            {
            #if kShouldPrintReachabilityFlags

                NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
                        (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
                        (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',

                        (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
                        (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
                        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
                        (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
                        (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
                        (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
                        (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
                        comment
                        );
            #endif
            }


            @implementation Reachability
            static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
            {
                #pragma unused (target, flags)
                NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
                NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");

                //We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
                // in case someon uses the Reachablity object in a different thread.
                NSAutoreleasePool* myPool = [[NSAutoreleasePool alloc] init];

                Reachability* noteObject = (Reachability*) info;
                // Post a notification to notify the client that the network reachability changed.
                [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];

                [myPool release];
            }

            - (BOOL) startNotifier
            {
                BOOL retVal = NO;
                SCNetworkReachabilityContext    context = {0, self, NULL, NULL, NULL};
                if(SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
                {
                    if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))
                    {
                        retVal = YES;
                    }
                }
                return retVal;
            }

            - (void) stopNotifier
            {
                if(reachabilityRef!= NULL)
                {
                    SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
                }
            }

            - (void) dealloc
            {
                [self stopNotifier];
                if(reachabilityRef!= NULL)
                {
                    CFRelease(reachabilityRef);
                }
                [super dealloc];
            }

            + (Reachability*) reachabilityWithHostName: (NSString*) hostName;
            {
                Reachability* retVal = NULL;
                SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
                if(reachability!= NULL)
                {
                    retVal= [[[self alloc] init] autorelease];
                    if(retVal!= NULL)
                    {
                        retVal->reachabilityRef = reachability;
                        retVal->localWiFiRef = NO;
                    }
                }
                return retVal;
            }

            + (Reachability*) reachabilityWithAddress: (const struct sockaddr_in*) hostAddress;
            {
                SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
                Reachability* retVal = NULL;
                if(reachability!= NULL)
                {
                    retVal= [[[self alloc] init] autorelease];
                    if(retVal!= NULL)
                    {
                        retVal->reachabilityRef = reachability;
                        retVal->localWiFiRef = NO;
                    }
                }
                return retVal;
            }

            + (Reachability*) reachabilityForInternetConnection;
            {
                struct sockaddr_in zeroAddress;
                bzero(&zeroAddress, sizeof(zeroAddress));
                zeroAddress.sin_len = sizeof(zeroAddress);
                zeroAddress.sin_family = AF_INET;
                return [self reachabilityWithAddress: &zeroAddress];
            }

            + (Reachability*) reachabilityForLocalWiFi;
            {
                struct sockaddr_in localWifiAddress;
                bzero(&localWifiAddress, sizeof(localWifiAddress));
                localWifiAddress.sin_len = sizeof(localWifiAddress);
                localWifiAddress.sin_family = AF_INET;
                // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
                localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
                Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress];
                if(retVal!= NULL)
                {
                    retVal->localWiFiRef = YES;
                }
                return retVal;
            }

            #pragma mark Network Flag Handling

            - (NetworkStatus) localWiFiStatusForFlags: (SCNetworkReachabilityFlags) flags
            {
                PrintReachabilityFlags(flags, "localWiFiStatusForFlags");

                BOOL retVal = NotReachable;
                if((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))
                {
                    retVal = ReachableViaWiFi;  
                }
                return retVal;
            }

            - (NetworkStatus) networkStatusForFlags: (SCNetworkReachabilityFlags) flags
            {
                PrintReachabilityFlags(flags, "networkStatusForFlags");
                if ((flags & kSCNetworkReachabilityFlagsReachable) == 0)
                {
                    // if target host is not reachable
                    return NotReachable;
                }

                BOOL retVal = NotReachable;

                if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)
                {
                    // if target host is reachable and no connection is required
                    //  then we'll assume (for now) that your on Wi-Fi
                    retVal = ReachableViaWiFi;
                }


                if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
                    (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))
                {
                        // ... and the connection is on-demand (or on-traffic) if the
                        //     calling application is using the CFSocketStream or higher APIs

                        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)
                        {
                            // ... and no [user] intervention is needed
                            retVal = ReachableViaWiFi;
                        }
                    }

                if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)
                {
                    // ... but WWAN connections are OK if the calling application
                    //     is using the CFNetwork (CFSocketStream?) APIs.
                    retVal = ReachableViaWWAN;
                }
                return retVal;
            }

            - (BOOL) connectionRequired;
            {
                NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
                SCNetworkReachabilityFlags flags;
                if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
                {
                    return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
                }
                return NO;
            }

            - (NetworkStatus) currentReachabilityStatus
            {
                NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
                NetworkStatus retVal = NotReachable;
                SCNetworkReachabilityFlags flags;
                if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
                {
                    if(localWiFiRef)
                    {
                        retVal = [self localWiFiStatusForFlags: flags];
                    }
                    else
                    {
                        retVal = [self networkStatusForFlags: flags];
                    }
                }
                return retVal;
            }
            @end

и использовать метод прямого вызова в классе appdel, используя

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkNetworkStatus:) name:kReachabilityChangedNotification object:nil];



 -(void) checkNetworkStatus:(NSNotification *)notice
{

 Reachability* internetReachable;
  BOOL isInternetActive;
// called after network status changes
NetworkStatus internetStatus = [internetReachable currentReachabilityStatus];
switch (internetStatus)
{
    case NotReachable:
    {
        NSLog(@"The internet is down.");
        isInternetActive = NO;

        break;
    }
    case ReachableViaWiFi:
    {
        NSLog(@"The internet is working via WIFI.");
        isInternetActive = YES;

        break;
    }
    case ReachableViaWWAN:
    {
        NSLog(@"The internet is working via WWAN.");
        isInternetActive = YES;

        break;
    }
 }

 NetworkStatus hostStatus = [hostReachable currentReachabilityStatus];
switch (hostStatus)
{
    case NotReachable:
    {
        NSLog(@"A gateway to the host server is down.");
        //            self.hostActive = NO;

        break;
    }
    case ReachableViaWiFi:
    {
        NSLog(@"A gateway to the host server is working via WIFI.");
        //            self.hostActive = YES;

        break;
    }
    case ReachableViaWWAN:
    {
        NSLog(@"A gateway to the host server is working via WWAN.");
        //            self.hostActive = YES;

        break;
    }
 }
}