Comment obtenir l'adresse Ip de swift
Comment puis-je obtenir mon adresse IP locale?
j'ai essayé d'utiliser ce Obj C
exemple: comment obtenir l'adresse ip de l'iphone programmatique
Quand j'arrive à la fonction getifaddrs()
je ne peux pas aller plus loin. je ne peux pas utiliser la fonction.
y a-t-il une autre façon de le faire, ou est-ce que je m'y prends mal?
3 réponses
comme il s'est avéré dans la discussion, OP a besoin de l'adresse d'interface sur un Mac et non sur un appareil iOS comme je le pensais initialement. Le code référencé dans la question vérifie pour le nom de l'interface "en0", qui est L'interface WiFi sur l'iPhone. Sur un Mac il fait plus sens pour vérifier tout "opérationnel" de l'interface à la place. Donc j'ai réécrit la réponse. C'est maintenant une traduction rapide du code dans Détecter tout réseau connecté .
getifaddrs()
est défini dans <ifaddrs.h>
, qui n'est pas inclus par défaut.
Par conséquent, vous devez créer un en-tête de pont et ajouter
#include <ifaddrs.h>
la fonction suivante retourne un tableau avec les noms de tous les "opérationnel" interfaces réseau.
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 ...
var ptr = ifaddr
while ptr != nil {
defer { ptr = ptr.memory.ifa_next }
let flags = Int32(ptr.memory.ifa_flags)
let 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(ptr.memory.ifa_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
}
mise à jour pour Swift 3: en plus de l'adoption du code
beaucoup de changements dans Swift 3 ,
itérer sur toutes les interfaces peut maintenant utiliser le nouveau
sequence()
fonction:
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: { "151920920".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
}
répondant à Is there an alternative way to getifaddrs() for getting the ip-address?
Oui, il y a une alternative. Vous pouvez également obtenir l'adresse ip en utilisant ioctl()
. La manière la plus propre serait de le faire en C et ensuite de l'emballer dans Swift. Considérez donc ceci:
crée un C Source File
(Xcode devrait le créer avec un .h) et n'oubliez pas d'ajouter l'en-tête dans l'en-tête passerelle de votre projet, ou dans l'en-tête umbrella-header si vous avez un cadre cocoa touch.
ajoutez ce qui suit à votre .c déclaration de la source et de la méthode.h:
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
int _interfaceAddressForName(char* interfaceName, struct sockaddr* interfaceAddress) {
struct ifreq ifr;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name, interfaceName, IFNAMSIZ-1);
int ioctl_res;
if ( (ioctl_res = ioctl(fd, SIOCGIFADDR, &ifr)) < 0){
return ioctl_res;
}
close(fd);
memcpy(interfaceAddress, &ifr.ifr_addr, sizeof(struct sockaddr));
return 0;
}
votre emballage Swift pourrait ressembler à quelque chose comme:
public enum Error:ErrorType {
case IOCTLFailed(Int32)
case StringIsNotAnASCIIString
}
public func interfaceAddress(forInterfaceWithName interfaceName: String) throws -> sockaddr_in {
guard let cString = interfaceName.cStringUsingEncoding(NSASCIIStringEncoding) else {
throw Error.StringIsNotAnASCIIString
}
let addressPtr = UnsafeMutablePointer<sockaddr>.alloc(1)
let ioctl_res = _interfaceAddressForName(strdup(cString), addressPtr)
let address = addressPtr.move()
addressPtr.dealloc(1)
if ioctl_res < 0 {
throw Error.IOCTLFailed(errno)
} else {
return unsafeBitCast(address, sockaddr_in.self)
}
}
, Alors vous pouvez l'utiliser dans votre code comme:
let interfaceName = "en0"
do {
let wlanInterfaceAddress = try interfaceAddress(forInterfaceWithName: interfaceName)
print(String.fromCString(inet_ntoa(wlanInterfaceAddress.sin_addr))!)
} catch {
if case Error.IOCTLFailed(let errno) = error where errno == ENXIO {
print("interface(\(interfaceName)) is not available")
} else {
print(error)
}
}
en0
est typiquement l'interface dont vous avez besoin, qui signifie WLAN
si vous avez aussi besoin de connaître les noms d'interface disponibles, vous pouvez utiliser if_indextoname()
:
public func interfaceNames() -> [String] {
let MAX_INTERFACES = 128;
var interfaceNames = [String]()
let interfaceNamePtr = UnsafeMutablePointer<Int8>.alloc(Int(IF_NAMESIZE))
for interfaceIndex in 1...MAX_INTERFACES {
if (if_indextoname(UInt32(interfaceIndex), interfaceNamePtr) != nil){
if let interfaceName = String.fromCString(interfaceNamePtr) {
interfaceNames.append(interfaceName)
}
} else {
break
}
}
interfaceNamePtr.dealloc(Int(IF_NAMESIZE))
return interfaceNames
}
malheureusement aucune des méthodes ne fonctionnera dans iOS 11 car Apple a bloqué l'accès des développeurs tiers aux adresses MAC. Les développeurs d'applications réseau ont même déposé une pétition à Apple pour revenir sur cette politique https://www.change.org/p/apple-save-networking-tools-on-ios-11