一,前言
小编在目前在做智能硬件方面的项目,遇到一个需求,就是开启手机热点,然后将智能设备配网连接到手机上,手机发送UDP广播查找设备。
二,问题
手机通过 255.255.255.255 地址进行UDP广播,设备没有收到消息,连接同一个热点的设备通过是能收到广播消息的,并且能相互通信。
三,个人分析
手机是连接着4G网络,并且开启热点,如果直接通过 255.255.255.255 地址进行广播的话,它怎么知道该广播到外网去呢还是手机热点的网段,因此猜测应该对指定的网段进行广播.
四,通过局域网 IP 计算网络广播地址
网段IP = 子网掩码&IP
网段最大地址 = ~(子网掩码)
广播地址 = 网段地址 + 网段最大地址;
例如:
IP:172.20.10.10/28
子网掩码:255.255.255.240
子网掩码二进制:11111111 11111111 11111111 11110000
网段IP = 172.20.10.10 & 255.255.255.240 (按位与) = 172.20.10.0
网段最大地址 = ~(255.255.255.240 ) = 15
广播地址 = 172.20.10.0 + 15 = 172.20.10.15
也可以使用在线工具计算 http://ip.chacuo.net/ipcalc
其他的网络知识请百度 "子网掩码","IP","广播地址","局域网广域网","IP地址分类" ,"IP地址分类主机数量"等关键字
五,实践看看是否是这样
1.首先我们在4G网络环境下开启热点
2.使用另一台设备连接该热点,并查看该热点的详细信息
可以拿到设备的ip(172.20.10.10) 子网掩码(255.255.255.240)
通过第四点可以计算出子 广播地址为172.20.10.15
然后在手机上或电脑上安装一些网络调试助手调试UDP
软件推荐:
MAC:PacketSender
iOS:TCP/UDP
Android:网络测试
Windows:网络调试助手
小编拿iPhone 和 Android 机进行测试,iPhone开启热点,Android模拟设备。
iPhone
Android
可以发现开启热点的手机是可以见数据广播到局域网中的。
六,iOS 相关代码
获取热点手机在局域网中的IP地址
UIDevice+GBCategory.h
#import <UIKit/UIKit.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>
#define IOS_CELLULAR @"pdp_ip0"
#define IOS_WIFI @"en0"
//#define IOS_VPN @"utun0"
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
@interface UIDevice(GBCategory)
- (NSDictionary *)getIPAddresses;
- (NSString *)getIPAddress:(BOOL)preferIPv4;
/**
获取热点地址
@param preferIPv4 <#preferIPv4 description#>
@return <#return value description#>
*/
- (NSString *)getHotSpotIp;
/**
获取热点子网掩码
@param preferIPv4 <#preferIPv4 description#>
@return <#return value description#>
*/
- (NSString *)getHotSpotMask;
/**
获取热点网络广播地址
@return <#return value description#>
*/
- (NSString *)getHotSpotBroadcastIp;
- (BOOL)flagWithOpenHotSpot;
@end
UIDevice+GBCategory.m
#import "UIDevice+GBCategory.h"
#import <UIDevice+YYAdd.h>
@implementation UIDevice(GBCategory)
- (NSDictionary *)getIPAddresses
{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on success
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
continue; // deeply nested code harder to read
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
const struct sockaddr_in *netmask = (const struct sockaddr_in*)interface->ifa_netmask;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
}
}
if(type) {
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
if (netmask) {
NSString *sin_addr = [NSString stringWithUTF8String:inet_ntoa(addr->sin_addr)];
NSString *ifa_netmask = [NSString stringWithUTF8String:inet_ntoa(netmask->sin_addr)];
NSString *key = [NSString stringWithFormat:@"ifa_netmask/%@", type];
addresses[key] = ifa_netmask;
NSLog(@"ip Info sin_addr:%@ - %@",sin_addr,ifa_netmask);
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
- (NSString *)getIPAddress:(BOOL)preferIPv4
{
NSArray *searchArray = preferIPv4 ?
@[ /*IOS_VPN @"/" IP_ADDR_IPv4, IOS_VPN @"/" IP_ADDR_IPv6,*/ IOS_WIFI @"/" IP_ADDR_IPv4, IOS_WIFI @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6 ] :
@[ /*IOS_VPN @"/" IP_ADDR_IPv6, IOS_VPN @"/" IP_ADDR_IPv4,*/ IOS_WIFI @"/" IP_ADDR_IPv6, IOS_WIFI @"/" IP_ADDR_IPv4, IOS_CELLULAR @"/" IP_ADDR_IPv6, IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getIPAddresses];
NSLog(@"addresses: %@", addresses);
__block NSString *address;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
address = addresses[key];
if(address) *stop = YES;
} ];
return address ? address : @"0.0.0.0";
}
- (NSString *)getHotSpotIp{
NSDictionary *dict = [self getIPAddresses];
if ( dict ) {
NSArray *keys = dict.allKeys;
for ( NSString *key in keys) {
if ( key && [key containsString:@"bridge"] && [key containsString:@"ipv4"])
return dict[key];
}
}
return @"0.0.0.0";
}
- (NSString *)getHotSpotMask{
NSDictionary *dict = [self getIPAddresses];
if ( dict ) {
NSArray *keys = dict.allKeys;
for ( NSString *key in keys) {
if ( key && [key containsString:@"ifa_netmask"] && [key containsString:@"ipv4"])
return dict[key];
}
}
return @"0.0.0.0";
}
- (NSString *)getHotSpotBroadcastIp{
NSString *ip = [self getHotSpotIp];
NSString *mask = [self getHotSpotMask];
NSArray *ips = [ip componentsSeparatedByString:@"."];
NSArray *masks = [mask componentsSeparatedByString:@"."];
NSMutableString *b_address = [[NSMutableString alloc] init]; //网段地址
for (int index = 0; index < 4; index ++) {
unsigned char value = [ips[index] unsignedCharValue] & [masks[index] unsignedCharValue];
if (index!=3) {
[b_address appendFormat:@"%d.",value];
}else{
unsigned char v = (~[masks[index] unsignedCharValue]) + value;
[b_address appendFormat:@"%d",v];
}
}
return b_address;
}
- (BOOL)flagWithOpenHotSpot
{
NSDictionary *dict = [self getIPAddresses];
if ( dict ) {
NSArray *keys = dict.allKeys;
for ( NSString *key in keys) {
if ( key && [key containsString:@"bridge"])
return YES;
}
}
return NO;
}
@end
获取热点名称
NSString *hotpotName = [[UIDevice currentDevice] name];