2016-04-24 10 views
3

Derzeit stellt das folgende Programm nur mithilfe einer IPv4-Adresse eine Verbindung her. Ich möchte, dass es geändert wird, um eine Verbindung zu einem Server herzustellen (kompatibel zum Akzeptieren von IPv4- und IPv6-Clients), indem Sie eine der IPv6- oder IPv4-Adressen des Servers verwenden.Möchten Sie, dass sich mein Clientprogramm mit IPv4 oder IPv6 verbindet?

  #include <stdio.h> 
      #include <stdlib.h> 
      #include <string.h> 
      #include <sys/types.h> 
      #include <sys/socket.h> 
      #include <netinet/in.h> 
      #include <arpa/inet.h> 

      #include "common.h" 
      #include "client.h" 



      int 
      CreateClientTCP(const char *svrHost, 
          unsigned short svrPort, 
          char *svrName, 
          int svrNameLen) 
      { 
       int sock; 
       struct sockaddr_in svrAddr; 

       sock = socket(AF_INET, SOCK_STREAM, 0); 
       if (sock < 0) { 
        perror("Failed to allocate the client socket"); 
        exit(EXIT_FAILURE); 
       } 

       memset(&svrAddr, 0, sizeof(svrAddr)); 
       svrAddr.sin_family = AF_INET; 
       svrAddr.sin_port = htons(svrPort); 

       if (inet_pton(AF_INET, svrHost, &svrAddr.sin_addr.s_addr) <= 0) { 
        perror("Failed to convert IP address\n"); 
        exit(EXIT_FAILURE); 
       } 

       SocketAddrToString(&svrAddr, svrName, svrNameLen); 
       Log("Attempting %s\n", svrName); 

       if (connect(sock, (struct sockaddr *)&svrAddr, sizeof(svrAddr)) < 0) { 
        perror("Failed to connect to the server"); 
        exit(EXIT_FAILURE); 
       } 

       return sock; 
      } 



      int 
      main(int argc, char *argv[]) 
      { 
       int sock; 
       ClientArgs cliArgs; 
       char svrName[INET_ADDRSTRLEN + PORT_STRLEN]; 

       ParseArgs(argc, argv, &cliArgs); 

       sock = CreateClientTCP(cliArgs.svrHost, cliArgs.svrPort, 
             svrName, sizeof svrName); 

       Log("Connected to server at %s\n", svrName); 

       Client(sock, &cliArgs); 

       close(sock); 
       Log("\nDisconnected from server at %s\n", svrName); 
       return 0; 
      } 

Antwort

1

Sie müssen Funktionen verwenden, die sowohl IPv6- und IPv4-zum Beispiel unterstützt, verwenden inet_pton nicht, verwenden getaddrinfo sondern kann Adressen der beiden Protokollversionen analysieren, und erzählen Sie Ihren die richtige Familie zu verwenden.

In allen folgenden Netzwerkaufrufen sollten Sie die von getaddrinfo zurückgegebene Familie verwenden, anstatt sie an AF_INET, z. sock = socket(addr->ai_family, ...).

Lesen Sie zusätzlich dieses IPV6-Intro, es ist ein sehr guter Start.

https://www.akkadia.org/drepper/userapi-ipv6.html

Für bestimmte Änderungen in Ihrem Code, ich glaube, Sie diese 3 Plätze für einen Start ändern müssen:

// struct sockaddr_in svrAddr; <-- sockaddr_in is for ipv4 address 
struct sockaddr_storage svrAddr; 

// inet_pton(AF_INET, svrHost, &svrAddr.sin_addr.s_addr) <-- IPV4 only 
struct addrinfo *res; 
getaddrinfo(svrHost, NULL, &hint, &res); 

// sock = socket(AF_INET, SOCK_STREAM, 0); <-- IPV4 only 
sock = socket(result->ai_family, SOCK_STREAM, 0); 
+0

Was wären Änderungen innerhalb des Programms erforderlich? Muss ich einen separaten Client codieren? – ramnarayanan

+0

@ramnarayanan Nein, der Zweck besteht darin, einen Client zu verwenden, um beide Versionen zu unterstützen. – fluter

+0

Es gibt einen Strom von Fehlern, die ich teilen möchte. @fluter – ramnarayanan

2

Neben fluter Antwort Sie die Struktur Ihres Codes ein ändern müssen Bit.

Anstatt einen Socket zu erstellen und dann die Adresse nachzuschlagen, müssen Sie getaddrinfo aufrufen, um alle Adressen für den angegebenen Hostnamen zu erhalten. Hostnamen sind oft doppelt gestapelt, sodass Sie mehrere IPv4- und/oder IPv6-Adressen zurück erhalten. Sie werden normalerweise in der Reihenfolge sortiert, in der Sie sie ausprobieren sollten. Übergeben Sie alle Adressen einzeln nacheinander, bis die Verbindung erfolgreich ist.

Da einige der Adressen IPv4 sind und einige IPv6, können Sie keinen Socket erstellen. Für jede Adresse, die Sie versuchen, sollten Sie einen neuen Socket erstellen, der zur Adressfamilie der Adresse gehört, mit der Sie sich verbinden möchten. Fluter hat Ihnen bereits gezeigt, wie.

Sobald Ihre Verbindung erfolgreich ist, unterbrechen Sie die Schleife und verwenden die eingerichtete Verbindung.

+1

Bei einigen Plattformen können Sie einen IPv6-Socket für die Kommunikation in beiden Protokollen verwenden. Aber ich bin mir nicht sicher, ob Code, der es so macht, portabel gemacht werden könnte. Es gibt jedoch andere Vorteile beim Erstellen eines separaten Sockets pro Adresse, da dies das parallele Ausprobieren von mehreren ermöglicht. – kasperd