posixgetaddrinfo
分配内存,稍后必须使用freeaddrinfo
释放内存。
见http://manpages.ubuntu.com/manpages/xenial/en/man3/getaddrinfo.3.html
为了简化API,我创建了以下函数:
import Foundation
enum SystemError: Swift.Error {
case getaddrinfo(Int32, Int32?)
}
public func getaddrinfo(node: String?, service: String?, hints: addrinfo?) throws -> [addrinfo] {
var err: Int32
var res: UnsafeMutablePointer<addrinfo>?
if var hints = hints {
err = getaddrinfo(node, service, &hints, &res)
} else {
err = getaddrinfo(node, service, nil, &res)
}
if err == EAI_SYSTEM {
throw SystemError.getaddrinfo(err, errno)
}
if err != 0 {
throw SystemError.getaddrinfo(err, nil)
}
defer {
freeaddrinfo(res)
}
var result = [addrinfo]()
var ai = res?.pointee
while ai != nil {
result.append(ai!)
ai = ai!.ai_next?.pointee
}
return result
}
不过,我觉得这个功能不正确。
Swift内存模型如何知道
getaddrinfo
分配内存,而Swift不应该用自己的东西覆盖该内存?Swift如何知道
freeaddrinfo
删除了整个列表,并且应该复制分配给结果数组的ai信息?与
getaddrinfo
接口的正确方式是什么? 最佳答案
由getaddrinfo
分配的内存(例如由malloc
分配)将不会提供给任何其他内存。
同一运行进程中的动态内存分配功能
直到由freeaddrinfo
发布(例如由free
发布)。
因此,swift运行时不会践踏该内存(如果我们
假设它没有编程错误,例如错误的指针计算)。
另外,struct addrinfo
是一个值类型,因此
result.append(ai!)
将向数组追加指向结构的副本。
但是仍然有一个问题。
struct addrinfo
是指针
public var ai_canonname: UnsafeMutablePointer<Int8>! /* canonical name for hostname */
public var ai_addr: UnsafeMutablePointer<sockaddr>! /* binary address */
它可能指向由
getaddrinfo
分配的内存,因此freeaddrinfo
之后无效,在您的函数返回导致未定义的行为。
因此,您必须将
freeaddrinfo
推迟到不再需要地址列表,或复制信息。
这有点麻烦,因为
ai_addr
可能指向具有不同长度的IPv4或IPv6套接字地址结构。
下面的代码演示如何复制地址列表
一组
sockaddr_storage
结构(足够大)保留任何IP地址)。此代码未经过彻底测试,
所以小心使用。
public func getaddrinfo(node: String, service: String, hints: addrinfo?) throws -> [sockaddr_storage] {
var err: Int32
var res: UnsafeMutablePointer<addrinfo>?
if var hints = hints {
err = getaddrinfo(node, service, &hints, &res)
} else {
err = getaddrinfo(node, service, nil, &res)
}
if err == EAI_SYSTEM {
throw SystemError.getaddrinfo(err, errno)
}
if err != 0 {
throw SystemError.getaddrinfo(err, nil)
}
defer {
freeaddrinfo(res)
}
guard let firstAddr = res else {
return []
}
var result = [sockaddr_storage]()
for addr in sequence(first: firstAddr, next: { $0.pointee.ai_next }) {
var sockAddr = sockaddr_storage()
memcpy(&sockAddr, addr.pointee.ai_addr, Int(addr.pointee.ai_addrlen))
result.append(sockAddr)
}
return result
}
评论:
如果你只对一个地址家庭感兴趣,那么
您可以将
sockaddr_storage
替换为sockaddr_in
或sockaddr_in6
我已将
node
和service
参数设置为非可选。原因是Swift当前在传递更多信息时有一个错误
C函数的多个可选字符串,请参见
Why does Swift return an unexpected pointer when converting an optional String into an UnsafePointer?
sequence()
用于遍历链接列表,而不是a
while
循环。