socket UDP简单通讯

时间:2022-07-17 15:39:45
//
//  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