3

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

4

1 回答 1

4

由 (eg by ) 分配的getaddrinfo内存在由(eg by )malloc释放之前不会被分配给同一运行进程中的任何其他动态内存分配函数。因此,Swift 运行时不会践踏该内存(如果我们假设它没有编程错误,例如错误的指针计算)。freeaddrinfofree

也是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
}

评论:

于 2016-10-04T18:49:20.357 回答