zoukankan      html  css  js  c++  java
  • socket UDP简单通讯

    //
    //  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
  • 相关阅读:
    认识岗位-带你一起偷窥产品经理的日常
    SpringBoot单元测试踩坑
    Oracle“ORA-38104: 无法更新ON子句中引用的列”解决方式
    SXSSFWorkbook使用补充
    JAVA复制字符串并用指定字符串拼接
    一个简单for循环的时间复杂度
    SXSSFWorkbook的简单使用
    AOP行为日志
    antV G2 为柱状图添加背景颜色
    AntV G2 图表tooltip重命名
  • 原文地址:https://www.cnblogs.com/-ljj/p/6107032.html
Copyright © 2011-2022 走看看