Доступность iOS / iPhone - Как только проверить, когда интернет потерян / недоступен, используя Reachability.m / .h

В настоящее время я использую класс apple reachability.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, поэтому я плохо разбираюсь в этом вопросе.

РЕДАКТИРОВАТЬ:

Вот моя функция 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;

    }
}

}


person Mausimo    schedule 23.01.2011    source источник
comment
Забавная вещь: я только что заметил, что если iPhone подключен к AdHoc WiFi (без подключения к Интернету), результат по-прежнему положительный для подключения к Интернету через Wi-Fi.   -  person Rok Jarc    schedule 21.02.2012
comment
@rokjarc, поэтому вы также проверяете, доступен ли хост.   -  person Joe    schedule 13.08.2012
comment
правда: обычно это все, что вам нужно знать. я просто считаю, что номенклатура неправильная: в изолированной сети AdHoc Интернет (или WWW) недоступен ... но я тут не на что ругаюсь :)   -  person Rok Jarc    schedule 15.08.2012


Ответы (7)


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

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

person Brian    schedule 23.01.2011

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

person ddavtian    schedule 25.05.2012

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

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

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

ВНИМАНИЕ! Чтобы определить доступность этого хоста, необходимо использовать DNS для разрешения имени хоста, а для некоторых сетевых подключений это может занять некоторое время. Из-за этого API будет возвращать NotReachable до завершения разрешения имени. Эта задержка может быть видна в интерфейсе некоторых сетей.

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

person yjw    schedule 12.06.2011

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

[hostReach connectionRequired];

до

[internetReachable startNotifier];

Для решения этой проблемы.

runmad ответил на эту проблему здесь: https://stackoverflow.com/a/2157858/623260

person smoothdvd    schedule 19.02.2012

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

person W Dyson    schedule 23.01.2011
comment
AlertView отображается несколько раз, потому что checkNetworkStatus вызывается несколько раз подряд. Вам нужно найти другие вызовы этого метода и удалить их, чтобы он вызывал только один раз. - person W Dyson; 23.01.2011
comment
Я не понимаю, я только один раз ставил checkNetworkStatus в NSNotificationCenter ... - person Mausimo; 23.01.2011
comment
Я не понимаю, почему он это делает, это что-то в Reachability, где он отправляет уведомление 3 раза. Я бы предположил, что в трех разных местах я давно не смотрел класс. Кто-нибудь знает, почему он это делает? Я считаю, что решение состоит в том, чтобы добавить в проект Reachability и удалить два нежелательных вызова. - person W Dyson; 23.01.2011
comment
Хм, все равно не имеет смысла. При повторном подключении я несколько раз получаю сообщение «Недоступен». Это похоже на то, как только устройство повторно подключается, оно начинает многократно вызывать checkNetworkStatus. - person Mausimo; 24.01.2011

Вы можете отказаться от подписки на измененное уведомление NSNotificationCenter removeObserver ... пока вы обрабатываете его в обратном вызове. Перед возвращением снова добавьте наблюдателя.

person danh    schedule 28.01.2012

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

добавить класс 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

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

#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;
    }
 }
}
person sinh99    schedule 26.10.2012
comment
SCNetworkReachabilitySetCallback теперь, по-видимому, имеет другое поведение в iOS 8, где обратный вызов выполняется не раньше, чем через некоторое время, а не сразу после разрыва связи. - person cynistersix; 21.10.2014