美文网首页iOS 多线程网络socket 通信网络
oc下实现局域网udp广播通讯使用开源框架GCDAsyncUdp

oc下实现局域网udp广播通讯使用开源框架GCDAsyncUdp

作者: levinYuXiao | 来源:发表于2015-12-23 11:55 被阅读2279次

UDP简介

UDP是OSI参考模型中一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检查与排序由应用层完成,提供面向事务的简单不可靠信息传送服务。

UDP 协议基本上是IP协议与上层协议的接口。UDP协议适用端口分别运行在同一台设备上的多个应用程序。udp协议通信在发送数据包的时候是不用绑定端口的,系统会自动分配端口,只有在发送消息的时候才需要绑定端口。

网络上已经有编写好的开源类库GCDAsyncSocket 和GCDAsyncUdpSocket 这是GCD版的 比AsyncSocket 和AsyncUdpSocket估计要好用点 用法也很简单,跟http很类似 只要指定服务器的ip和端口 然后再实现各种回调就行。

socket默认情况下就是采用TCP协议,创建之后通信双方的socket会一直保持连接,除非手动close或因为网络原因close,所以,此种状况对服务器而言是有一定资源消耗的,这种模式只适应与对服务器小规模的访问,特别是对于实时性很高的应用,如视频直播、呼叫系统等,而http一般都是短连接的,一次请求完之后客户端便会于服务端端开连接http是凌驾于socket之上的高级协议,而socket是比较底层的通讯方式,只是建立了一个连接通道,具体上面传输什么样的数据,按照什么格式传输,需要你自己定义,所以这就需要重新编写定义服务端与客户端的所应遵循的规定,而http已经被前人们定义使用过了。

GCDAsyncUdpSocket ARC版下载地址(github)

代码

初始化GCDAsyncUdpSocket类

共有四个初始化方法。


- (id)init;

- (id)initWithSocketQueue:(dispatch_queue_t)sq;

- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq;

- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq socketQueue:(dispatch_queue_t)sq;

在使用GCDAsyncUdpSocket传输数据前必须设置代理和代理队列,否则就会报错。

socketQueue是可选的,如果不设置系统会置为NULL,这样GCDAsyncUdpSocket会自动创建一个自己的socketqueue
代理队列 delegateQueue和socketQueue可以一样。

这里已经绑定了delegate 不用在类声明中重复实现这个代理了。

消息发送

若客户端只向服务端发送消息而不用接收到其他的udp消息就可以不用绑定端口

消息发送的方法

该方法只能用于已经连接了的Socket中


- (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

该方法不能用于已经连接了socket中,只能用于没有长连接的socket中

- (void)sendData:(NSData *)data toHost:(NSString *)host port:(uint16_t)port

withTimeout:(NSTimeInterval)timeout tag:(long)tag;

该方法不能用于已经连接了socket中,只能用于没有长连接的socket中

- (void)sendData:(NSData *)data toAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout tag:(long)tag;

例子:


- (IBAction)sendToServer:(id)sender {

     NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:@"my1",            
               @"my1",
              @"my2", @"扣篮大赛",
              @"my3", @"贷记卡",
              @"my4", @"我到了", nil];

    NSString *str = [[SBJson4Writer alloc]stringWithObject:dic];

    NSLog(@"%@",str);

    NSString *host = @"224.0.0.1";

    int port = 3339;

    NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];

//host是在服务端设置的host,port也是服务端绑定的port,上文说过如果客户端不需要接收消息,就不用绑定端口
  
    [udpSocket sendData:data toHost:host port:port withTimeout:-1 tag:tag];

    NSLog(@"SENT (%i): %@", (int)tag, msg);

    tag++;

}

端口绑定和广播开启

方法:

以下方法用于服务端,客户端可以跳过


//绑定端口

- (BOOL)bindToPort:(uint16_t)port error:(NSError **)errPtr;

- (BOOL)bindToPort:(uint16_t)port interface:(NSString *)interface error:(NSError **)errPtr;

//绑定到一个地址,可不设置

- (BOOL)bindToAddress:(NSData *)localAddr error:(NSError **)errPtr;

//绑定到一个HOST和端口

- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr;

//开启广播,若不实现这个方法,默认是关闭的

- (BOOL)enableBroadcast:(BOOL)flag error:(NSError **)errPtr;

例子:


- (IBAction)startServer:(id)sender {

    int port = 3339;

    NSError *error = nil;

    if (![udpServer bindToPort:port error:&error]) {

        NSLog(@"Error starting server (bind): %@", error);

        return;

    }

    if (![udpServer enableBroadcast:YES error:&error]) {

        NSLog(@"Error enableBroadcast (bind): %@", error);

        return;

     }

    if (![udpServer joinMulticastGroup:@"224.0.0.1"  error:&error]) {

        NSLog(@"Error joinMulticastGroup (bind): %@", error);

        return;
  
    }

    if (![udpServer beginReceiving:&error]) {

        [udpServer close];

        NSLog(@"Error starting server (recv): %@", error);

        return;

    }

    NSLog(@"udp servers success starting %hd", [udpServer localPort]);

    isRunning =true;

}

特别说明:

若客户端也要实现接收到服务器发送的消息,也必须实现上述代码,只是不需要实现enableBraodcast:error:这个方法,这样在客户端就可以接收到消息了

另外,客户端的端口号不需要与服务端的端口号保持一致。这时候,从另外一个角度来说,客户端变成了一个简易的服务端。

代理方法 GCDAsyncUdpSocketDelegate

方法:


//在绑定address成功后回调

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address;

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error;

//发送消息后回调,不关心是否成功发送。

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag;

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error;

//在接收到消息后回调这个方法

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data

fromAddress:(NSData *)address

withFilterContext:(id)filterContext;

//在Socket连接关闭后回调

- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError *)error;

例子:

服务端接收到消息:


-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext{

    NSLog(@"%@%d",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],[sock connectedPort]);

    [udpServer sendData:data toAddress:address withTimeout:-1 tag:0];

}

这里做了两件事情,第一件事情是将接收到的消息打印出来,第二件事情就是重新发送一个消息给来源接收。这样目的是是发送端知道接收端己收到消息。从而做出相应的处理。

Demo下载(CSDN)

相关文章

  • oc下实现局域网udp广播通讯使用开源框架GCDAsyncUdp

    UDP简介 UDP是OSI参考模型中一种无连接的传输层协议,它主要用于不要求分组顺序到达的传输中,分组传输顺序的检...

  • 实时Android语音对讲系统架构

    本文属于Android局域网内的语音对讲项目系列,《通过UDP广播实现Android局域网Peer Discove...

  • LWIP UDP偶发丢包问题

    RTTHREAD LWIP实现UDP通讯,使用得socket通讯偶发UDP丢包,用Wireshark监控通讯数据,...

  • Quartz2D - 1

    开源框架CorePlot oc oc使用方法: Swift swift 使用context的方法:

  • AFNetWorking

    开源网络框架 (1)ASIHttpRequest 是一个使用OC封装了CFNetWorking的网络编程框架,可以...

  • OC基础-Foundation对象 和 Core Foundat

    Foundation框架是使用OC实现的 Core Foundation是使用C实现的 Foundation对象 ...

  • VLAN

    局域网通讯广播会消耗大量的网络带宽和CPU资源,用于在二层交换机上分割广播域的技术——VLAN 1、VLAN的实现...

  • Java 网络编程

    1.获取主机名,IP地址 2.UDP编程 客户端 3.UDP实现键盘数据的传输,同时使用广播IP地址实现一对多传输...

  • TDD(1)-实战BIOsocket[局域网中的p2p通信]

    【需求】在相互都不知道自己tcp端口的情况下,局域网中实现p2p通信。【方案】udp+tcp实现局域网的p2p通信...

  • 数据链路层(二)

    一、使用广播信道的数据链路层 广播信道可以进行一对多的通信,因此使用广播信道的局域网被称为共享式局域网。现在具有更...

网友评论

  • e9b8157de80f:您好,我问一下,就我调用GCDAsyncUdpSocket在ios真机调试的时候完全没有问题,但是项目打包上传再下载到手机上运行,会出现<OS_dispath_queue_main:com.apple.main-thhread>的提示,请问您遇到过吗?该怎么解决呢?
  • 冰三尺:我现在有一个需求, 就是客户端监听127.0.0.1:8080, 我们的网页会在safair 浏览器中运行, 会使用ajax 向改地址发送一个请求, 然后同一个手机上安装的我们的App里面会监听这个地址, 这个具体要怎么做, 我自己测试的是App直接是可以传递消息的, 但是safair 里面发送的ajax请求, 我无法监听到?
  • McDan:楼主你好,请问一个问题,是这样的场景,连续不间断往20多个不同的Host发送相同的数据(随机数),发送完一轮后又重复发送,在didReceiveData的时候已经收到发送数据包成功了。经常性在服务器端就是收不到任何包。但是用android客户端是可以一直收到包的。请问这个问题怎么玩
    McDan:@levinYuXiao 好 谢谢
    levinYuXiao:udp是无连接的,不能保证数据一定到 你检查一下你的端口或者服务器ip
    didReceiveData收到的可能是你自己的发出去的包
    建议你和电脑连接同一个网络 然后在电脑端用tcpdump抓包吧
  • 纠结的哈士奇:有个疑问,想请教下楼主

    我发送data,超过9k大小时,就会失败,代理里会提示 message too long,然后直接closeSocket,想问下,这种限制该怎么解决呢?
    levinYuXiao:@纠结的哈士奇 数据量这么大建议使用tcp或者压缩
  • keelZJP:问一个问题,我客户端需要接受服务器发送的广播,客户端已经监听相应的接口,不需要先寻找服务器吗?还是只监听那个端口,等待广播吗
    levinYuXiao:可以下载我的代码示例 修改成你需要的参数 试着跑一下
    注释还是比较详细的
  • Z了个L:@property (nonatomic, strong) GCDAsyncUdpSocket* mConnection; // ump连接
    在前台可以正常工作,进入后台就不工作了,大牛有什么办法解决莫?Voice over IP也勾选了,还是没有用 :grin:
    levinYuXiao:这个是iOS的机制导致的,目前我没有想到有任何好的解决办法,如果你有心得了我们可以交流一下
    xiaoamani@qq.com
  • f43bfc228c91:想请教两个问题:1.UDP客户端需要接收服务端发来的消息,我绑定端口后,返回值一直是YES,但是时间上端口后不是我指定绑定的那个端口号,例如我绑定的是“34567”,但是从服务器接收来看,我是从“65348”(随机端口,不会重复)这个端口发出来的。2.手机锁屏后,绑定的端口被回收了能有好的解决方法吗?退出程序重新进,端口就变成随机的了,但是绑定的时候返回值还是YES。 我是IOS菜鸟,希望高手、大神能够指导下,不胜感激。。。
    levinYuXiao:是的,udp的发送端口是随机的.
    进入到后台之后,连接确实会失效 这个是iOS的机制导致的
  • 5afbca4c118c:你好,我的需求是 能够发送消息给服务端,同时需要接受服务端来的内容! 这个时候,我的socket 是不是应该- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr , 由于已经连接 发送数据的时候 就只需要用这个方法就可以了啊 - (void)sendData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;

  • windyfat:如果是在公网的条件下,需要绑定IP地址吗?如果需要的话,绑定的是手机本地的IP地址,还是服务器的地址?
    levinYuXiao:@XK_summer 可以使用公网ip去发送 但是公网发送的udp可能稳定性就不能保证,因为udp本身就由丢包的问题,所以如果你需要去通过公网,我建议不要使用udp相反的http很不错
    另外,绑定的ip肯定是服务器的ip和端口
  • 理想的不俗人://发送消息后回调,不关心是否成功发送。

    - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag;

    - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error;
    从客户端来说 怎么判断它发送成功?

    -(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
    服务端收到是null 返回null 是客户端发送不成功 还是丢包服务端没接收到
    levinYuXiao:@遵从内心 udp不是面向连接的
    你只能判断客户端是否发送成功 不会知道在网络中会不会丢包
    - (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag;

    - (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error;
    这两个代理就能判断客户端是否发送成功, 可以通过tag判断
    GCDAsyncSocket的作者建议是 用tag区分发送的包 这样你就可以通过tag判断哪个包没有成功发送 去重发
    但是 服务端有没有收到udp的包客户端是不会知道的

    如果服务端收到的data是null 那很有可能是你代码的问题 你可以自己调试一下 可以看是不是socket初始化错误了

本文标题:oc下实现局域网udp广播通讯使用开源框架GCDAsyncUdp

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