【问题标题】:Swift getaddrinfo快速获取地址信息
【发布时间】:2016-10-04 16:28:23
【问题描述】:

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
}

不过,我不觉得这个函数是正确的。

  • Swift 内存模型如何知道getaddrinfo 分配了内存,而 Swift 不应该用自己的东西覆盖该内存?
  • Swift 怎么知道freeaddrinfo 删除了整个列表,它应该复制出分配给结果数组的ai 信息?

getaddrinfo 交互的正确方式是什么?

【问题讨论】:

    标签: swift memory posix low-level-api


    【解决方案1】:

    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
    }
    

    备注:

    【讨论】:

    • 这与您的答案无关,但是:您从哪里获得 addrinfo 字段的 Swift 版本?你只是手动重写了它们,还是有一个工具可以显示 C 代码的 Swift 接口? (这周我正在处理 Swift 中的 C 语言,我很想知道。)
    • @JoshCaswell:我不确定我是否理解你的问题。命令单击addrinfo 会在导入到 Swift 时显示标题。 – 您也可以将#import &lt;netdb.h&gt; 添加到任何C 文件中,命令单击“netdb.h”以跳转到该文件,然后选择“生成的接口”。
    • Xcode 中的“生成的接口”是我缺少的部分,谢谢!
    • @Etan: Swift 使用牛作为例如的 backing store字符串和数组,即仅适用于它知道和管理的类型。 struct addrinfo 是从 C 中导入的,Swift 不会弄乱它。
    • @Etan:不幸的是我没有 the 参考,但这里有一些提示:developer.apple.com/reference/swift/array 提到了写时复制,github.com/apple/swift/blob/master/docs/Arrays.rstmikeash.com/pyblog/… 显示了如何实现了数组。 Joe Groff 在lists.swift.org/pipermail/swift-users/Week-of-Mon-20160516/… 表示 Swift 尊重导入结构的布局。
    猜你喜欢
    • 2020-04-21
    • 2023-04-09
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多