2012-07-17 7 views
15

Muszę być w stanie wykryć zmianę adresu IP dla mojego klienta Mac. Muszę wykonać akcję za każdym razem, gdy dostanę nową, kiedy przejdę z wifi do przewodowej ...Jak programowo wykryć zmianę adresu IP na OSX w C lub C++

Ktoś zrobił coś podobnego? Obecnie odpytywam co minutę i muszę to zmienić, aby zwiększyć liczbę zdarzeń.

+0

w bardziej ogólnym horyzoncie, moje pytanie powinno być naprawdę: w OS X Jak wykonać akcję podczas zmiany adresu IP? – reza

+0

Jeśli masz zamiar wykryć zmiany domyślnego adresu IP, abyś mógł wykonać pewne działanie (np. Wysłać nowy adres do jakiegoś serwera), gotowe.Jeśli jest to coś innego, powiedz nam, czym jest to coś innego, zamiast wielokrotnie zgadywać na następnym etapie, w kierunku tego, co chcesz osiągnąć. (Najlepiej, aby zamiast tego zmienić nowe pytanie, ponieważ w obecnej formie jest to dobre pytanie, z tym, co mam nadzieję, jest całkiem dobrą odpowiedzią, nawet jeśli w rzeczywistości nie jest to twoje pytanie). – abarnert

Odpowiedz

22

Istnieje wiele sposobów, aby to zrobić, od powiadomień IOKit na górze, ale najprostsza jest prawdopodobnie struktura SystemConfiguration.

Pierwszym krokiem jest odpalić scutil i bawić się z nim, aby dowiedzieć się, który klucz (y) chcesz powiadomienia o:

$ scutil 
> list 
... 
> n.add State:/Network/Global/IPv4 
> n.watch 
... unplug your network cable (or disconnect from WiFi) 
notification callback (store address = 0x10e80e3c0). 
changed key [0] = State:/Network/Global/IPv4 

Spójrz na to, mam to na pierwszej próbie. :) Ale jeśli chcesz oglądać konkretną kartę sieciową lub używać IPv6 zamiast v4 itp., Oczywiście będziesz potrzebował innego klucza z listy. Zauważ, że możesz używać wzorów regex (styl POSIX, zgodnie z definicją man 3 regex), więc jeśli chcesz oglądać, powiedzmy, dowolną kartę NIC dla IPv4, możesz użyć State:/Network/Interface/.*/IPv4, lub jeśli chcesz powiedzieć globalne IPv4 lub IPv6, State:/Network/Global/IPv. itd.

Teraz wystarczy wywołać SCDynamicStoreSetNotificationKeys za pomocą odpowiednich klawiszy.

Zauważ, że SCDynamicStoreSetNotificationKeys może regex wzorów (stylu POSIX, jak określono Man 3 regex)

Ponieważ jest to trochę bolesne w C, będę pisać w Pythonie:

#!/usr/bin/python 

from Foundation import * 
from SystemConfiguration import * 

def callback(store, keys, info): 
    for key in keys: 
    print key, SCDynamicStoreCopyValue(store, key) 

store = SCDynamicStoreCreate(None, 
          "global-network-watcher", 
          callback, 
          None) 
SCDynamicStoreSetNotificationKeys(store, 
            None, 
            ['State:/Network/Global/IPv4']) 
CFRunLoopAddSource(CFRunLoopGetCurrent(), 
        SCDynamicStoreCreateRunLoopSource(None, store, 0), 
        kCFRunLoopCommonModes) 
CFRunLoopRun() 

Głównym powodem, dla którego jest to bardziej bolesne w C, jest to, że potrzebujesz kilkudziesięciu linii standardowego elementu dla takich rzeczy jak tworzenie CFArray z CFString w nim, drukowanie wartości CFString, zarządzanie czasem życia obiektów, itp. Z komentarza Jeremy'ego Friesnera, jest C++ sample code dostępny jeśli wolisz czytać 113 linii C++ niż 17 linii Pythona. Ale tak naprawdę istnieje tylko jedna linia tutaj, że powinien być zaznajomiony z kimś, kto nigdy nie używane Python:

def callback(store, keys, info): 
    for key in keys: 
    print key, SCDynamicStoreCopyValue(store, key) 

... jest równoważna definicji C:

void callback(SCDynamicStoreRef store, CFArrayRef keys, void *info) { 
    /* iterate over keys, printing something for each one */ 
} 

dziwne, nie mogę znaleźć aktualna dokumentacja lub przewodnik po SystemConfiguration już; jedyną rzeczą, która pojawia się w przypadku SCDynamicStoreSetNotificationKeys lub funkcji pokrewnych, jest sekcja Nawigowanie zaporami ogniowymi pod numerem CFNetwork Programming Guide. Ale oryginalny symbol TN1145: Living in a Dynamic TCP/IP Environment nadal istnieje i ma wystarczającą ilość tła i kodu przykładowego, aby dowiedzieć się, jak napisać to sam (i jak wykryć nowe adresy IP po otrzymaniu powiadomienia).

Oczywiście wymaga to wiedzy o tym, co dokładnie chcesz oglądać. Jeśli tego nie wiesz, nikt nie może ci powiedzieć, jak go oglądać. Pierwotne pytanie brzmiało, jak "wykryć zmianę adresu IP".

Powyższy kod pozwoli wykryć zmiany adresu domyślnego. To jest adres, który zostanie użyty po podłączeniu gniazda do adresu internetowego bez wiązania go lub powiązania gniazda z "0.0.0.0", aby działał jako serwer internetowy. Jeśli nie napisałeś kodu serwera, który Cię interesuje, prawie wszyscy klienci sieciowi robią to pierwsze, a większość serwerów robi to drugie, chyba że je skonfigurujesz inaczej, więc to chyba wszystko, na czym ci zależy.

Teraz chodźmy poprzez przykłady w swoich komentarzach, jeden po drugim:

jeśli próbuję określić zmianę sieci, wifi do LAN

Nie ma czegoś takiego, jak zmienia się od WiFi do sieci LAN. Gdy łączysz się z siecią LAN, WiFi nadal działa. Oczywiście możesz go ręcznie wyłączyć przed lub po podłączeniu do sieci LAN, ale nie musisz, i to jest osobny krok, z osobnym powiadomieniem.

Zazwyczaj dodanie sieci LAN spowoduje zmianę domyślnego adresu na adres sieci LAN, dzięki czemu /Network/Global powiadomi Cię o tym. Jeśli system operacyjny może powiedzieć, że sieć LAN nie jest faktycznie podłączona do Internetu lub zmieniłeś niektóre ukryte ustawienia, aby preferować połączenie Wi-Fi z siecią LAN itp., Nie zmieni to adresu domyślnego, a /Network/Global nie powiadomi Cię, ale pewnie cię to nie obchodzi.

Jeśli zależy Ci na tym, czy dany interfejs otrzymuje, traci lub zmienia adres, możesz go obejrzeć. Na większości komputerów Mac wbudowany Ethernet to en0, a wbudowane WiFi to en1, ale oczywiście możesz mieć zewnętrzne złącze USB WiFi lub możesz korzystać z telefonu komórkowego na uwięzi, lub możesz być zainteresowany nie tyle w rzeczywistym adresie IP sieci LAN, ile w adresie VPN sieci VPN, z którą połączona jest sieć LAN itp. Jeśli piszesz coś specjalnego, prawdopodobnie wiesz, na którym interfejsie Ci zależy, abyś mógł oglądać, np. State:/Network/Interface/en0/IPv4. Jeśli chcesz otrzymywać powiadomienia o zmianie interfejsu bez względu na wszystko, po prostu obejrzyj State:/Network/Interface/.*/IPv4.

lub hotspotu lub inny wifi

Zmiana z jednej sieci do drugiej WiFi (hotspot lub inaczej) zmieni en1-lub, jeśli używasz karty sieci bezprzewodowej innej firmy, niektóre inne berło. Jeśli Twój domyślny adres pochodzi z WiFi, zmieni się również Global, co oznacza, że ​​powyższy kod zadziała tak jak jest. Jeśli Twój domyślny adres nadal jest twoją siecią LAN, Global nie ulegnie zmianie, ale prawdopodobnie Cię to nie obchodzi. Jeśli ci na tym zależy, obejrzyj Interface/en1 lub Interface/.* itp., Jak powyżej.

co wszystkie ustawienia sieci powinien być oglądanie dla IPv4 i 6

Wystarczy zastąpić IPv4 z IPv6, lub użyć IPv.. Ale czy naprawdę zależy ci na IPv6?

co jeszcze

Co jeszcze obchodzi? Jeśli jest coś, o czym chcesz powiadomić, prawdopodobnie wiesz, co to jest.

Co więcej, jeśli system poinformuje cię, że adres foo w interfejsie pręta zmieniono na "ZZ9 Liczba mnoga Z Alpha", a nigdy nie słyszałeś o protokole foo, co byś mógł z pożytkiem zrobić z tymi informacjami? Ale jeśli naprawdę chcesz tego, znowu możesz po prostu użyć wzorca regex, aby obejrzeć cokolwiek pod każdym interfejsem.

+0

Concur - Byłem w połowie drogi przez napisanie czegoś podobnego - bez miłego krzyku o scutil. :) – Joe

+0

To jest fantastyczne, dziękuję. Nie czytam Pythona. Co to robi? – reza

+3

Istnieje przykładowy program w języku C/C++, który można uruchomić na stronie http://www.lcscanada.com/jaf/osx_ip_change_notify.cpp (uzyskany z kodu Uwaga Techniki firmy Apple) ... skompiluj go za pomocą "g ++ osx_ip_change_notify.cpp" framework SystemConfiguration-frame Carbon " –