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
我已将nodeservice参数设置为非可选。
原因是Swift当前在传递更多信息时有一个错误
C函数的多个可选字符串,请参见
Why does Swift return an unexpected pointer when converting an optional String into an UnsafePointer?
sequence()用于遍历链接列表,而不是
awhile循环。

09-10 09:30
查看更多