// // SocketUDPServerClient.m // socket_server_client // // Created by lujunjie on 2016/11/26. // Copyright © 2016年 lujunjie. All rights reserved. // #import "SocketUDPServerClient.h" #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import "UDPProtocolHerader.h" #import <ifaddrs.h> #include <net/if.h> @interface SocketUDPServerClient() { in_addr_t broadcastClientAddr; // 发送广播的地址 } @end @implementation SocketUDPServerClient int serverSockfd = -1; /** 启动服务监听接收广播 */ - (void)startUDPServer { // 第一步:打开套节字描述 serverSockfd = socket(AF_INET, SOCK_DGRAM, 0);// 协议族、数据报、0 if(serverSockfd < 0) { NSLog(@"error:打开套节字描述符失败socket()"); } NSLog(@"打开套节字描述sockfd:%d",serverSockfd); // 第二步:设置广播包 int opt; if ((setsockopt(serverSockfd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(int)))< 0) { NSLog(@"error:广播包setsockopt"); } // 第三步: bind struct sockaddr_in serveraddr; serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons(30000); // 5000~655355 serveraddr.sin_addr.s_addr = INADDR_ANY; // 当你调用bind()函数绑定IP时使用INADDR_ANY ,明接收来自任意IP、任意网卡的发给指定端口的数据。作为发送端,当用调用bind()函数绑定IP时使用INADDR_ANY,表明使用网卡号最低的网卡进行发送数据,也就是UDP数据广播。 if ((bind(serverSockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) { NSLog(@"error:bind"); } [NSThread detachNewThreadSelector:@selector(recvFromThread) toTarget:self withObject:nil]; } - (void)recvFromThread { struct sockaddr_in clientaddr; socklen_t clientaddrLen = sizeof(clientaddr); int msgHeaderSize = sizeof(CC_searchBrodcastHeader); char *buf = (char *)malloc(msgHeaderSize); memset(buf, 0, sizeof(msgHeaderSize)); long recvSize = 0; while (1) { if ([self selectReadSockfd:serverSockfd]) { long recvRet = recvfrom(serverSockfd,buf + recvSize,msgHeaderSize -recvSize, 0,(struct sockaddr *)&clientaddr,&clientaddrLen); if (recvRet <= 0) { NSLog(@"<= 0 error:recvfrom errorcode:%zi",recvRet); sleep(2); continue; } recvSize += recvRet; if (recvSize >= msgHeaderSize) { NSString *ipaddr = [self getIPAddress]; const char *addr = [ipaddr UTF8String]; NSString* tempIPString=[NSString stringWithCString:inet_ntoa(clientaddr.sin_addr) encoding:NSUTF8StringEncoding]; NSLog(@"RECV:::::IPADDR: %@",tempIPString); if (clientaddr.sin_addr.s_addr != inet_addr(addr)) {// 自己不能收到自己的广播 // 接收到的广播 broadcastClientAddr = clientaddr.sin_addr.s_addr; [self recvSuccessWithBuf:buf]; } // 清空出来 recvSize = 0; memset(buf, 0, sizeof(msgHeaderSize)); } } } } - (BOOL)selectReadSockfd:(int)sockfd { fd_set read_set; struct timeval tmval; tmval.tv_sec = 2; tmval.tv_usec = 0; FD_ZERO(&read_set); // 将指定的文件描述符集清空 FD_SET(sockfd,&read_set); // 用于在文件描述符集合中增加一个新的文件描述符 int ret =select(sockfd+1,&read_set, NULL, NULL,&tmval); if (ret <= 0) { NSLog(@"<= 0 error:select errorcode:%zi",ret); return NO; } if (FD_ISSET(sockfd, &read_set)) { return YES; } return NO; } - (BOOL)selectWriteSockfd:(int)sockfd { fd_set w_set; struct timeval tmval; tmval.tv_sec = 2; tmval.tv_usec = 0; FD_ZERO(&w_set); // 将指定的文件描述符集清空 FD_SET(sockfd,&w_set); // 用于在文件描述符集合中增加一个新的文件描述符 int ret =select(sockfd+1,NULL, &w_set, NULL,&tmval); if (ret <= 0) { NSLog(@"<= 0 error:select errorcode:%zi",ret); return NO; } if (FD_ISSET(sockfd, &w_set)) { return YES; } return NO; } - (void)recvSuccessWithBuf:(char *)buf { CC_searchBrodcastHeader *header = (CC_searchBrodcastHeader *)buf; char pheader[3] = {0}; memcpy(pheader,header->protocolHeader, sizeof(pheader)); memset(pheader+2, 0, 1); NSString *protocolHeader=[NSString stringWithCString:pheader encoding:NSASCIIStringEncoding]; if ([protocolHeader isEqualToString:@"CC"]) { if (header->controlMask == udp_broadcast_request) { [self sendtoClient]; }else if(header->controlMask == udp_broadCast_reply) { struct in_addr temp_in_addr; memset(&temp_in_addr, 0, sizeof(temp_in_addr)); memcpy(&temp_in_addr, &header->IP, sizeof(header->IP)); NSString* tempIPString=[NSString stringWithCString:inet_ntoa(temp_in_addr) encoding:NSUTF8StringEncoding]; NSLog(@"对方的IP地址是: %@",tempIPString); } } } - (void)sendtoClient { // 回复 NSString *ipaddr = [self getIPAddress]; const char *localAddr = [ipaddr UTF8String]; CC_searchBrodcastHeader header; memset(&header, 0, sizeof(header)); header.controlMask = udp_broadCast_reply; header.protocolHeader[0] = 'C'; header.protocolHeader[1] = 'C'; header.IP = inet_addr(localAddr); char *buf = (char *)malloc(sizeof(header)); memcpy(buf, &header, sizeof(header)); if ([self selectWriteSockfd:serverSockfd]) { if ([self sendtoWithSockfd:serverSockfd Buffer:buf size:sizeof(header) addr:broadcastClientAddr]) { NSLog(@"=====IP:%@",ipaddr); NSLog(@"=====IP发送成功"); } } } - (BOOL)sendtoWithSockfd:(int)sockfd Buffer:(char *)buffer size:(int)size addr:(in_addr_t)addr { struct sockaddr_in clientaddr; clientaddr.sin_family = AF_INET; clientaddr.sin_port = htons(30000); clientaddr.sin_addr.s_addr = addr; long sendSize = 0; while (sendSize < size) { long retSize = sendto(sockfd, buffer+sendSize, size-sendSize, 0, (struct sockaddr*)&clientaddr, sizeof(clientaddr)); if (retSize <= 0) { continue; } sendSize += retSize; if (sendSize >= size) { // 发送成功 return true; } } return true; } int clientSockfd = -1; /** 发送广播 */ - (void)searchUDPServer { // 第一步:打开套节字描述 clientSockfd = socket(AF_INET, SOCK_DGRAM, 0);// 协议族、数据报、0 if(clientSockfd < 0) { NSLog(@"error:打开套节字描述符失败socket()"); } NSLog(@"打开套节字描述sockfd:%d",clientSockfd); // 第二步:设置广播包 int clientOpt; if ((setsockopt(clientSockfd, SOL_SOCKET, SO_BROADCAST, &clientOpt, sizeof(int)))< 0) { NSLog(@"error:广播包setsockopt"); } if ([self selectSockfd:clientSockfd]) { CC_searchBrodcastHeader header; memset(&header, 0, sizeof(header)); header.controlMask = udp_broadcast_request; header.protocolHeader[0] = 'C'; header.protocolHeader[1] = 'C'; char *buf = (char *)malloc(sizeof(header)); memcpy(buf, &header, sizeof(header)); if ([self sendtoWithSockfd:clientSockfd Buffer:buf size:sizeof(header) addr:INADDR_BROADCAST]) { NSLog(@"=====广播发送成功"); } } close(clientSockfd); clientSockfd = -1; } - (BOOL)selectSockfd:(int)sockfd { fd_set w_set; struct timeval tmval; tmval.tv_sec = 2; tmval.tv_usec = 0; FD_ZERO(&w_set); // 将指定的文件描述符集清空 FD_SET(sockfd,&w_set); // 用于在文件描述符集合中增加一个新的文件描述符 int ret =select(sockfd+1,NULL, &w_set, NULL,&tmval); if (ret <= 0) { NSLog(@"<= 0 error:select errorcode:%zi",ret); return NO; } if (FD_ISSET(sockfd, &w_set)) { return YES; } return NO; } - (NSString *)getIPAddress { NSArray *searchArray = @[@"en1/ipv4",@"en0/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"; } - (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; 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 = @"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 = @"ipv6"; } } if(type) { NSString *key = [NSString stringWithFormat:@"%@/%@", name, type]; addresses[key] = [NSString stringWithUTF8String:addrBuf]; } } } // Free memory freeifaddrs(interfaces); } return [addresses count] ? addresses : nil; } @end