Swift - 获取设备的WIFI IP地址

95

我需要在Swift中获取iOS设备的IP地址。这不是其他关于此问题的重复!我只需要获取WiFi IP地址,如果没有WiFi IP地址-我需要处理它。 Stack Overflow上有一些关于此的问题,但只有返回IP地址的函数。例如(来自如何在Swift中获取IP地址):

func getIFAddresses() -> [String] {
    var addresses = [String]()

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs> = nil
    if getifaddrs(&ifaddr) == 0 {

        // For each interface ...
        for (var ptr = ifaddr; ptr != nil; ptr = ptr.memory.ifa_next) {
            let flags = Int32(ptr.memory.ifa_flags)
            var addr = ptr.memory.ifa_addr.memory

            // Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
            if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
                if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {

                    // Convert interface address to a human readable string:
                    var hostname = [CChar](count: Int(NI_MAXHOST), repeatedValue: 0)
                    if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
                        nil, socklen_t(0), NI_NUMERICHOST) == 0) {
                            if let address = String.fromCString(hostname) {
                                addresses.append(address)
                            }
                    }
                }
            }
        }
        freeifaddrs(ifaddr)
    }

    return addresses
}

我这里得到了两个值——来自手机网络(我想是)的地址和我需要的WiFi地址。是否有其他方法只获取WiFi IP地址?

18个回答

4

这里所有的答案都只提供了Wi-Fi的IP地址,而没有提供有线或蜂窝网络的IP地址。以下代码段可用于Wi-Fi/有线/蜂窝网络情况:

func getIPAddressForCellOrWireless()-> String? {

    let WIFI_IF : [String] = ["en0"]
    let KNOWN_WIRED_IFS : [String] = ["en2", "en3", "en4"]
    let KNOWN_CELL_IFS : [String] = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]

    var addresses : [String : String] = ["wireless":"",
                                         "wired":"",
                                         "cell":""]

    var address: String?
    var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil
    if getifaddrs(&ifaddr) == 0 {

        var ptr = ifaddr
        while ptr != nil {
            defer { ptr = ptr?.pointee.ifa_next } // memory has been renamed to pointee in swift 3 so changed memory to pointee

            let interface = ptr?.pointee
            let addrFamily = interface?.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

                if let name: String = String(cString: (interface?.ifa_name)!), (WIFI_IF.contains(name) || KNOWN_WIRED_IFS.contains(name) || KNOWN_CELL_IFS.contains(name)) {

                    // String.fromCString() is deprecated in Swift 3. So use the following code inorder to get the exact IP Address.
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(interface?.ifa_addr, socklen_t((interface?.ifa_addr.pointee.sa_len)!), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                    if WIFI_IF.contains(name){
                        addresses["wireless"] =  address
                    }else if KNOWN_WIRED_IFS.contains(name){
                        addresses["wired"] =  address
                    }else if KNOWN_CELL_IFS.contains(name){
                        addresses["cell"] =  address
                    }
                }

            }
        }
    }
    freeifaddrs(ifaddr)

    var ipAddressString : String?
    let wirelessString = addresses["wireless"]
    let wiredString = addresses["wired"]
    let cellString = addresses["cell"]
    if let wirelessString = wirelessString, wirelessString.count > 0{
        ipAddressString = wirelessString
    }else if let wiredString = wiredString, wiredString.count > 0{
        ipAddressString = wiredString
    }else if let cellString = cellString, cellString.count > 0{
        ipAddressString = cellString
    }
    return ipAddressString
}

1
请注意,问题明确要求“我只需要获取WiFi IP地址...” - Martin R

3
func getIPAddress() -> String {
    var address: String = "error"

    var interfaces: ifaddrs? = nil

    var temp_addr: ifaddrs? = nil
    var success: Int = 0
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(interfaces)
    if success == 0 {
        // Loop through linked list of interfaces
        temp_addr = interfaces
        while temp_addr != nil {
            if temp_addr?.ifa_addr?.sa_family == AF_INET {
                // Check if interface is en0 which is the wifi connection on the iPhone
                if (String(utf8String: temp_addr?.ifa_name) == "en0") {
                    // Get NSString from C String
                    address = String(utf8String: inet_ntoa((temp_addr?.ifa_addr as? sockaddr_in)?.sin_addr))
                }
            }
            temp_addr = temp_addr?.ifa_next
        }
    }
        // Free memory
    freeifaddrs(interfaces)
    return address
}

2

支持IPv4和IPv6

将其作为UIDevice的扩展,并使用UIDevice.current.getIPAdress()进行调用。

private struct Interfaces {
    // INTERFACCIE SUPPORT
    static let wifi = ["en0"]
    static let cellular = ["pdp_ip0","pdp_ip1","pdp_ip2","pdp_ip3"]
    static let supported = wifi + cellular
}


func getIPAdress() -> (String?,String?)? {
    var ip4Adress: String?
    var ip6Adress: String?
    var hasAdress: UnsafeMutablePointer<ifaddrs>?


    if getifaddrs(&hasAdress) == 0 {
        var pointer = hasAdress

        while pointer != nil {
            defer { pointer = pointer?.pointee.ifa_next}

            guard let interface = pointer?.pointee else {continue}

            // SEARCH FOR IPV4 OR IPV6 IN THE INTERFACE OF THE NODE
            // HERE I'M ALREADY LOOSING MY MIND
            // PRIORITY FOR IPV4 THAN IPV6
            if  interface.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
                guard let ip4 = processInterface(interface: interface) else {
                    continue
                }
                ip4Adress = ip4
            }

            if interface.ifa_addr.pointee.sa_family == UInt8(AF_INET6) {
                guard let ip6 = processInterface(interface: interface) else {
                    continue
                }
                ip6Adress = ip6
            }
        }
        freeifaddrs(hasAdress)
    }
    return (ip4Adress, ip6Adress)
}



func processInterface(interface: ifaddrs) -> String? {

    var ipAdress: String = ""
    guard
        let interfaceName = interface.ifa_name else {return nil}
                       guard
                           let interfaceNameFormatted = String(cString: interfaceName, encoding: .utf8) else {return nil}
    guard Interfaces.supported.contains(interfaceNameFormatted) else {return nil}

                       var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))

                       print(interfaceNameFormatted)

                       // CONVERT THE SOCKET ADRESS TO A CORRESPONDING HOST AND SERVICE
                       getnameinfo(interface.ifa_addr,
                                   socklen_t(interface.ifa_addr.pointee.sa_len),
                                   &hostname, socklen_t(hostname.count),
                                   nil,
                                   socklen_t(0),
                                   NI_NUMERICHOST)

                       guard let formattedIpAdress = String(cString: hostname, encoding: .utf8) else {return nil}
                       if !formattedIpAdress.isEmpty {
                           ipAdress = formattedIpAdress
                       }
    return ipAdress
}

1
enum Network: String {
    case wifi = "en0"
    case cellular = "pdp_ip0"
//    case en1 = "en1"
//    case lo = "lo0"
}

// get ipv4 or ipv6 address
extension UIDevice {
    
    func address(family: Int32, for network: Network) -> String? {
        var address: String?

        // Get list of all interfaces on the local machine:
        var ifaddr: UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0, let firstAddr = ifaddr else { return nil }

        // For each interface ...
        for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
            let interface = ifptr.pointee

            let addrFamily = interface.ifa_addr.pointee.sa_family
            if addrFamily == UInt8(family) {
                // Check interface name:
                let name = String(cString: interface.ifa_name)
                if name == network.rawValue {
                    // Convert interface address to a human readable string:
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                                &hostname, socklen_t(hostname.count),
                                nil, socklen_t(0), NI_NUMERICHOST)
                    address = String(cString: hostname)
                }
            }
        }
        freeifaddrs(ifaddr)

        return address
    }
    
    func ipv4(for network: Network) -> String? {
        self.address(family: AF_INET, for: network)
    }
    
    func ipv6(for network: Network) -> String? {
        self.address(family: AF_INET6, for: network)
    }
    
    // get all addresses
    func getIFAddresses() -> [String] {
        var addresses = [String]()
        
        // Get list of all interfaces on the local machine:
        var ifaddr : UnsafeMutablePointer<ifaddrs>?
        guard getifaddrs(&ifaddr) == 0 else { return [] }
        guard let firstAddr = ifaddr else { return [] }
        
        // For each interface ...
        for ptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
            let flags = Int32(ptr.pointee.ifa_flags)
            let addr = ptr.pointee.ifa_addr.pointee
            
            // Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
            if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
                if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
                    
                    // Convert interface address to a human readable string:
                    var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                    if (getnameinfo(ptr.pointee.ifa_addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
                                    nil, socklen_t(0), NI_NUMERICHOST) == 0) {
                        let address = String(cString: hostname)
                        addresses.append(address)
                    }
                }
            }
        }
        
        freeifaddrs(ifaddr)
        
        return addresses
    }
}

如何使用
// for wifi
  let wifi = UIDevice.current.ipv4(for: .wifi) # ipv4
  let wifi6 = UIDevice.current.ipv6(for: .wifi) # ipv6

// for cellular
  let cellular = UIDevice.current.ipv4(for: .cellular) # ipv4
  let cellular6 = UIDevice.current.ipv6(for: .cellular) # ipv6

1
如果您只想得到IPv4响应作为输出,只需修改Martin R.的解决方案即可。
 func getWiFiAddress() -> String? {
    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else { return nil }
    guard let firstAddr = ifaddr else { return nil }

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        //if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {  // **ipv6 committed
        if addrFamily == UInt8(AF_INET){

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if  name == "en0" {

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)
            }
        }
    }
    freeifaddrs(ifaddr)

    return address
}

使用方法:

if let addr = getWiFiAddress() {
   print(addr)
} else {
   print("No WiFi address")
}

当我激活个人热点时,我的 address === ""(即为空),但是当我的设备连接到外部热点时一切正常。我正在使用iOS 12.1.4。 - b-asaf
不确定这个问题,我有4个设备ios 12.1,1个设备12.2,然后2个设备出现错误 -> https://stackoverflow.com/questions/55491986/swift-crash-when-getcurrentip-address-fixedwidthinteger-inita - famfamfam

0

对于在Mac上的Swift - Swift 4: 通过这种方式,您还可以找到来自Wifi(而不仅仅是以太网)的IP地址。

func getWiFiAddress() -> String? {
    var address : String?

    // Get list of all interfaces on the local machine:
    var ifaddr : UnsafeMutablePointer<ifaddrs>?
    guard getifaddrs(&ifaddr) == 0 else { return nil }
    guard let firstAddr = ifaddr else { return nil }

    // For each interface ...
    for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
        let interface = ifptr.pointee

        // Check for IPv4 or IPv6 interface:
        let addrFamily = interface.ifa_addr.pointee.sa_family
        if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {

            // Check interface name:
            let name = String(cString: interface.ifa_name)
            if  name == "en0" {

                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(0), NI_NUMERICHOST)
                address = String(cString: hostname)
            } else if name == "en1" {
                // Convert interface address to a human readable string:
                var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
                getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
                            &hostname, socklen_t(hostname.count),
                            nil, socklen_t(1), NI_NUMERICHOST)
                address = String(cString: hostname)
            }
        }
    }
    freeifaddrs(ifaddr)

    return address
}

0

假设您已经拥有套接字文件描述符,这是Swift 5.2的解决方案。

// Depending on your case get the socket's file descriptor
// the way you want
let socketFd = foo()

// Get the remote address for that file descriptor
var addr: sockaddr_storage = sockaddr_storage()
var addr_len: socklen_t = socklen_t(MemoryLayout.size(ofValue: addr))

var hostBuffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))

// Make local copy to avoid: "Overlapping accesses to 'addr', 
// but modification requires exclusive access; consider copying
// to a local variable"
let addrLen = addr.ss_len

withUnsafeMutablePointer(to: &addr) {
    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
        if getpeername(socketFd, $0, &addr_len) != 0 { return }
        
        getnameinfo($0, socklen_t(addrLen), &hostBuffer, socklen_t(hostBuffer.count), nil, 0, NI_NUMERICHOST)
    }
}

let connectedHost = String(cString: hostBuffer, encoding: .utf8)

0
enum Network: String {
case wifi = "en0"
case cellular = "pdp_ip0"
case hotspot = "bridge100"
}

如果使用热点获取IP


目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community
此回答未解决如何获取IP的问题。建议删除。 - burnsi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接