2016-04-24 73 views
3

目前,以下程序僅使用IPv4地址進行連接。 我希望將它修改爲使用服務器的任何IPv6或IPv4地址連接到服務器(兼容接受IPv4和IPv6客戶端)。希望我的客戶端程序通過IPv4或IPv6連接

  #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; 
      } 

回答

1

您需要使用支持IPv6和IPv4,例如功能,不使用inet_pton,使用getaddrinfo相反,它可以分析這兩個協議版本的地址,並告訴您正確的家庭使用。

在所有後續的網絡調用中,您應該使用由getaddrinfo返回的系列,而不是將其硬編碼爲AF_INET,例如, sock = socket(addr->ai_family, ...)

此外,閱讀這個IPV6介紹,這是一個非常好的開始。

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

代碼中的具體變化,我認爲你需要改變這3個地方的一個開始:

// 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

程序中需要做什麼修改? 我是否需要編寫一個單獨的客戶端? – ramnarayanan

+0

@ramnarayanan否,目的是使用一個客戶端來支持這兩個版本。 – fluter

+0

我想分享一些錯誤。 @fluter – ramnarayanan

2

除了fluter的答案,你需要改變你的代碼的結構位。

除了創建套接字,然後查找地址,您需要調用getaddrinfo來獲取給定主機名的所有地址。主機名通常是雙重堆疊的,所以你會得到多個IPv4和/或IPv6地址。他們通常按您應該嘗試的順序排序。所以循環所有的地址一個接一個,直到連接成功。

由於某些地址是IPv4,有些地址是IPv6,因此無法預先創建套接字。對於您嘗試的每個地址,應該創建一個屬於您嘗試連接的地址的地址系列的新套接字。弗萊特已經告訴你如何。

一旦連接成功,您將斷開循環並使用已建立的連接。

+1

某些平臺將允許您使用IPv6套接字在兩種協議上進行通信。但我不確定這樣做的代碼可以變得便攜。然而,爲每個地址創建一個單獨的套接字還有其他優點,因爲這樣可以並行地嘗試多個套接字。 – kasperd