我得到了广播工作(在 iOS 10 上)
我发布了第二个答案,因为我找到了一种在个人热点模式下在 iOS 10 上进行广播的方法。解决方案有点复杂,但这是我如何让它工作的:
- 用于
getifaddrs(if)
循环遍历所有接口 ( do {} while (if = if->ifa_next)
)
- 拒绝环回接口 (
if->ifa_flags & IFF_LOOPBACK
)
- 只过滤支持广播的接口 (
if->ifa_flags & IFF_BROADCAST
)
- 创建一个套接字
int sock = socket()
- 启用广播:
int yes = 1; setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes)
- 连接到广播地址:
connect(sock, if->ifa_dstaddr, if->ifa_dstaddr->sa_len)
- 现在使用
send()
发送您的消息!
我已经确认这在 iPhone 连接到 WiFi 网络时以及在个人热点模式下都有效。
完整示例代码
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>
-(IBAction)sayHello:(id)sender {
// fetch a linked list of all network interfaces
struct ifaddrs *interfaces;
if (getifaddrs(&interfaces) == -1) {
NSLog(@"getifaddrs() failed: %s", strerror(errno));
return;
}
// loop through the linked list
for(struct ifaddrs *interface=interfaces; interface; interface=interface->ifa_next) {
// ignore loopback interfaces
if (interface->ifa_flags & IFF_LOOPBACK) continue;
// ignore interfaces that don't have a broadcast address
if (!(interface->ifa_flags & IFF_BROADCAST) || interface->ifa_dstaddr == NULL) continue;
// check the type of the address (IPv4, IPv6)
int protocol_family;
struct sockaddr_in ipv4_addr = {0};
struct sockaddr_in6 ipv6_addr = {0};
struct sockaddr *addr;
if (interface->ifa_dstaddr->sa_family == AF_INET) {
if (interface->ifa_dstaddr->sa_len > sizeof ipv4_addr) {
NSLog(@"Address too big");
continue;
}
protocol_family = PF_INET;
memcpy(&ipv4_addr, interface->ifa_dstaddr, interface->ifa_dstaddr->sa_len);
ipv4_addr.sin_port = htons(16000);
addr = (struct sockaddr *)&ipv4_addr;
char text_addr[255] = {0};
inet_ntop(AF_INET, &ipv4_addr.sin_addr, text_addr, sizeof text_addr);
NSLog(@"Sending message to %s:%d", text_addr, ntohs(ipv4_addr.sin_port));
}
else if (interface->ifa_dstaddr->sa_family == AF_INET6) {
if (interface->ifa_dstaddr->sa_len > sizeof ipv6_addr) {
NSLog(@"Address too big");
continue;
}
protocol_family = PF_INET6;
memcpy(&ipv6_addr, interface->ifa_dstaddr, interface->ifa_dstaddr->sa_len);
ipv6_addr.sin6_port = htons(16000);
addr = (struct sockaddr *)&ipv6_addr;
char text_addr[255] = {0};
inet_ntop(AF_INET6, &ipv6_addr.sin6_addr, text_addr, sizeof text_addr);
NSLog(@"Sending message to %s:%d", text_addr, ntohs(ipv6_addr.sin6_port));
}
else {
NSLog(@"Unsupported protocol: %d", interface->ifa_dstaddr->sa_family);
continue;
}
// create a socket
int sock = socket(protocol_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == -1) {
NSLog(@"socket() failed: %s", strerror(errno));
continue;
}
// configure the socket for broadcast mode
int yes = 1;
if (-1 == setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof yes)) {
NSLog(@"setsockopt() failed: %s", strerror(errno));
}
// create some bytes to send
NSString *message = @"Hello world!\n";
NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding];
if (-1 == connect(sock, addr, addr->sa_len)) {
NSLog(@"connect() failed: %s", strerror(errno));
}
// send the message
ssize_t sent_bytes = send(sock, data.bytes, data.length, 0);
if (sent_bytes == -1) {
NSLog(@"send() failed: %s", strerror(errno));
}
else if (sent_bytes<data.length) {
NSLog(@"send() sent only %d of %d bytes", (int)sent_bytes, (int)data.length);
}
// close the socket! important! there is only a finite number of file descriptors available!
if (-1 == close(sock)) {
NSLog(@"close() failed: %s", strerror(errno));
}
}
freeifaddrs(interfaces);
}
此示例方法在端口 16000 上广播 UDP 消息。
对于调试,您可以使用该工具socat
。只需在您的计算机上运行以下命令(应该与手机在同一网络上):
socat UDP-RECV:16000 STDOUT