iOS wifi(socket)通讯

1.集成第三方开源库 CocoaAsyncSocket

CocoaAsyncSocket

2.对第三库进行二次封装

RZSocketTools.h

//
//  RZSocketTools.h
//  UDPTest
//
//  Created by  on 17/3/8.
//  Copyright © 2017年 刘. All rights reserved.
//

#import <Foundation/Foundation.h>

#define AUTO_RECONNECT_NAME @"AUTO_RECONNECT_NAME"

typedef void(^ConnectStateBlock)(BOOL connectState);
typedef void(^RBServersBlock)(NSMutableArray *servers);
typedef void(^RBServerMsgBlock)(NSDictionary * msgDic);
typedef void(^RBGetEMIdMsgBlock)(NSDictionary *msgDic);

@interface RZSocketTools : NSObject

@property (nonatomic,copy) ConnectStateBlock   connectStateBlock;           //链接状态更新
@property (nonatomic,copy) RBServersBlock       serverBlock;                //获取搜索到的设备
@property (nonatomic,copy) RBGetEMIdMsgBlock    getIdBlock;                 //获取到环信ID block
@property (nonatomic,copy) NSMutableArray       *serverArrs;                //UDP监听到的服务器数组
@property (nonatomic,assign)  BOOL              connectAuto;                //是否自动重连
@property (nonatomic,assign)  BOOL              connectState;               //机器人链接状态
@property (nonatomic,assign)  BOOL              udpReciveState;             //判断是否有接受到UDP,没有接受到 弹出AirKiss 网络链接框


+ (instancetype)shareSocketTool;

- (void)scanNearServers;                                                            //搜索周围机器人
- (void)close;
- (void)connectRobot:(NSString*)robotName;                                          //链接机器人
- (BOOL)sendMsgToRobot:(NSDictionary*)msgDic CallBack:(RBServerMsgBlock)msgBlock;          //发送信息给机器人
- (void)disConnectRobot;                                                                //断开机器人链接

@end

RZSocketTools.m

//
//  RZSocketTools.m
//  UDPTest
//
//  Created by on 17/3/8.
//  Copyright © 2017年 . All rights reserved.
//

#import "RZSocketTools.h"
#import "GCDAsyncUdpSocket.h"
#import "GCDAsyncSocket.h"
#import "common.h"
#import "Socket_Defines.h"

#define UDP_PORT 30695
#define TCP_PORT 30690

#define IOS_CLIENT @"X-ZERO-IOS"

#define SERVER_NAME 

#define TCP_FIRST_TAG @"0"

#define AUTO_CONNECT_MAX 6      //自动重连数据
#define AUTO_TIME_SPACE 5       //自动重连时间间隔
#define SEND_HEART_TIME_SPACE 13    //发送心跳包数据时间间隔 13
#define ACCEPT_HEART_TIME_SPACE 40   //未收到心跳包数据时间间隔  40


@interface RZSocketTools()<GCDAsyncUdpSocketDelegate,GCDAsyncSocketDelegate>
{
    GCDAsyncUdpSocket       *asyncUdpSocket;        //UDPSocket
    GCDAsyncSocket           *asyncSocket;          //TCPSocket
    
    RBServerMsgBlock       msgReveiceBlock;                 //消息回调

    NSTimer                   *sendTimer;              //定时想服务器发送消息,保持服务器socket不断
    NSString                  *connectName;             //连接的设备名称
    
    NSDictionary                *currentPacketHead;
//    BOOL                      sendHeartFlag;            //发送心跳包数据标志量 SEND_HEART_TIME_SPACE后不发送
    NSTimer                     *noSendTimer;   //断开连接的轮训   60s未收到自动断开
	
	BOOL						startAutoConnect;				//开机自动重连
}
@end

static RZSocketTools *socketTool = nil;
@implementation RZSocketTools

+ (instancetype)shareSocketTool
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        socketTool = [[super allocWithZone:NULL] init];
        [socketTool initSocket];
    });
    //lxaddlx
    if (!socketTool) {
       
            socketTool = [[super allocWithZone:NULL] init];
            [socketTool initSocket];
       
    }
    return socketTool;
}

- (void)close{
    socketTool = nil;
}

+ (id)allocWithZone:(struct _NSZone *)zone
{
    return [RZSocketTools shareSocketTool];
}

- (id)copyWithZone:(struct _NSZone *)zone
{
    return  [RZSocketTools shareSocketTool];
}

#pragma mark - InitSocket
- (void)initSocket
{
    _serverArrs = [NSMutableArray arrayWithCapacity:15];
    
    asyncUdpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    NSError *err = nil;
    [asyncUdpSocket enableBroadcast:YES error:&err];
    [asyncUdpSocket bindToPort:UDP_PORT error:&err];
    
    asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    
    _connectState = NO;
    
    //是否收到UDP
    _udpReciveState = NO;
    
    //自动连接
    //这句是控制启动APP后首次扫描时,自动连接最后一次连接的设备的
	startAutoConnect = YES;

    sendTimer = [NSTimer timerWithTimeInterval:SEND_HEART_TIME_SPACE target:self selector:@selector(sendHeartData) userInfo:nil repeats:YES];   //定义心跳轮询
    

    connectName = [NSString string];
    
    //lx开心跳包,自动重连存到沙盒的机器人
    [socketTool autoConnectTcp:[UserDataHelper getUserData:AUTO_RECONNECT_NAME]];                //设置自动重连


}

#pragma mark - ConnectTcp

//扫描周围的设备
- (void)scanNearServers
{
    [_serverArrs removeAllObjects];
    [asyncUdpSocket receiveOnce:nil];

}

//根据机器名字来链接TCP
- (void)connectRobot:(NSString*)robotName
{
    NSMutableArray *arr = _serverArrs;
    for (NSDictionary *dic in _serverArrs) {
        if ([dic[@"name"] isEqualToString:robotName]){
            NSError *err;
            [asyncSocket disconnect];
            //收到消息后反馈给robot
            NSData *clientData = [IOS_CLIENT dataUsingEncoding:NSUTF8StringEncoding];
            [asyncUdpSocket sendData:clientData toHost:dic[@"ip"] port:UDP_PORT withTimeout:2 tag:1];
            
            [NSThread sleepForTimeInterval:0.8];
            BOOL flag = [asyncSocket connectToHost:dic[@"ip"] onPort:TCP_PORT error:&err];
            
            connectName = robotName;
            NSLog(@"链接TCP%d,%@",flag,err);
            break;
        }
    }
}

//给rb发送指令并且回调
- (BOOL)sendMsgToRobot:(NSDictionary*)msgDic CallBack:(RBServerMsgBlock)msgBlock
{
//    NSLog(@"给机器人发消息(未连接)%@",msgDic);
    if (!_connectState) {
        return NO;
    }
    
    NSLog(@"lxadd给机器人发的信息%@",msgDic);
    msgReveiceBlock = msgBlock;
    [socketTool sendMsgToRobot:msgDic];
    
    return YES;
}


//断开TCP直接的链接
- (void)disConnectRobot
{
    NSLog(@"发断开连接");
    [asyncSocket disconnect];
    socketTool.connectAuto = NO;
}

//自动重连
- (void)autoConnectTcp:(NSString*)robotName
{
    if (_connectAuto && ([robotName isEqualToString:[UserDataHelper getUserData:AUTO_RECONNECT_NAME]])) {//存到沙盒的机器人名字
        for (NSInteger i = 0; i < AUTO_CONNECT_MAX; i++) {
            NSTimeInterval autoTime = i * AUTO_TIME_SPACE + i*i;        //重连4次
            NSTimer *autoTimer = [NSTimer timerWithTimeInterval:autoTime target:self selector:@selector(sendAutoConnect:) userInfo:nil repeats:NO];   //定义心跳重连
            [[NSRunLoop mainRunLoop] addTimer:autoTimer forMode:NSRunLoopCommonModes];
        }
    }
}


- (void)sendAutoConnect:(NSTimer*)timer
{
   // NSLog(@"自动重连的心跳包");
    if (_connectState) {
        [timer invalidate];
        
        return;
    }
    [socketTool connectRobot:connectName];
}



#pragma mark - UDPDelegate
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(nullable id)filterContext
{
//    NSLog(@"lxaddl--udpDidReceiveData");
    //这里需要把robot-name,IP地址 记录下来;
    NSString *robotName = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    [asyncUdpSocket receiveOnce:nil];
    if ([robotName hasPrefix:SERVER_NAME] && [GCDAsyncUdpSocket isIPv4Address:address]) {
        _udpReciveState = YES;
        if (![_serverArrs containsObject:@{@"name":robotName,@"ip":[GCDAsyncUdpSocket hostFromAddress:address]}]) {
            NSMutableArray *testArr = _serverArrs;
            NSDictionary *dict = @{@"name":robotName,@"ip":[GCDAsyncUdpSocket hostFromAddress:address]};
            [_serverArrs addObject:@{@"name":robotName,@"ip":[GCDAsyncUdpSocket hostFromAddress:address]}];
//            _serverArrs = [_serverArrs mutableCopy];
            if (socketTool.serverBlock) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    socketTool.serverBlock(_serverArrs);
                });
            }
			if ([robotName isEqualToString:[UserDataHelper getUserData:AUTO_RECONNECT_NAME]] && startAutoConnect) {		//开机自动连接
//                NSLog(@"开机自动连接");
//                NSString *str = [UserDataHelper getUserData:AUTO_RECONNECT_NAME];
//                
//                [self connectRobot:[UserDataHelper getUserData:AUTO_RECONNECT_NAME]];
			}
        }
    }
    
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
    NSLog(@"消息已经发送出去 ,%ld",tag);
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error
{
    NSLog(@"消息并没有发送出去,请重试 ,%ld,err:%@",tag,error);
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error
{
    assert(@"UDP 无法连接");
}

- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError  * _Nullable)error
{
    assert(@"UDP Socket 关闭");
}

#pragma mark - TCPDelegate
- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
    NSLog(@"didAcceptNewSocket");
}

//机器人连接成功
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    if (asyncSocket == nil) {
        asyncSocket = sock;
    }
    _connectAuto = NO;
    _connectState = YES;
	startAutoConnect = NO;
    if (socketTool.connectStateBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            socketTool.connectStateBlock(_connectState);
        });
    }
    //把连到的机器人名字存进沙盒
    [UserDataHelper setUserData:connectName forKey:AUTO_RECONNECT_NAME];
    
    if (sendTimer.isValid) {
        [sendTimer setFireDate:[NSDate distantPast]];
    }
    //连接后开始发心跳包
    [[NSRunLoop mainRunLoop] addTimer:sendTimer forMode:NSRunLoopCommonModes];                  //加入到RunLoop中
    [asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:1];

}

//lxadd读到机器人发来的数据
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    NSDictionary *dict = [NSJSONSerialization
                          JSONObjectWithData:data
                          options:NSJSONReadingMutableContainers
                          error:nil];
//    NSLog(@"读取到的数据包信息--==%@",dict);
//    NSLog(@"currentPacktHead==%@",currentPacketHead);
//    NSLog(@"当前线程=--%@",[NSThread currentThread]);
    //先读取到当前数据包头部信息
    if (!currentPacketHead) {//当第一次来的是心跳包的时候会出问题
        currentPacketHead = [NSJSONSerialization
                             JSONObjectWithData:data
                             options:NSJSONReadingMutableContainers
                             error:nil];
        //NSLog(@"currentPacketHead---%@",currentPacketHead);
        NSUInteger packetLength = [currentPacketHead[@"size"] integerValue];
        
        if (packetLength == 0) {
            currentPacketHead = nil;
            return;
        }
        
        //读到数据包的大小
//        NSLog(@"packetLength:---%ld",packetLength);
        [sock readDataToLength:packetLength withTimeout:-1 tag:1];
        
        return;
    }

    if (!currentPacketHead) {
        NSLog(@"error:当前数据包的头为空");
        //断开连接
        return;
    }
    
    //正式的包处理
    NSUInteger packetLength = [currentPacketHead[@"size"] integerValue];
//    NSLog(@"packetLength--%zd",packetLength);
   //NSLog(@"data.length--%zd",data.length);
//     NSLog(@"正式包数据--==%@",dict);
    //说明数据有问题 看正式包的长度是否与包头告诉你的长度一致
    if (packetLength <= 0 || data.length != packetLength) {
        
        NSLog(@"error:当前数据包数据大小不正确");
        currentPacketHead = nil;
        return;
    }
    
 
    

    NSString *aString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

    id jsonMap = [JsonHelper fromJsonString:aString];
    if (([jsonMap[@"commondType"] integerValue] != 20)) {//表示回的不是心跳包,是有效数据
        dispatch_async(dispatch_get_main_queue(), ^{
            
            [UserDataHelper setUserData:jsonMap[@"robot_version"] forKey:ROBOT_VERSION];
            if (msgReveiceBlock) {
                NSLog(@"机器人传回的jsonMap==%@",jsonMap);
                msgReveiceBlock(jsonMap);
                
//                if ([jsonMap[@"commondType"] integerValue] == [PLAY_RESOURCE_DATA integerValue]){
//               NSNotification *notification =[NSNotification notificationWithName:@"fromRobot" object:nil userInfo:jsonMap];
//                [[NSNotificationCenter defaultCenter] postNotification:notification];
//                }
            }
        });
        
    }else{//表示回的是心跳包
        [UserDataHelper setUserData:jsonMap[@"hyID"] forKey:Robot_EM_ID];
        
        [noSendTimer invalidate];
        noSendTimer = nil;
        noSendTimer = [NSTimer scheduledTimerWithTimeInterval:ACCEPT_HEART_TIME_SPACE target:self selector:@selector(robotConnectTimeOut) userInfo:nil repeats:NO];
        
        static dispatch_once_t onceToken_id;
        dispatch_once(&onceToken_id, ^{
            if (_getIdBlock) {
                _getIdBlock(jsonMap);
            }
        });
    }
    
    currentPacketHead = nil;
//    NSLog(@"fromRobot msg,currentPacketHead,%@",jsonMap);
    [asyncSocket readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:1];

}

- (void)robotConnectTimeOut
{
   // NSLog(@"检测是否收到心跳包的定定时器,40秒没收到说明断开了");
    _connectState = NO;
    if (socketTool.connectStateBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            socketTool.connectStateBlock(_connectState);
        });
    }
    [noSendTimer invalidate];
    noSendTimer = nil;
}



- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(nullable NSError *)err
{//lxaddlx 机器人连接失败的超时时长是oc的socket框架定的
    NSLog(@"lxaddlx已经断开连接了 TCP%@",err);
    _connectState = NO;
    if (socketTool.connectStateBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            socketTool.connectStateBlock(_connectState);
        });
    }
    [sendTimer setFireDate:[NSDate distantFuture]];
    [socketTool autoConnectTcp:connectName];
}

- (void)socketDidCloseReadStream:(GCDAsyncSocket *)sock
{
    NSLog(@"%@",sock);
}

#pragma mark - Actions
- (void)sendHeartData
{
    //NSLog(@"来到发心跳包的定时器");
    
    if (!_connectState) {
        //关掉定时器
        [sendTimer invalidate];
    }
    NSDictionary *writeDic = @{@"commondType":@"19"};
   // NSLog(@"给机器人发心跳包%@",writeDic);
    NSMutableData *writeData = [NSMutableData dataWithData:[JsonHelper toJsonData:writeDic]];
    
    
    [writeData appendData:[GCDAsyncSocket CRLFData]];
    
    NSUInteger size = writeData.length;
    
    NSMutableDictionary *headDic = [NSMutableDictionary dictionary];
    
    [headDic setObject:[NSString stringWithFormat:@"%ld",(unsigned long)size] forKey:@"size"];
    
    NSData *jsonData = [JsonHelper toJsonData:headDic];
    
   // NSLog(@"写入Socket的心跳包%@",headDic);
    NSMutableData *mData = [NSMutableData dataWithData:jsonData];
    //分界
    [mData appendData:[GCDAsyncSocket CRLFData]];
    
    [mData appendData:writeData];
    
   // NSLog(@"sendHeartData:%@",mData);
    [asyncSocket writeData:mData withTimeout:-1 tag:1];
    
}

//给RB发送指令
- (void)sendMsgToRobot:(NSDictionary*)msgDic{
    
    NSMutableData *writeData = [NSMutableData dataWithData: [JsonHelper toJsonData:msgDic]];
    
    [writeData appendData:[GCDAsyncSocket CRLFData]];
    
    NSUInteger size = writeData.length;
    
    NSMutableDictionary *headDic = [NSMutableDictionary dictionary];
    [headDic setObject:[NSString stringWithFormat:@"%ld",size] forKey:@"size"];
    //NSLog(@"给机器人发的数据%@",headDic);
    NSData *jsonData = [JsonHelper toJsonData:headDic];
    
    
    NSMutableData *mData = [NSMutableData dataWithData:jsonData];
    //分界
    [mData appendData:[GCDAsyncSocket CRLFData]];
    
    [mData appendData:writeData];
    
    //NSLog(@"%@",writeData);
    [asyncSocket writeData:mData withTimeout:-1 tag:1];
    
}


@end

3.用微信的Airkiss给机器人配网

4.iOS13后无法获取wifi名的解决

ios13无法获取wifi名(SSID)(亲测有效)

相关博客

Socket简析与iOS实现

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页