2

我有以下扩展sockaddr

extension sockaddr {
  /// Indicates if this is an IPv4 address.
  var isIPv4: Bool {
    return sa_family == UInt8(AF_INET)
  }

  /// Indicates if this is an IPv6 address.
  var isIPv6: Bool {
    return sa_family == UInt8(AF_INET6)
  }

  /// Returns the address in string notation.
  var address: String? {
    var result: String = ""
    var me = self
    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))

    if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
       result = String(cString: hostname)
    }

    return result
  }
}

在我的代码的另一部分中,我正在调用getifaddrs以获取当前设备的接口地址。上面的代码适用于 IPv4,但对于 IPv6 来说有些不可靠。

我得到如下结果:192.168.1.10fe80::e0fa:1204:100:0

当我将线路更改var result: String = ""var result: String? = nil. IPv6地址突然变成fe80::了,剩下的就被切断了。

更奇怪的是,当我像这样切换var result和行时var me = self

extension sockaddr {
  /// Indicates if this is an IPv4 address.
  var isIPv4: Bool {
    return sa_family == UInt8(AF_INET)
  }

  /// Indicates if this is an IPv6 address.
  var isIPv6: Bool {
    return sa_family == UInt8(AF_INET6)
  }

  /// Returns the address in string notation.
  var address: String? {
    var me = self
    var result: String = ""
    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))

    if getnameinfo(&me, socklen_t(me.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0 {
       result = String(cString: hostname)
    }

    return result
  }
}

然后该功能将仅适用于 IPv4 地址。将getnameinfo返回 4(失败)。

这是在调试期间,没有我知道的优化。如果我在模拟器或真实设备上运行它并不重要。

有人可以解释为什么会这样吗?

4

1 回答 1

1

问题是getnameinfo需要一个可以是 asockaddr_in或 a的指针sockaddr_in6。该函数的定义有点混乱,因为它需要一个sockaddr指针。

因为我正在使用扩展来提取 IP 地址,所以正在制作内存内容的副本。这对 IPv4 来说不是问题,因为 a 的大小与 asockaddr_in的大小相同sockaddr。但是对于 IPv6,sockaddr_in6它比sockaddr结构大,并且一些相关信息被截断。

我的命令的顺序可能决定了在sockaddr地址之后的位置存储在内存中的内容。有时它看起来像一个正确的 IPv6 地址,但实际上是不正确的。

我通过将我的扩展移动到网络接口解决了这个问题ifaddrs

extension ifaddrs {
  /// Returns the IP address.
  var ipAddress: String? {
    var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    let address = ifa_addr.pointee
    let result = getnameinfo(ifa_addr, socklen_t(address.sa_len), &buffer, socklen_t(buffer.count), nil, socklen_t(0), NI_NUMERICHOST)
    return result == 0 ? String(cString: buffer) : nil
  }
}

感谢@MartinR 找到问题的原因!

于 2017-06-11T01:12:50.127 回答