UDP 的特性就是效率高 速度快 但是准确率无法保障 正常情况就是发送

这里继续以CocoaAsyncSocket 这个库为例 首先导入库对应的头文件以及继承下相关的代理 然后我们要知道监听的对端的ip和端口号 这里以ip:192.168.0.100 端口号: 8000为案例

#define BOARD_CAST_PORT 8000 // 监听广播的端口
#define BOARD_CAST_ADDRESS @"192.168.0.100" // 监听广播的ip地址
#define UDP_MAX_SEND_FAILED_COUNT  20 // 最大尝试次数

#import <CocoaAsyncSocket/GCDAsyncUdpSocket.h>

@interface UDPServerSocket : NSObject<GCDAsyncUdpSocketDelegate>
@property (nonatomic, weak) id <UDPServerSocketDelegate>delegate;

@property (nonatomic, strong) GCDAsyncUdpSocket * udpSocket;

@property (nonatomic, copy) NSString * clientIP;
@property (nonatomic, assign) uint16_t clientPort;
@property (nonatomic, assign) BOOL isSending;
@property (nonatomic, strong) NSMutableArray * messageQueue;
@property (nonatomic, copy) NSString * currentSendingMessage;
@property (nonatomic, assign) NSInteger times;
@property (nonatomic, assign) NSInteger resendTimes;

开启UDP

// 开启UDP
- (void)startConnectUdpSocket {
    [self disConnect];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    self.udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:queue];
    self.isOpen = YES;
    self.sendFailedCount = 0;
    self.sendCount = 0;
    // 开启广播
    [self startReceiveBroadcast];
}

停止UDP

// 停止
- (void)disConnect {
    if (self.udpSocket) {
        [self.udpSocket close];
    }
    self.isOpen = NO;
    self.sendCount = 0;
    NSLog(@"udpsocket关闭了");
}

这里开启接收广播的时候 需要先绑定下监听的端口号 另外需要允许接收广播(也就是开启对应权限)

- (void)startReceiveBroadcast {
    NSLog(@"startReceiveBroadcast udp server");
    NSError *error = nil;
    if (![self.udpSocket bindToPort:BOARD_CAST_PORT error:&error]) {
        NSLog(@"bindToPort 绑定端口出现错误");
        return;
    }
    if (![self.udpSocket enableBroadcast:YES error:&error]) {
        NSLog(@"udpSocket 允许广播出现错误");
        return;
    }
    // 对端ip
    NSString *tmpAddress = BOARD_CAST_ADDRESS;
    // 关键一行代码,发送广播,即使应用不需要。如果不发送,无法触发是否允许访问本地网络弹框。
    [self sendData:[@{} mj_JSONData] withAddress:tmpAddress onPort:BOARD_CAST_PORT];
    NSError *receiveError = nil;
    if (![self.udpSocket beginReceiving:&receiveError]) {
        NSLog(@"beginReceiving开始接受数据出现错误");
        return;
    }
}

UDP的广播方法代理

#define mark - GCDAsyncUdpSocketDelegate
- (void)sendData:(NSData *)data withAddress:(NSString *)address onPort:(UInt16)port {
    if (!self.isOpen) {
        NSLog(@"udp关闭了");
        return;
    }
    [self.udpSocket sendData:data toHost:address port:port withTimeout:UDP_TIME_OUT tag:HOMEVISION_BROADCAST_TAG];
}

// 连接成功
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address {
    NSLog(@"didConnectToAddress");
}

// 连接断开
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError * _Nullable)error {
    NSLog(@"didNotConnect");
}

// 通过tag发送消息
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
    self.sendFailedCount = NUMBER_ZERO;
    NSLog(@"didSendDataWithTag");
}

为了防止 一直发送或者接收消息 这里给一个最大的尝试次数 超过之后 就直接断开连接

// 发送消息失败的回调
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error {
    self.sendFailedCount++;
    if (self.sendFailedCount >= UDP_MAX_SEND_FAILED_COUNT) {
        NSLog(@"发送失败,次数超过 %ld了",UDP_MAX_SEND_FAILED_COUNT);
        // 手动关闭连接
        [self disConnect];
    }
    if (tag == HOMEVISION_BROADCAST_TAG) {
        NSLog(@"发送数据失败");
    }
}

// upd连接关闭
- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError  * _Nullable)error {
    NSLog(@"udpSocketDidClose %@",error.localizedDescription);
}

// upd接收到消息
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext {
    if (address.length == NUMBER_ZERO) {
        NSLog(@"接受的数据地址为空");
        return;
    }
    NSString *hostAddress = [GCDAsyncUdpSocket hostFromAddress:address];
    uint16_t port = [GCDAsyncUdpSocket portFromAddress:address];
    // 判断是否为需要接收广播的设备ip
    if ([hostAddress isEqualToString:BOARD_CAST_ADDRESS] && (port == HD_BOARD_CAST_PORT)) {
        if (data.length == NUMBER_ZERO) {
            NSLog(@"接收的数据为空");
            return;
        }
        int messageCode = *(int *)([data bytes]);
        NSString *addressString = [NSString stringWithFormat:@"receive data from hostaddress is %@ port is %hu",hostAddress,port];
        NSLog(@"接收数据的地址为 %@",addressString);
    }
}

UDP的整体发送消息流程比较简单 不过开发者需要申请对应的的权限还是比较麻烦

我会单独出一期关于权限申请的流程。