POSIX getaddrinfo分配以后必须使用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
}不过,我觉得这个功能不正确。
getaddrinfo分配内存,Swift不应该用自己的东西覆盖该内存?freeaddrinfo删除了整个列表,并且应该复制分配给结果数组的ai信息?与getaddrinfo接口的正确方法是什么?
发布于 2016-10-04 18:49:20
由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函数传递多个可选字符串时有一个Bug,请参阅Why does Swift return an unexpected pointer when converting an optional String into an UnsafePointer?。sequence()来遍历链接列表,而不是while循环。https://stackoverflow.com/questions/39857435
复制相似问题