You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过NetService获取打印机URL以创建UIPrinter实例

解决获取AirPrint打印机URL的问题

首先可以明确:你用NetServiceBrowser搜索_ipp._tcp.服务的方式完全适用于查找本地AirPrint打印机,因为AirPrint正是基于IPP(Internet Printing Protocol)协议实现的,这个服务类型就是AirPrint设备的标准标识。

接下来解决核心问题:如何从找到的NetService中提取出能创建UIPrinter的URL。你当前只打印了服务的基本信息,这还不够——需要先解析服务的详细信息(地址、端口、TXT记录),才能构造出正确的IPP URL。

具体步骤和代码示例

  1. 在didFind service代理方法中触发服务解析
    当找到服务时,不要只打印信息,调用resolve(withTimeout:)来获取该服务的完整网络信息:

    func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool) {
        print("找到打印机服务:\(service.name)")
        service.resolve(withTimeout: 5.0) // 设置合理的超时时间
    }
    
  2. 实现服务解析完成的代理方法
    当服务解析成功后,在netServiceDidResolveAddress中提取地址、端口和TXT记录里的资源路径(rp字段),然后构造IPP URL:

    func netServiceDidResolveAddress(_ sender: NetService) {
        guard let addresses = sender.addresses, !addresses.isEmpty else {
            print("无法解析服务地址")
            return
        }
        
        // 提取IP地址(兼容IPv4和IPv6,优先IPv4)
        var ipAddress: String?
        for addressData in addresses {
            var socketAddress = sockaddr_storage()
            addressData.copyBytes(to: &socketAddress, count: MemoryLayout<sockaddr_storage>.size)
            
            if socketAddress.ss_family == UInt8(AF_INET) {
                let addr = withUnsafePointer(to: &socketAddress) {
                    $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee.sin_addr }
                }
                var buffer = [CChar](repeating: 0, count: Int(INET_ADDRSTRLEN))
                inet_ntop(AF_INET, &addr, &buffer, socklen_t(INET_ADDRSTRLEN))
                ipAddress = String(cString: buffer)
                break
            } else if socketAddress.ss_family == UInt8(AF_INET6) {
                let addr = withUnsafePointer(to: &socketAddress) {
                    $0.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) { $0.pointee.sin6_addr }
                }
                var buffer = [CChar](repeating: 0, count: Int(INET6_ADDRSTRLEN))
                inet_ntop(AF_INET6, &addr, &buffer, socklen_t(INET6_ADDRSTRLEN))
                ipAddress = String(cString: buffer)
            }
        }
        
        guard let ip = ipAddress, let port = sender.port, port > 0 else {
            print("无法获取IP或端口")
            return
        }
        
        // 从TXT记录中获取资源路径(rp字段,AirPrint打印机必备)
        var resourcePath = "/ipp/print" // 默认路径,部分打印机可能不同
        if let txtRecords = sender.txtRecord() {
            let txtDict = NetService.dictionary(fromTXTRecord: txtRecords)
            if let rpData = txtDict["rp"], let rpString = String(data: rpData, encoding: .utf8) {
                resourcePath = rpString
            }
        }
        
        // 构造完整的IPP URL
        guard let printerUrl = URL(string: "ipp://\(ip):\(port)\(resourcePath)") else {
            print("构造URL失败")
            return
        }
        
        // 创建UIPrinter实例
        let printer = UIPrinter(url: printerUrl)
        print("成功创建打印机实例:\(printer.name)")
    }
    
  3. 处理解析失败的情况
    别忘了实现netService(_:didNotResolve:)来处理解析失败的场景:

    func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber]) {
        print("解析服务失败:\(errorDict)")
    }
    

补充说明

  • 为什么需要解析服务?因为NetService初始返回的信息只有名称、服务类型和域名,缺少构造URL必需的IP地址、端口和资源路径,这些都需要通过resolve(withTimeout:)来获取。
  • TXT记录中的rp字段:这是IPP服务的资源路径,不同打印机可能略有差异,但大部分AirPrint设备会设置为/ipp/print,所以我们可以用这个作为默认值,再优先取TXT记录里的实际值。

内容的提问来源于stack exchange,提问作者serdaryillar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:24:48