unterscheidung ppc / intel

  • unterscheidung ppc / intel

    hallo leute,

    ich bin das erstemal auf ein ppc / intel problem gestoßen.
    dieser code gibt mir die ip adresse eines nsnetservice zurück:

    Quellcode

    1. - (NSString*) address: (NSNetService*) service
    2. {
    3. int firstOctet, secondOctet, thirdOctet, fourthOctet;
    4. NSData *address = nil;
    5. struct sockaddr_in *socketAddress;
    6. NSString *ipAddressString = nil;
    7. NSLog(@"addresses: %@",[service addresses]);
    8. address = [[service addresses] objectAtIndex:0];
    9. socketAddress = (struct sockaddr_in *)[address bytes];
    10. firstOctet = (socketAddress->sin_addr.s_addr & 0xFF000000) >> 24;
    11. secondOctet = (socketAddress->sin_addr.s_addr & 0x00FF0000) >> 16;
    12. thirdOctet = (socketAddress->sin_addr.s_addr & 0x0000FF00) >> 8;
    13. fourthOctet = (socketAddress->sin_addr.s_addr & 0x000000FF) >> 0;
    14. // PPC code
    15. ipAddressString = [NSString stringWithFormat:@"%d.%d.%d.%d", firstOctet, secondOctet, thirdOctet, fourthOctet];
    16. // Intel Code
    17. ipAddressString = [NSString stringWithFormat:@"%d.%d.%d.%d", fourthOctet, thirdOctet, secondOctet, firstOctet];
    18. NSLog(@"hostName: %@",ipAddressString);
    19. return ipAddressString;
    20. }
    Alles anzeigen


    interessant sind zeile 17 und 20. je nach platform muss die ip anders zusammen gebaut werden.

    wie kann ich jetzt herausfinden auf welcher plattform mein programm läuft?


    sascha
  • RE: unterscheidung ppc / intel

    Du kannst entweder diese Funktion benutzen:
    CFByteOrderGetCurrent()
    und gegen diese Typen pruefen: CFByteOrder
    Schau mal in der Doku danach.

    Es gibt meines Wissens, es hat hier schonmal jemand geposted, auch Defines, in NSApplication, schau mal in NSApplication.h, dort muessetn sie definiert sein. Auch was mit INTEL und PPC bzw. Little und Big endian.

    Manfred
  • Re: unterscheidung ppc / intel

    Hallo macuser,

    Es gibt eine umständliche und eine echt "tricky" Lösung ;) - Die Tricky verrate ich ganz unten um es spannend zu machen :)

    Also es gibt dazu eig. mehrere Lösungen:

    (Einfach mal Byte-Swapping Routines (Universal Binary Guidelines) anschauen.

    1) NSSwapBigLongToHost ist Dein Freund :)

    ...oder eben die CoreFoundation-Lösung, die Michael oben vorschlägt... :)

    2) Oder Du verwendest einfach die eigentlich dafür vorgesehenen calls ntohl() (Network_to_Host_Long) bzw. htonl (Host_To_Network_Long).

    WICHTIG: Das ist eine Lösung die auch unter portierbar ist. Die beiden Calls sorgen schon dafür, dass es dann aud Deinem Host passt (Auf i386(little Endian) wird geswapped und auf PowerPC(big Endian) nicht). Deswegen preferiere ich persönlich die ntonl(), ntohl(), htons(), ntohs(). Lässt sich auch unter Linux, BSD, Solaris, Windows... compilieren ;)

    Background: Im Netzwerk wird *immer* Big Endian verwendet.

    Du musst als je nach Architektur die Bytes drehen, wenn Du sie so wie gewünscht repräsentieren willst.

    Und die oben gennaten Calls machen das für Dich. Eigentlich machen die auf PowerPC nichts, denn der ist ja schon ne Big Endian Architektur ;)

    Da wir hier aber in einem OS-X Forum sind, zeige ich das mal erst anhand NSSwapBigLongToHost. Danach mit ntohl()

    1) Anwendung von NSSwapBigLongToHost:

    Quellcode

    1. - (NSString*) address: (NSNetService*) service
    2. {
    3. uint8_t firstOctet, secondOctet, thirdOctet, fourthOctet;
    4. NSData *address = nil;
    5. struct sockaddr_in *socketAddress;
    6. NSString *ipAddressString = nil;
    7. NSLog(@"addresses: %@",[service addresses]);
    8. address = [[service addresses] objectAtIndex:0];
    9. socketAddress = (struct sockaddr_in *)[address bytes];
    10. in_addr_t netAddress = socketAddress->sin_addr.s_addr;
    11. in_addr_t hostAddress = NSSwapBigLongToHost(netAddress);
    12. firstOctet = (hostAddress & 0xFF000000) >> 24;
    13. secondOctet = (hostAddress & 0x00FF0000) >> 16;
    14. thirdOctet = (hostAddress & 0x0000FF00) >> 8;
    15. fourthOctet = (hostAddress & 0x000000FF) >> 0;
    16. ipAddressString = [NSString stringWithFormat:@"%d.%d.%d.%d", firstOctet, secondOctet, thirdOctet, fourthOctet];
    17. NSLog(@"hostName: %@",ipAddressString);
    18. return ipAddressString;
    19. }
    Alles anzeigen


    2) Anwendung von htonl() (erfordert #import <arpa/inet.h>):

    Quellcode

    1. #include <arpa/inet.h>
    2. ...
    3. - (NSString*) address: (NSNetService*) service
    4. {
    5. ...
    6. // Selber Code wie oben, nur schreibst Du statt NSSwapBigLongToHost() eben ntohl()
    7. // also statt: NSSwapBigLongToHost(netAddress);
    8. in_addr_t hostAddress = ntohl(netAddress);
    9. ...
    10. }



    Und nun noch zu der echt "tricky" Lösung, die zudem auch prima portabel ist :)

    Es gibt nämlich unter #include <arpa/inet.h> einige leckere Prototypes von Funktionen!

    Damit sparst Du Dir die ganze Endian-Anpasserei und auch das Isolieren der Octets durch das Schieben! Die Funktion kümmert sich bereits um die korrekte Endianess auf dem Host! :)

    Darunter ist Eine (inet_ntoa()) die es bereits erlaubt, einen inet_addr in einen C-String auszugeben!

    Also einfach mal in /usr/include/arpa/inet.h reinschauen, oder mal man inet_ntoa eingeben...

    Also, hier die "tricky" Lösung:

    Quellcode

    1. #include <arpa/inet.h>
    2. ...
    3. - (NSString*) address: (NSNetService*) service
    4. {
    5. NSData *address = nil;
    6. struct sockaddr_in *socketAddress;
    7. NSLog(@"addresses: %@",[service addresses]);
    8. address = [[service addresses] objectAtIndex:0];
    9. socketAddress = (struct sockaddr_in *)[address bytes];
    10. NSString *ipAddressString = [NSString stringWithCString:inet_ntoa(socketAddress->sin_addr)];
    11. NSLog(@"hostName: %@",ipAddressString);
    12. return ipAddressString;
    13. }
    Alles anzeigen


    Happy Codin'

    - James -
  • Hallo Sascha,

    Klasse! :)

    Noch was - habe ich gerade in der Doku (man inet_ntoa) gelesen:
    ... The string returned by inet_ntoa() resides in a static memory area. ...


    Klingeling! Etwas in statischen Speichern zurückzugeben ist "evil" 8o

    Wenn man das Ganze dann vielleicht mal in einem mulithreaded Kontext laufen lässt, dann muss man tunlichst drauf achten, den Zugriff durch Mutexes exklusiv zu schützen... Kurzum - die Sache ist nicht reentrant und kann Dir dann u.U. üblen Ärger verursachen!

    Es gibt aber eine Lösung - das man-File verrät es - verwende bitte addr2ascii() der als Ersatz des oben gennannten Calls gedacht ist!

    Dem kann man nämlich einen Buffer mitgeben, in dem dann der String Platz findet. Damit wird nichts mehr in einer statischen Speicher hinterlegt!

    Damit ist die Sache auch reentrant (=Thread-Safe)!

    Also, würdest Du bitte die Zeile mit dem inet_ntoa() in ein addr2ascii() umwandeln:

    Bitte schreibe statt:

    Quellcode

    1. NSString *ipAddressString = [NSString stringWithCString:inet_ntoa(socketAddress->sin_addr)];


    besser:

    Quellcode

    1. char buffer[32];
    2. NSString *ipAddressString = [NSString stringWithCString:
    3. addr2ascii(AF_INET, ((in_addr*)hostent->h_addr), sizeof(in_addr_t), buffer)];


    Übrigens - mit der "Portierbarkeit" zu o.g. Call sieht es wohl doch nicht soooo rosig aus. Zumindest steht in "man inet_ntoa":

    ... The inet_aton() and inet_ntoa() functions are semi-deprecated in favor of the addr2ascii(3) family. However, since those functions are not yet widely implemented, portable programs cannot rely on their presence and will continue to use the inet(3) functions for some time. ...


    ...Aber hauptsache es funktioniert und ich denke die Lösung ist auch sauber :)

    Schöne Grüsse - James -
  • Original von macuser
    nö das is nich passiert. hab den code so copiert wie er dasteht.

    Ok, dann muss es das hier sein:

    Quellcode

    1. char buffer[32];
    2. NSString *ipAddressString = [NSString stringWithCString:
    3. addr2ascii(AF_INET, ((in_addr_t*)hostent->h_addr), sizeof(in_addr_t), buffer)];
    4. ^^
    Da, wo ich in der Zeile vier die beiden Marker gesetzt habe, fehlt wohl bereits im Original von James das "_t".

    Michael
  • Original von Michael
    Original von macuser
    nö das is nich passiert. hab den code so copiert wie er dasteht.

    Ok, dann muss es das hier sein:

    Quellcode

    1. char buffer[32];
    2. NSString *ipAddressString = [NSString stringWithCString:
    3. addr2ascii(AF_INET, ((in_addr_t*)hostent->h_addr), sizeof(in_addr_t), buffer)];
    4. ^^
    Da, wo ich in der Zeile vier die beiden Marker gesetzt habe, fehlt wohl bereits im Original von James das "_t".

    Michael


    Nein - Der code passt schon so - wenn man ihn als Objective C++ (PLUS PLUS!) übersetzt *hüstel* :D

    Ich habe meinem Testfile als Gewohnheitstier die Fileendung .mm verpasst.

    Es ist nämlich eine Eigenschaft von C++ / Obj-C++, dass man bei "struct type" das struct weglassen kann, denn "type" ist bei C++ schon typisiert ;)

    Also einfach oben schreiben:

    Quellcode

    1. addr2ascii(AF_INET, ((struct in_addr*)hostent->h_addr), sizeof(struct in_addr), buffer)];


    Dann passt es immer!

    Sorry für die Umstände! - James -
  • RE: unterscheidung ppc / intel

    Original von James
    Es gibt nämlich unter #include <arpa/inet.h> einige leckere Prototypes von Funktionen!
    Damit sparst Du Dir die ganze Endian-Anpasserei und auch das Isolieren der Octets durch das Schieben! Die Funktion kümmert sich bereits um die korrekte Endianess auf dem Host! :)
    Darunter ist Eine (inet_ntoa()) die es bereits erlaubt, einen inet_addr in einen C-String auszugeben!


    Sag ich doch. Da soll Tom noch mal behaupten, in meinem Buch stünden zu viele überflüssige C-Funktionen drin. Grummel! ;)
  • Original von macuser
    ich glaube die umstände mach eher ich hier :)
    denn es geht noch nicht, ich bekomm wieder einen error:

    Quellcode

    1. error: 'hostent' undeclared (first use in this function)
    2. confused by earlier errors, bailing out

    die zweite zeile find ich da besonders interessant :)


    Maunz!? 8o

    Jau, (schon wieder) meine Schuld! *grummel* - So geht's wenn man zu viel ausprobiert... -> Hab das falsche Snippet aus meinem Testprogramm kopiert... ;) Oh die knapp 40 merke ich jetzt - oder liegt's am Fasching? :rolleyes:

    Anyway - Richtig ist (mal die ganze Methode):

    Quellcode

    1. - (NSString*) address: (NSNetService*) service
    2. {
    3. NSData *address = nil;
    4. struct sockaddr_in *socketAddress;
    5. NSLog(@"addresses: %@",[service addresses]);
    6. address = [[service addresses] objectAtIndex:0];
    7. socketAddress = (struct sockaddr_in *)[address bytes];
    8. // replacement of : NSString *ipAddressString = [NSString stringWithCString:inet_ntoa(socketAddress->sin_addr)];
    9. char buffer[32];
    10. NSString *ipAddressString = [NSString stringWithCString:
    11. addr2ascii(AF_INET, (&socketAddress->sin_addr), sizeof(struct in_addr), buffer)];
    12. NSLog(@"hostName: %@",ipAddressString);
    13. return ipAddressString;
    14. }
    Alles anzeigen


    Sorry und happy Codin' - James -
  • Eine kleine Frage zum Verständnis:

    Quellcode

    1. socketAddress = (struct sockaddr_in *)[address bytes];


    Woher ist zu entnehmen, dass [address bytes] den Datentyp von struct sockaddr_in hat?

    Ah, sorry, habe diesen Satz überlesen:

    Quellcode

    1. Each NSData object in the returned array contains an appropriate sockaddr structure that you can use to connect to the socket. The exact type of this structure depends on the service to which you are connecting.


    @macuser:

    Dabei sollst du sicherheitshalber auch überprüfen, ob im addresses gleich oder mehr als ein Objekt enthält. Das Programm kann abstürzen, wenn kein Objekt in diesem Array enthält.
    Aus macfreakz wurde Apfelbeisser …