C 服务器地址为FQDN时无法打开FTP连接

C 服务器地址为FQDN时无法打开FTP连接,c,sockets,networking,ftp,C,Sockets,Networking,Ftp,我用C编写的FTP程序只有在服务器地址是IP地址时才能工作。但当服务器地址为完全限定域名(FQDN)时,连接失败。 当ftp_主机为FQDN时,ftp连接无法打开。请提供帮助。我强烈建议使用getaddrinfo()函数,如下所示。这还有一个优点,即无论是现在还是以后,都可以很容易地转换为IPv6 我假设您只关心IPv4地址,下面是一个函数示例,它将主机名作为参数,并在中为您填写一个结构sockaddr\u: int get_ftp_addr(const char *hostname, stru

我用C编写的FTP程序只有在服务器地址是IP地址时才能工作。但当服务器地址为完全限定域名(FQDN)时,连接失败。
当ftp_主机为FQDN时,ftp连接无法打开。请提供帮助。

我强烈建议使用
getaddrinfo()
函数,如下所示。这还有一个优点,即无论是现在还是以后,都可以很容易地转换为IPv6

我假设您只关心IPv4地址,下面是一个函数示例,它将主机名作为参数,并在中为您填写一个
结构sockaddr\u:

int get_ftp_addr(const char *hostname, struct sockaddr_in *addr)
{
  char host_buffer[256];
  struct addrinfo hints;
  struct addrinfo *result;
  struct sockaddr_in *res_addr;
  int error = -1;
  char *colon;

  snprintf(host_buffer, sizeof(host_buffer), "%s", hostname);

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;

  colon = strchr(host_buffer, ':');
  if (colon) {
    *colon = '\0';
    error = getaddrinfo(host_buffer, colon + 1, &hints, &result);
  } else {
    error = getaddrinfo(host_buffer, "ftp", &hints, &result);
  }

  if (error != 0 || !result) {
    return error;
  }

  res_addr = (struct sockaddr_in*)(result->ai_addr);
  memcpy(addr, res_addr, sizeof(struct sockaddr_in));

  freeaddrinfo(result);
  return 0;
}
请注意,我只获取字符串的一个副本,以避免修改调用者中的版本-如果您不想保留该部分,则不必保留该部分,但就我个人而言,我认为它使界面更清晰

此函数将接受虚线四元表示法中的IP地址和完全限定的主机名,因为
getaddrinfo()
同时接受这两种名称。如果使用冒号指定端口,则将使用该端口,否则将使用默认FTP端口

返回值为零表示成功。可以将正返回传递到
gai_strerror()
以获取字符串错误代码,也可以检查可能的错误代码。返回
-1
表示来自
getaddrinfo()
的成功结果,但没有结果结构-我认为这不会发生,但我不希望留下任何可能的返回代码不处理

这里有几个注意事项,最重要的两个是:

  • 这段代码目前只支持IPv4,尽管
    getaddrinfo()
    也使支持IPv6变得非常容易。如果您想同时支持这两种功能,那么将提示结构中的
    AF_INET
    更改为
    AF_unsec
    ,您将获得所有地址族。不过,您需要迭代地址,并只过滤掉IPv4和IPv6地址(请参阅我的下一点)
  • DNS查找可能会查找多个IP地址-这在大型站点(如Google)中非常常见,因为它们使用此功能在主机之间进行负载平衡,并实现冗余。理想情况下,您的代码应该遍历所有返回的地址,并尝试连接到每个地址,直到其中一个工作。对于FTP客户机来说,这可能有些过分,但我认为意识到这一点很重要
如果您想支持IPv6,或支持多A记录(即从DNS查询返回多个地址),则需要遵循
struct addrinfo
结构中的
ai_next
指针-类似以下内容:

struct addrinfo *res;

/* Assume result is initialised as above via getaddrinfo() */

for (res = result; res != NULL; res = res->ai_next) {
    ...
}