2016-04-24 87 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