投稿文章,作者:Haley_Wong(簡(jiǎn)書(shū)) 查漏補(bǔ)缺集是自己曾經(jīng)做過(guò)相關(guān)的功能,但是重做相關(guān)功能或者重新看到相關(guān)功能的實(shí)現(xiàn),感覺(jué)理解上更深刻。這一類的文章集中記錄在查漏補(bǔ)缺集。 iOS 開(kāi)發(fā)中難免會(huì)遇到很多與網(wǎng)絡(luò)方面的判斷,這里做個(gè)匯總,大多可能是與WiFi相關(guān)的。 1.Ping域名、Ping某IP 有時(shí)候可能會(huì)遇到ping 某個(gè)域名或者ip通不通,再做下一步操作。這里的ping與傳統(tǒng)的做get或者post請(qǐng)求還是有很大區(qū)別的。比如我們連接了某個(gè)WiFi,測(cè)試ping www.baidu.com,如果能ping 通,基本可以斷定可以上網(wǎng)了,但是如果我們做了一個(gè)get 請(qǐng)求(url 是www.baidu.com),路由器可能重定向這個(gè)WiFi內(nèi)的某網(wǎng)頁(yè)了,依然沒(méi)有錯(cuò)誤返回,就會(huì)誤認(rèn)為可以正常上網(wǎng)。 這里有關(guān)于ping命令的詳細(xì)解釋:百度百科Ping iOS中想要ping域名或者ip,蘋(píng)果提供了一個(gè)官方例子SimplePing 在例子中,有一個(gè)蘋(píng)果已經(jīng)封裝過(guò)的類【SimplePing.h】和【SimplePing.m】 使用起來(lái)也相當(dāng)?shù)暮?jiǎn)單: 首先創(chuàng)建一個(gè)Ping對(duì)象: 1 2 3 4 5 | SimplePing *pinger = [[SimplePing alloc] initWithHostName:self.hostName];
self.pinger = pinger;
pinger.delegate = self;
pinger.addressStyle = SimplePingAddressStyleICMPv4;
[pinger start];
|
然后在start成功的代理方法中,發(fā)送數(shù)據(jù)報(bào)文: 1 2 3 4 5 6 7 8 9 10 11 12 13 | /**
* start成功,也就是準(zhǔn)備工作做完后的回調(diào)
*/
- (void)simplePing:(SimplePing *)pinger didStartWithAddress:(NSData *)address
{
// 發(fā)送測(cè)試報(bào)文數(shù)據(jù)
[self.pinger sendPingWithData:nil];
}
- (void)simplePing:(SimplePing *)pinger didFailWithError:(NSError *)error
{
NSLog(@ "didFailWithError" );
[self.pinger stop];
}
|
其他幾個(gè)代理方法也非常簡(jiǎn)單,就簡(jiǎn)單記錄一下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 發(fā)送測(cè)試報(bào)文成功的回調(diào)方法
- (void)simplePing:(SimplePing *)pinger didSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber
{
NSLog(@ "#%u sent" , sequenceNumber);
}
//發(fā)送測(cè)試報(bào)文失敗的回調(diào)方法
- (void)simplePing:(SimplePing *)pinger didFailToSendPacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber error:(NSError *)error
{
NSLog(@ "#%u send failed: %@" , sequenceNumber, error);
}
// 接收到ping的地址所返回的數(shù)據(jù)報(bào)文回調(diào)方法
- (void)simplePing:(SimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet sequenceNumber:(uint16_t)sequenceNumber
{
NSLog(@ "#%u received, size=%zu" , sequenceNumber, packet.length);
}
- (void)simplePing:(SimplePing *)pinger didReceiveUnexpectedPacket:(NSData *)packet
{
NSLog(@ "#%s" ,__func__);
}
|
注意點(diǎn): iOS 中 ping失敗后(即發(fā)送測(cè)試報(bào)文成功后,一直沒(méi)后收到響應(yīng)的報(bào)文),不會(huì)有任何回調(diào)方法告知我們。而一般ping 一次的結(jié)果也不太準(zhǔn)確,ping 花費(fèi)的時(shí)間也非常短,所以我們一般會(huì)ping多次,發(fā)送一次ping 測(cè)試報(bào)文0.5s后檢測(cè)一下這一次ping是否已經(jīng)收到響應(yīng)。0.5s后檢測(cè)時(shí),如果已經(jīng)收到響應(yīng),則可以ping 通;如果沒(méi)有收到響應(yīng),則視為超時(shí)。 做法也有很多種,可以用NSTimer或者 {- (void)performSelector: withObject:afterDelay:} 這里有一個(gè)別人寫(xiě)的工程:https://github.com/lovesunstar/STPingTest PingTest效果圖 終端ping效果圖 2.獲取WiFi信息 以前物聯(lián)網(wǎng)剛火的時(shí)候,出現(xiàn)過(guò)很多一體式無(wú)線路由,所以App里難免會(huì)遇到要判斷當(dāng)前所連接的WiFi,以及獲取WiFi信息的功能。13年的時(shí)候查過(guò)一些關(guān)于WiFi的方法,后面漸漸都忘記了。慚愧?。?! 需要添加SystemConfiguration.framework 并在當(dāng)前類中添加代碼#import 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //獲取WiFi 信息,返回的字典中包含了WiFi的名稱、路由器的Mac地址、還有一個(gè)Data(轉(zhuǎn)換成字符串打印出來(lái)是wifi名稱)
- (NSDictionary *)fetchSSIDInfo {
NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
if (!ifs) {
return nil;
}
NSDictionary *info = nil;
for (NSString *ifnam in ifs) {
info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
if (info && [info count]) { break ; }
}
return info;
}
//打印出來(lái)的結(jié)果:
2016-05-12 15:28:51.674 SimplePing[18883:6790207] WIFI_INFO:{
BSSID = "a4:2b:8c:c:7f:bd" ;
SSID = bdmy06;
SSIDDATA = ;
}
|
3.獲取WiFi名稱 有了上一步,獲取WiFi名稱就非常簡(jiǎn)單了。 1 2 3 | NSString *WiFiName = info[@ "SSID" ];
//打印結(jié)果:
2016-05-12 15:35:13.059 SimplePing[18887:6791418] bdmy06
|
完整的: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | - (NSString *)fetchWiFiName {
NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
if (!ifs) {
return nil;
}
NSString *WiFiName = nil;
for (NSString *ifnam in ifs) {
NSDictionary *info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
if (info && [info count]) {
// 這里其實(shí)對(duì)應(yīng)的有三個(gè)key:kCNNetworkInfoKeySSID、kCNNetworkInfoKeyBSSID、kCNNetworkInfoKeySSIDData,
// 不過(guò)它們都是CFStringRef類型的
WiFiName = [info objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID];
// WiFiName = [info objectForKey:@"SSID"];
break ;
}
}
return WiFiName;
}
|
4.獲取當(dāng)前所連接WiFi的網(wǎng)關(guān)地址 例如自己家的路由器一般默認(rèn)的網(wǎng)關(guān)地址是192.168.1.1,獲取的就是這個(gè)192.168.1.1。 為什么不直接寫(xiě)死呢? 因?yàn)橐恍┥虉?chǎng)或者有多個(gè)路由器的網(wǎng)關(guān)地址是不一樣的,比如之前有個(gè)公司的網(wǎng)關(guān)是192.168.89.1。 這里有篇博客,這是地址 需要導(dǎo)入的庫(kù): 獲取網(wǎng)關(guān)的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | - (NSString *)getGatewayIpForCurrentWiFi {
NSString *address = @ "error" ;
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 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 != NULL) {
/*/
int i=255;
while ((i--)>0)
//*/
if (temp_addr->ifa_addr->sa_family == AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@ "en0" ])
{
// Get NSString from C String //ifa_addr
//ifa->ifa_dstaddr is the broadcast address, which explains the "255's"
// address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr)];
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
//routerIP----192.168.1.255 廣播地址
NSLog(@ "broadcast address--%@" ,[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr)]);
//--192.168.1.106 本機(jī)地址
NSLog(@ "local device ip--%@" ,[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]);
//--255.255.255.0 子網(wǎng)掩碼地址
NSLog(@ "netmask--%@" ,[NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_netmask)->sin_addr)]);
//--en0 端口地址
NSLog(@ "interface--%@" ,[NSString stringWithUTF8String:temp_addr->ifa_name]);
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
in_addr_t i = inet_addr([address cStringUsingEncoding:NSUTF8StringEncoding]);
in_addr_t* x = &i;
unsigned char *s = getdefaultgateway(x);
NSString *ip=[NSString stringWithFormat:@ "%d.%d.%d.%d" ,s[0],s[1],s[2],s[3]];
free(s);
return ip;
}
|
其中 getdefaultgateway 是一個(gè)C語(yǔ)言文件中的方法,在工程里可以找到。 5.獲取本機(jī)在WiFi環(huán)境下的IP地址 獲取本機(jī)在WiFi環(huán)境下的ip地址,在上一節(jié)中其實(shí)已經(jīng)寫(xiě)過(guò),這里將其提取出來(lái)即可: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | - (NSString *)getLocalIPAddressForCurrentWiFi
{
NSString *address = nil;
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 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 != NULL) {
if (temp_addr->ifa_addr->sa_family == AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@ "en0" ]) {
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
return address;
}
}
temp_addr = temp_addr->ifa_next;
}
freeifaddrs(interfaces);
}
return nil;
}
|
同樣的方式也可以獲取廣播地址、子網(wǎng)掩碼、端口等,組裝成一個(gè)字典。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | - (NSMutableDictionary *)getLocalInfoForCurrentWiFi {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 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 != NULL) {
if (temp_addr->ifa_addr->sa_family == AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@ "en0" ]) {
//----192.168.1.255 廣播地址
NSString *broadcast = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_dstaddr)->sin_addr)];
if (broadcast) {
[dict setObject:broadcast forKey:@ "broadcast" ];
}
NSLog(@ "broadcast address--%@" ,broadcast);
//--192.168.1.106 本機(jī)地址
NSString *localIp = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
if (localIp) {
[dict setObject:localIp forKey:@ "localIp" ];
}
NSLog(@ "local device ip--%@" ,localIp);
//--255.255.255.0 子網(wǎng)掩碼地址
NSString *netmask = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_netmask)->sin_addr)];
if (netmask) {
[dict setObject:netmask forKey:@ "netmask" ];
}
NSLog(@ "netmask--%@" ,netmask);
//--en0 端口地址
NSString *interface = [NSString stringWithUTF8String:temp_addr->ifa_name];
if (interface) {
[dict setObject:interface forKey:@ "interface" ];
}
NSLog(@ "interface--%@" ,interface);
return dict;
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return dict;
}
|
將返回的字典打印出來(lái): 1 2 3 4 5 6 | 2016-05-12 17:59:09.257 SimplePing[19141:6830567] wifi:{
broadcast = "192.168.1.255" ;
interface = en0;
localIp = "192.168.1.7" ;
netmask = "255.255.255.0" ;
}
|
|