美文网首页iOSiOS 物联网程序员
iOS之UDP广播作为客户端

iOS之UDP广播作为客户端

作者: shushuzhen | 来源:发表于2016-10-28 22:22 被阅读2373次

    重大更新前的严肃解释

    简书里的好友,私信我说好久没有看我更新了,这段时间不是忙,只是感觉自己工作上的很多东西,似乎没有什么精华拿来分享(这似乎是一个看起来比较正常的借口),严肃脸.jpg

    懒癌该治

    公司做的智能家居,一开始的时候是用wifi将设备入网,然后用wifi或者4G来控制硬件设备,但是用wifi的话对网络的要求又是极高的。由于受到三大网络运营商的限制,wifi在一定程度上是没有那么好的网速的。于是物联网在前几年就开始寻找更好的协议去控制硬件,一开始的socket还是很流行的,至今为止还是用的不错。现在用的比较好的就是mqtt协议,由于这方面的开源还比较少,小公司用这个协议的还是少数。说了这么多,还是回归主题,直接在局域网内如何用UDP发送数据包给服务端,从而高效快速的控制设备。

    某百科上的UDP是这样解释的:


    其实UDP,在第三方的基础上写是十分简单的,因为底层的都已经写好了,我们只需要专注于自己的业务逻辑上即可。github上下载量多的主要是CocoaAsyncSocket,他有基于Runloop的还有一个是基于GCD的,大家看自己需求来。我这里是基于GCD的里面,大家根据github上的操作,直接将三方拉入自己的项目或者用pods导入均可。我在工程里用的就是
    GCDAsyncUdpSocket。
    下面我直接贴上代码:
    #import <Foundation/Foundation.h>
    #import "GCDAsyncUdpSocket.h"
    
    @protocol UDPClientSocketDelegate <NSObject>
    
    - (void)clientSocketDidReceiveMessage:(NSString *)message andPort:(uint16_t)port withHost:(NSString *)hostIP;
    
    @end
    
    @interface UDPClientSocket : NSObject <GCDAsyncUdpSocketDelegate>
    
    @property (nonatomic,assign)NSInteger times;
    
    @property (nonatomic, weak)id <UDPClientSocketDelegate>delegate;
    
    @property (nonatomic, strong)GCDAsyncUdpSocket * udpSocket;
    
    @property (nonatomic, copy) NSString *mHost;
    @property (nonatomic, assign) int mPort;
    
    +(UDPClientSocket *)shareInstance;
    - (void)getAddressAndPort:(NSString *)hostID;
    
    - (void)sendMessageToHost:(NSString *)host WithPort:(uint16_t)port transData:(NSDictionary *)dataPackage;
    
    //- (void)sendMessage:(NSString *)message;
    
    - (void)disconnect;
    
    @end
    

    因为在主机通信里,只要有控制设备或者查询设备的状态就会有UDP的发送,所以这在这个.h文件里写了一个代理,可以直接把服务端返回的包直接在自己需要的地方获取。


    udp delegate.png

    下面看.m文件:

    #import "UDPClientSocket.h"
    
    @implementation UDPClientSocket
    
    +(UDPClientSocket *)shareInstance{
    
        static UDPClientSocket * clientUdp = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            clientUdp = [[self alloc]init];
        });
        return clientUdp;
    }
    
    // 探测是否连通
    - (void)getAddressAndPort:(NSString *)hostID{
        
        NSString *url = [NSString stringWithFormat:@"%@/host/GetHostLocation",getIP()];
        NSDictionary *params = @{kSessionID:getMySessionID(),@"HostID":hostID};
        [SZRequest PostURLForNetRequest:url AndParams:params block:^(id result) {
            NSLog(@"主机IP:%@",result);
            if (Kerrcode==0) {
                _mHost = result[@"HostIP"];
                _mPort = [result[@"HostPort"] intValue];
                // 探测数据包
                NSDictionary *params = @{@"devID":@"",@"requestCode":@"8900",@"userName":[mUserDefaults objectForKey:UserName]};
                [self sendMessageToHost:_mHost WithPort:(uint16_t)_mPort transData:params];
                [mUserDefaults setObject:_mHost forKey:KhostIP];
                [mUserDefaults setInteger:_mPort forKey:KhostPort];
                [mUserDefaults synchronize];
            }
        } andBlock:^(NSError *error) {
            NSLog(@"error = %@",error);
        }];
    }
    - (void)sendMessageToHost:(NSString *)host WithPort:(uint16_t)port transData:(NSDictionary *)dataPackage{
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        self.udpSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:queue];
        
        NSLog(@"data=%@ %@ %d",dataPackage,host,port);
        NSData *data = [[CJSONDataSerializer serializer] serializeDictionary:dataPackage];
        [self.udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:0];
        [self.udpSocket receiveOnce:nil];
    }
    
    //- (void)sendMessage:(NSString *)message{
    //    NSData * data = [message dataUsingEncoding:NSUTF8StringEncoding];
    //    [self.udpSocket sendData:data toHost:@"192.168.7.113" port:8011 withTimeout:-1 tag:0];
    //}
    
    - (void)disconnect{
        [self.udpSocket closeAfterSending];
    }
    
    - (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext{
        NSString * ip = [GCDAsyncUdpSocket hostFromAddress:address];
        uint16_t port = [GCDAsyncUdpSocket portFromAddress:address];
        
        NSString * message = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"接收到服务端的消息:ip:%@  port:%d  message:%@",ip,port,message);
        
        if ([self.delegate respondsToSelector:@selector(clientSocketDidReceiveMessage:andPort:withHost:)]) {
            [self.delegate clientSocketDidReceiveMessage:message andPort:port withHost:ip];
        }
        
        [self.udpSocket receiveOnce:nil];
    //    [self.udpSocket beginReceiving:nil];
    }
    

    先来说*+(UDPClientSocket )shareInstance这个,这个大家都知道是单例,因为在项目里多处使用,为了避免在别的文件写更多的冗余代码,所以用单例是很方便的。
    // 探测是否连通
    **- (void)getAddressAndPort:(NSString *)hostID ** 这个之所以写到这里,是因为自己项目的需求,每次去发控制请求时,应该先去探测自己是不是和主机咋同一个局域网内,如果收到回包,就说明是在同一个局域网,这样就可以本地控制主机了。
    **- (void)sendMessageToHost:(NSString *)host WithPort:(uint16_t)port transData:(NSDictionary *)dataPackage **这个就是发送数据包自己写的方法了,调用这个方法时,需要传进来服务器端的ip和服务端的port,还有就是需要传给服务端的数据包。我这里之所以传字典进来,是因为我这边传的数据就是json字符串,然后将字典转成NSData格式,服务端就能收到我这边发送的数据了。 *NSData data = [[CJSONDataSerializer serializer] serializeDictionary:dataPackage];这个是NSDictionary转NSData,这个方法也是用的第三方库,也很好找。

    delegate.png
    这个代理方法是写在收到服务端回包之后,就可以把收到的数据传到自己调用这个代理方法的类里了。

    UDPClientSocket使用

    在自己要用到udp的类里,先import "UDPClientSocket.h"文件,然后遵循UDPClientSocketDelegate,将代理方法写进去,大家就可以很愉快的使用啦。

    使用.png
    后续会更新一篇服务端,结合自己的客户端,可以用电脑端做服务端,手机做客户端,相互通信。当然,我会继续把自己的业务逻辑这一块写完之后,再来写总结。
    最后,希望看到这篇文章的小伙伴给我推荐一些自己觉得不错的书籍或者大神的博客或者有趣好玩的一切。

    来吧,一起玩代码吧!!!

    附上demo:
    链接: https://pan.baidu.com/s/1cvQPw6 密码: jtu7
    要是有不明白的敬请留言呀~~~

    相关文章

      网友评论

      • 柒夏_2b13:楼主用Native.js实现udp收发广播包有没有实例代码看看啊?我现在可以发送出去但是接收不到。大恩不言谢:pray:
        shushuzhen:@柒夏_2b13 不客气呢 相互学习
        柒夏_2b13:@shushuzhen 好的,打扰啦
        shushuzhen:@柒夏_2b13 native.js的没有写过哦
      • xhzth70911:加你qq有验证,想向学习一下物联网方面的知识,谢谢。 我的qq 506279370
      • 普通上班族老王:大妹子哟,现在就是我接手你的项目了!!:joy:
        要重构项目,头疼,看着以前代码:skull:
        shushuzhen:@644fce8a467a 大神加油,把我这个菜鸡的项目焕然一新
      • 我来也风行:您好! 我iPhone与iPhone,iPad, iMac 发UDP都可以正常使用,为什么和我们公司自己的WIFi产品和windows PC端他们收不到我发送的UDP呢?请指教...
        shushuzhen:@我来也风行 我建议你看看安卓那边的 因为代码其实就是这样的 看看是不是哪里弄错了
        我来也风行:@ssZhen 路由器的地址:192.168.8.66 端口:58899 没有错了. 路由器的地址自己获取的, 端口是硬件工程师说的.

        shushuzhen:@我来也风行 仔细看看地址和端口是否一样
      • snail小菜:大神麻烦加下我QQ:1359417701
        shushuzhen:@snail小菜 我是菜鸡 不是大神 哈哈
      • 8fc99f582558:能否给个QQ 有些问题需要请教一下。
        8fc99f582558:@ssZhen 你加我吧,80398694:blush:
        8fc99f582558:@ssZhen 有验证。。
        shushuzhen:@xia当 1260585440
      • 8fc99f582558:小姐姐 你了解过茄子快传么,我目前想用UDP广播的方式发现硬件,实现两个手机传输数据,不知道小姐姐有什么建议
        shushuzhen:@xia当 udp广播 只要一个做客户端 一个服务端 就能相互通信啦
        shushuzhen:@xia当 这个没有了解过 不过可以相互讨论一下
      • 99871c1ba165:姐,给个源码学习下,复制这个.m .h报错啊!拜托
        shushuzhen:@天空_e890 好的 我等会把它放到百度云盘上然后更新一下文章给链接下载呀
      • vision_colion:好厉害的样子~
      • ____Rainbow:你好 请问你尝试过用iphone手机做热点 然后互相发送广播么?
        shushuzhen:一个是客户端 一个是服务端就可以的呢
      • Mr吴标:有个问题,想请教一下。iOS10.2(最新系统)好像不支持UDP广播了,我通过抓包工具查看,无法发送UDP广播包了
        Mr吴标:@ssZhen 我是UDP广播
        9703af692b11:@ssZhen 你好,想请教一下,关于 iOS10.2(最新系统)是否支持UDP广播这个问题?具体是什么情况,在写项目的时候,碰到了这个问题,iOS10.2以下的 都没有问题,iOS10.2就不支持了。
        shushuzhen:@Mr吴标 我的是10.2 好像应该是可以的吧 我再试试呀 你告诉我你的广播是怎么发的呀
      • YuGHo:博主有没有手机端demo源码
        shushuzhen:@YuGHo 这个.h和.m文件就是手机端的源码了
      • b0df936fea4d:博主能不能给一份完整的源码呢?新手学习~谢谢
        代码移动工程师:@ssZhen 对服务端的来一份
        shushuzhen:@b0df936fea4d 完整的源码是说还要服务端的吗
      • 流星大石头:不错,喜欢
        shushuzhen:@流星大石头 谢谢 其实用起来之后觉得这个还是很好用的 不难

      本文标题:iOS之UDP广播作为客户端

      本文链接:https://www.haomeiwen.com/subject/borkuttx.html