问题描述
我在sockaddr
上具有以下扩展名:
I have the following extension on 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则有些不可靠.
In an other part of my code I'm calling getifaddrs
to get the interface addresses of the current device. The code above works fine for IPv4, but is somewhat unreliable for IPv6.
我得到如下结果:192.168.1.10
和fe80::e0fa:1204:100:0
当我将行var result: String = ""
更改为var result: String? = nil
时. IPv6地址突然变为fe80::
,其余部分被切断.
When I change the line var result: String = ""
to var result: String? = nil
. The IPv6 addresses suddenly become fe80::
, the rest is cut off.
甚至很奇怪,当我像这样切换var result
和var me = self
行时:
Even weirder, when I just switch the var result
and the var me = self
lines like this:
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(失败).
Then the function will only work for IPv4 addresses. The getnameinfo
will return 4 (FAIL).
这是在调试过程中,没有我所知道的优化.无论是在模拟器上还是在真实设备上运行,都没关系.
This is during debugging, with no optimizations that I know of. It doesn't matter if I run it on a simulator or real device.
有人可以解释为什么会这样吗?
Could someone please explain why this is happening?
推荐答案
问题是getnameinfo
需要一个可以为sockaddr_in
或sockaddr_in6
的指针.该函数的定义有点令人困惑,因为它需要一个sockaddr
指针.
The problem is that getnameinfo
expects a pointer that can either be a sockaddr_in
or a sockaddr_in6
. The definition of the function is a bit confusing because it expects a sockaddr
pointer.
因为我正在使用扩展名提取IP地址,因此正在复制内存内容.对于IPv4而言,这不是问题,因为sockaddr_in
的大小与sockaddr
的大小相同.但是,对于IPv6,sockaddr_in6
大于sockaddr
结构,并且一些相关信息被切除.
Because I'm using an extension to extract the IP address a copy is being made of the memory contents. This isn't a problem for IPv4 because the size of a sockaddr_in
is the same size as a sockaddr
. However for IPv6, the sockaddr_in6
is larger than the sockaddr
struct and some relevant information is cut off.
我的命令顺序可能决定了sockaddr
地址之后的位置中存储在内存中的内容.有时看起来像是正确的IPv6地址,但实际上是错误的.
The order of my commands probably determined what was stored in memory at the location directly after the sockaddr
address. Sometimes it would look like a proper IPv6 address, but in reality incorrect.
我已通过将扩展程序移至网络接口ifaddrs
解决了此问题:
I've resolved this issue by moving my extension to the network interface 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找到问题的原因!
Thank you @MartinR finding the cause of the problem!
这篇关于Swift getnameinfo的IPv6结果不可靠的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!