Swift 3, как разрешить NetService IP?

Просто пробую Bonjour в Swift 3

Вот мой код, я могу получить делегата

func netServiceDidResolveAddress(_ sender: NetService) {
 print("netServiceDidResolveAddress service name \(sender.name) of type \(sender.type)," +
                "port \(sender.port), addresses \(sender.addresses)")
}

И вот мой результат

имя службы netServiceDidResolveAddress Mac mini Уэббера типа _myapp._tcp., порт 5678, адреса Необязательный ([‹1002162e c0a80205 00000000 00000000>, ‹1c1e162e 00000000 fe800000 00000000 00b4bce7ad 00b4bce7ad 00b4bce700000000000000000000)

c0a80205 - это IP-адрес, который я ищу => 192.168.2.5

И адрес [Данные] , Apple говорит

Адреса службы. Это NSArray экземпляров NSData, каждый из которых содержит одну структуру sockaddr, подходящую для использования с connect(2). В случае, если адреса для службы не разрешены или служба еще не разрешена, возвращается пустой NSArray.

Я все еще не понимаю, почему Data не может использовать .btyes? Как Apple говорит: «Это NSArray экземпляров NSData», но я не могу использовать его как NSData.

И как разрешить адрес как читаемую строку IP?

Я пробовал это раньше, но не получаю результата, кроме как...

let thedata = NSData(bytes: sender.addresses, length: (sender.addresses?.count)!)
var storage = sockaddr_storage()
thedata.getBytes(&storage, length: sizeof(sockaddr_storage))

if Int32(storage.ss_family) == AF_INET {
    let addr4 = withUnsafePointer(&storage) {UnsafePointer<sockaddr_in>($0).pointee }
    print(inet_ntoa(addr4.sin_addr));
}

Любое предложение поможет, спасибо


person Webber Lai    schedule 05.07.2016    source источник


Ответы (4)


Вот как я это сделал в Swift 3.

func netServiceDidResolveAddress(_ sender: NetService) {
    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
    guard let data = sender.addresses?.first else { return }
    data.withUnsafeBytes { (pointer:UnsafePointer<sockaddr>) -> Void in
        guard getnameinfo(pointer, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
            return
        }
    }
    let ipAddress = String(cString:hostname)
    print(ipAddress)
}

Свифт 5

var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
data.withUnsafeBytes { (pointer: UnsafeRawBufferPointer) -> Void in
        let sockaddrPtr = pointer.bindMemory(to: sockaddr.self)
        guard let unsafePtr = sockaddrPtr.baseAddress else { return }
        guard getnameinfo(unsafePtr, socklen_t(data.count), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
            return
        }
    }
let ipAddress = String(cString:hostname)
print(ipAddress)
person Phil Cole    schedule 27.03.2017
comment
Фил, с Swift 5 я получаю предупреждение: 'withUnsafeBytes' устарел: вместо этого используйте withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R. Не могли бы вы сказать мне, как это решить, документация от Apple у меня выше головы. - person KaasCoder; 16.04.2019
comment
используйте предложенный метод withUnsafeBytes и используйте baseAddress.assignMemoryBound(to:) для преобразования. См. мой ответ ниже. - person Nick H247; 10.06.2019

Отредактированный ответ Фила Коулза для решения Swift 5.0 без предупреждений:

func netServiceDidResolveAddress(_ sender: NetService) {
        var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
        guard let data = sender.addresses?.first else { return }
        data.withUnsafeBytes { ptr in
            guard let sockaddr_ptr = ptr.baseAddress?.assumingMemoryBound(to: sockaddr.self) else {
                // handle error
                return
            }
            var sockaddr = sockaddr_ptr.pointee
            guard getnameinfo(sockaddr_ptr, socklen_t(sockaddr.sa_len), &hostname, socklen_t(hostname.count), nil, 0, NI_NUMERICHOST) == 0 else {
                return
            }
        }
        let ipAddress = String(cString:hostname)
        print(ipAddress)
    }
person Nick H247    schedule 10.06.2019
comment
Это дает ошибку в «data.withUnsafeBytes {ptr in..», и в ошибке говорится: «Невозможно определить тип возвращаемого значения сложного закрытия»; добавить явный тип для устранения неоднозначности в Swift 4.2 - person AfnanAhmad; 23.09.2019

ОК ... это не умный ответ, по крайней мере, я могу получить читаемый IP-адрес

Просто используйте эту функцию, чтобы получить строку IP

let bonjourDevices = [NetService]()

let bonjourDevice = bonjourDevices[0] 

let host = self.getIPV4StringfromAddress(address:bonjourDevice.addresses!)


func getIPV4StringfromAddress(address: [Data] , port : Int ) -> String{

        let data = address.first! as NSData;

        var ip1 = UInt8(0)
        data.getBytes(&ip1, range: NSMakeRange(4, 1))

        var ip2 = UInt8(0)
        data.getBytes(&ip2, range: NSMakeRange(5, 1))

        var ip3 = UInt8(0)
        data.getBytes(&ip3, range: NSMakeRange(6, 1))

        var ip4 = UInt8(0)
        data.getBytes(&ip4, range: NSMakeRange(7, 1))

        let ipStr = String(format: "%d.%d.%d.%d:%d",ip1,ip2,ip3,ip4,port);

        return ipStr;
    }
person Webber Lai    schedule 06.07.2016

Я не могу заставить его работать с Data, но с NSData я бы использовал это:

extension NSData {
    func castToCPointer<T>() -> T {
        let mem = UnsafeMutablePointer<T>.allocate(capacity: MemoryLayout<T.Type>.size)
        self.getBytes(mem, length: MemoryLayout<T.Type>.size)
         return mem.move()
    }
}

Итак, у нас есть netServiceDidResolveAddress:

func netServiceDidResolveAddress(_ sender: NetService) {
    if let addresses = sender.addresses, addresses.count > 0 {
        for address in addresses {
            let data = address as NSData

            let inetAddress: sockaddr_in = data.castToCPointer()
            if inetAddress.sin_family == __uint8_t(AF_INET) {
                if let ip = String(cString: inet_ntoa(inetAddress.sin_addr), encoding: .ascii) {
                    // IPv4
                    print(ip)
                }
            } else if inetAddress.sin_family == __uint8_t(AF_INET6) {
                let inetAddress6: sockaddr_in6 = data.castToCPointer()
                let ipStringBuffer = UnsafeMutablePointer<Int8>.allocate(capacity: Int(INET6_ADDRSTRLEN))
                var addr = inetAddress6.sin6_addr

                if let ipString = inet_ntop(Int32(inetAddress6.sin6_family), &addr, ipStringBuffer, __uint32_t(INET6_ADDRSTRLEN)) {
                    if let ip = String(cString: ipString, encoding: .ascii) {
                        // IPv6
                        print(ip)
                    }
                }

                ipStringBuffer.deallocate(capacity: Int(INET6_ADDRSTRLEN))
            }
        }
    }
}

У меня есть следующий результат (сохранение ips в массиве перед отображением):

["172.16.10.120", "172.16.8.251", "::", "::82c9:d9a5:2eed:1c87"]

Код, вдохновленный https://gist.github.com/agrippa1994/d8c66a2ded74fb2dd801, написан на Swift 2.3. и адаптирован для Swift 3.0

person Kévin Renella    schedule 01.11.2016
comment
ipStringBuffer.deallocate(емкость: Int(INET6_ADDRSTRLEN)) устарела. Вместо этого используйте ipStringBuffer.deallocate(). - person KaasCoder; 09.08.2018