美文网首页
iOS - socket通信(实现iwatch与安卓手机通信)

iOS - socket通信(实现iwatch与安卓手机通信)

作者: 温柔vs先生 | 来源:发表于2020-06-24 16:47 被阅读0次

最近公司有个需求,需要iWatch来控制手机端的一些应用,然后在网上找了很多,发现大部分都是iWatch与iOS端的通信,这里的通信都是苹果原生之间的交互,那么怎样 才能让iWatch与安卓手机之间进行通信呢?

首先我们能想到的通信机制有以下几种:

(1)用户需要开启蓝牙连接Watch后在APP中进行设置,调整手表与iPhone的交互关系,蓝牙距离9米,超过距离,断开连接

(2)iPhone 与AppleWatch处于同一Wi-Fi网络(或者连接到由iPhone建立的Wi-Fi热点),只要信号存在,双方距离多远都可以

(3)watch应用对象添加到项目后,包含Watch App 和 WatchKit Extension。Watch App 位于iWatch上,目前只允许包含storyboard 和 Resource文件;Watch Extension 位于用户的iPhone安装的对应App上,这里包括我们需要实现的代码逻辑和其他资源。这两部分通过Watch Kit进行连接通讯,用户点击Watch App后,与Watch匹配的iPhone会启动WatchKit extension,然后和Watch建立连接,产生通信

就是说要想实现与安卓手机通信只能通过蓝牙,或者局域网socket通信(第三种方法只针对iOS应用),然后当我想用iWatch与安卓手机进行蓝牙连接时,却怎么也不能在手机上找到对应的iWatch(可能苹果对安卓手机进行了屏蔽),所以只能采用第二种方法进行通信。

决定采用socket通信后,在网上找了发现有很多的相关文章,并且大部分都是采用GCDAsyncSocket这个第三方框架,可是当我植入我的iWatch工程中后,发现应用报错,因为该第三方库中有很多方法和用到的类(这些可以在iOS中适配),iWatch并不支持,所以并没有采用这个方法,后来发现苹果原生也可以实现socket的通信

#import "SocketManager.h"

@interface SocketManager ()<NSStreamDelegate>
{
    NSInputStream *inputStream;
    NSOutputStream *outputStream;
}
@end
@implementation SocketManager

+ (instancetype)sharedSocketManager {
    static SocketManager *socketManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        socketManager = [[SocketManager alloc] init];
    });
    return socketManager;
}

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{

    NSLog(@"eventCode:%lu",(unsigned long)eventCode);
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            
              NSLog(@"输入输出流打开完成");
            
            break;
          case NSStreamEventHasBytesAvailable:

              NSLog(@"有字节可读");
                 [self readData];
            
            break;
           case NSStreamEventHasSpaceAvailable:
            
              NSLog(@"可以发送字节");
            
                 break;
             case NSStreamEventErrorOccurred:
            
              NSLog(@" 连接出现错误");
                 break;
            case NSStreamEventEndEncountered:
            
              NSLog(@"连接结束");
            
                 // 关闭输入输出流
                 [inputStream close];
                 [outputStream close];
    
                 // 从主运行循环移除
                 [inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
                 [outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
                 break;
             default:
                 break;
     }

}

- (void)connect{
   
    NSString *host = IP地址;
    int port = 端口号;
    
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
    
     inputStream = (__bridge NSInputStream *)(readStream);
     outputStream = (__bridge NSOutputStream *)(writeStream);
    
    inputStream.delegate = self;
    outputStream.delegate = self;
    
    [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    [inputStream open];
    [outputStream open];
    
}

- (void)login{
    写入的命令,此处省略
    [outputStream write:data.bytes maxLength:data.length];
}


- (void)readData{
        uint8_t buf[1024];
    
        NSInteger len = [inputStream read:buf maxLength:sizeof(buf)];
        NSData *data = [NSData dataWithBytes:buf length:len];
        
        NSString *recStr =  [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        
        NSLog(@"%@",recStr);
//    [self cleanUpStream:inputStream];
}
- (void)cleanUpStream:(NSStream *)stream
{
    [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [stream close];
    
    stream = nil;
}
@end

还有一种方法使用socket编程最底层的connect等方法

#import "SocketManager1.h"
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <syslog.h>
#include <errno.h>
#include <stdlib.h>
@implementation SocketManager1

- (void)socketConnect:(NSString *)host Port:(int)port{

    const char *ip = IP地址;
    self.socketFD = socket(AF_INET, SOCK_STREAM, 0);
    NSData *dataLogin = [self login];
    NSInteger bytes = [dataLogin length];
    NSLog(@"socketFD:%d",self.socketFD);
    struct sockaddr_in nativeAdd;
    nativeAdd.sin_len = sizeof(struct sockaddr);
    nativeAdd.sin_family = AF_INET;
    nativeAdd.sin_port = htons(port);
    nativeAdd.sin_addr.s_addr = inet_addr(ip);

    self.status = connect(self.socketFD, (struct sockaddr *) &nativeAdd, sizeof(nativeAdd));
    if (-1 == self.status) {
        close(self.socketFD);
    }
    if (self.status <0) {
        NSLog(@"连接失败:%d",errno);
        
    }else{
    
        NSLog(@"连接成功:%d",self.status);
        self.status = write(self.socketFD, [dataLogin bytes], bytes);
        if (self.status <0) {
            NSLog(@"写入失败:%d",errno);
            
        }else{
            
            NSLog(@"登陆成功:%d",self.status);
            
            [self read];
        }

    }
  
}

- (void)read{
    [NSThread detachNewThreadSelector:@selector(thread:) toTarget:self withObject:@"thread1"];
}

- (void)thread:(NSString *)sender{
    
    NSThread *thread = [NSThread currentThread];
    NSLog(@"%@",thread);
  NSMutableData *data = [[NSMutableData alloc]init];
    BOOL waitingForData = YES;
    int i = 0;
    do {
        const char *buffer[10000];
        int length = sizeof(buffer);
        int result = read(self.socketFD, &buffer, length);
        NSLog(@"result:%d",result);
        if (result >0) {
            [data appendBytes:buffer length:result];
            NSLog(@"data:%@",data);
            
        }else{
            
            NSString *string = [[NSString alloc]initWithData:data encoding:NSASCIIStringEncoding];
            NSLog(@"string:%@",string);
            waitingForData = NO;
        }
        
        ++i;
        NSLog(@"i:%d",i);
       NSLog(@"waitingForData:%d",waitingForData);
    } while (waitingForData == YES);

}
- (NSData *)login{
  写入的命令,此处省略;
    return data;
}

自己封装

//
//  SocketManager.h
//  yang Extension
//
//  Created by wbb on 2020/6/24.
//  Copyright © 2020 于浦洋. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
typedef void(^ContentBlock)(NSString *msg);
@interface SocketManager : NSObject

@property(nonatomic, copy) void(^ContentBlock)(NSString *msg);

/// 单利
+ (instancetype)sharedSocketManager;


/// 初始化socket
/// @param host IP地址
/// @param port 端口号
- (void)connect:(NSString *)host port:(int)port;

/// 发送数据
/// @param msg 内容
- (void)sendMsg: (NSString *)msg;

/// 关闭输入输出流
- (void)closeSocket;
@end

NS_ASSUME_NONNULL_END

//
//  SocketManager.m
//  yang Extension
//
//  Created by wbb on 2020/6/24.
//  Copyright © 2020 于浦洋. All rights reserved.
//

#import "SocketManager.h"
#import "NSData+Extension.h"

@interface SocketManager ()<NSStreamDelegate>
{
    NSInputStream *inputStream;
    NSOutputStream *outputStream;
}
@property(nonatomic, strong) NSMutableData *bufData;// 当前包内容
@property (nonatomic, assign) int currentByteLength;//当前包内字节内容的长度
@property (nonatomic, assign) int byteLength;//获取包长度的4个字节(是否是4个字节)
@end
@implementation SocketManager

+ (instancetype)sharedSocketManager {
    static SocketManager *socketManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        socketManager = [[SocketManager alloc] init];
    });
    return socketManager;
}

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
    
    NSLog(@"eventCode:%lu",(unsigned long)eventCode);
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            
            NSLog(@"输入输出流打开完成");
            
            break;
        case NSStreamEventHasBytesAvailable:
            
            NSLog(@"有字节可读");
//            uint8_t buf0[1024];
//            NSInteger len0 = [inputStream read:buf0 maxLength:sizeof(buf0)];
//            NSData *data0 = [NSData dataWithBytes:buf0 length:len0];
            
            [self readData];
            
            break;
        case NSStreamEventHasSpaceAvailable:
            
            NSLog(@"可以发送字节");
//            [self readData];

            break;
        case NSStreamEventErrorOccurred:
            NSLog(@" 连接出现错误%@",aStream.streamError);
            [self closeSocket];
            break;
        case NSStreamEventEndEncountered:
            
            NSLog(@"连接结束");
            
            [self closeSocket];
            break;
        default:
            break;
    }
    
}
- (void)closeSocket {
    // 关闭输入输出流
    [inputStream close];
    [outputStream close];
    
    // 从主运行循环移除
    [inputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
/*
 NSString *host = @"";//IP地址;
 int port = @"";//端口号;
 */
- (void)connect:(NSString *)host port:(int)port{
    
    
    
    CFReadStreamRef readStream;
    CFWriteStreamRef writeStream;
    CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
    
    inputStream = (__bridge NSInputStream *)(readStream);
    outputStream = (__bridge NSOutputStream *)(writeStream);
    
    inputStream.delegate = self;
    outputStream.delegate = self;
    
    [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    
    [inputStream open];
    [outputStream open];
    
}

- (void)sendMsg: (NSString *)msg
{
    NSMutableData *sendData = [NSMutableData dataWithCapacity:0];
    
    /// 所传的内容
    NSData *data = [msg dataUsingEncoding:NSUTF8StringEncoding];
    
    /// 第一个字节传入type
    Byte byte1[1];
    byte1[0] = 0x04;
    
    /// 后面四个字节是内容的长度
    NSInteger dataLength = data.length;
//    int8_t ch[4];
//    for(int32_t i = 0;i<4;i++){
//        ch[i] = ((dataLength >> ((3 - i)*8)) & 0x0ff);
//    }
    /// 大小端转换
    Byte ch[4];
    OSWriteBigInt32(ch, 0, dataLength);
    
    [sendData appendBytes:byte1 length:1];
    [sendData appendBytes:&ch length:sizeof(ch)];
    [sendData appendData:data];
    
    [outputStream write:sendData.bytes maxLength:sendData.length];
}

- (void)readData {
    int dataLenInt = 0;
    if (self.currentByteLength==0) {

        if(self.byteLength==0) {
            self.bufData = nil;
            uint8_t buf1[1];
            NSInteger len1 = [inputStream read:buf1 maxLength:sizeof(buf1)];
            NSData *data1 = [NSData dataWithBytes:buf1 length:len1];
            [self.bufData appendData:data1];

            Byte byte[] = {0x03};
            if ([data1 isEqualToData:[NSData dataWithBytes:byte length:1]]) {
                uint8_t buf2[4];
                NSInteger len2 = [inputStream read:buf2 maxLength:sizeof(buf2)];
                self.byteLength = len2;
                NSData *data2 = [NSData dataWithBytes:buf2 length:len2];
                [self.bufData appendData:data2];
                if (len2<4) {
                    self.currentByteLength = 0;
                }else {
                    self.currentByteLength = CFSwapInt32BigToHost(*(int*)([data2 bytes]));
                    dataLenInt = self.currentByteLength;
                    self.byteLength = 0;
                }
            }
        }
        
        while (self.byteLength>0) {
            uint8_t buf2[4-self.byteLength];
            NSInteger len2 = [inputStream read:buf2 maxLength:sizeof(buf2)];
            NSData *data2 = [NSData dataWithBytes:buf2 length:len2];
            [self.bufData appendData:data2];
            if (len2<4-self.byteLength) {
                self.byteLength = 4-self.byteLength - len2;
            }else {
                NSData *langthData = [self.bufData subdataWithRange:NSMakeRange(1, 4)];
                self.currentByteLength = CFSwapInt32BigToHost(*(int*)([langthData bytes]));
                dataLenInt = self.currentByteLength;
                self.byteLength = 0;
            }
        }
    }

    while (self.currentByteLength>0) {
        uint8_t buf[self.currentByteLength];
        NSInteger len = [inputStream read:buf maxLength:sizeof(buf)];
        NSData *data = [NSData dataWithBytes:buf length:len];
        [self.bufData appendData:data];
        
        if (self.currentByteLength>len) {
            self.currentByteLength = self.currentByteLength - len;
        }else {
            self.currentByteLength = 0;
        }
        
    }
    
    NSData * lastsendData = [self.bufData subdataWithRange:NSMakeRange(5, dataLenInt)];
    NSString * contentStr = [[NSString alloc]initWithData:lastsendData encoding:NSUTF8StringEncoding];
    
    NSLog(@"contentStr == %@",contentStr);
    if (contentStr.length>0) {
        if (self.ContentBlock) {
            self.ContentBlock(contentStr);
        }
    }
    
}
- (void)cleanUpStream:(NSStream *)stream
{
    [stream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [stream close];
    
    stream = nil;
}
- (NSMutableData *)bufData {
    if (!_bufData) {
        _bufData = [[NSMutableData alloc] initWithCapacity:0];
    }
    return _bufData;
}
@end

终端来模拟服务器

我们来利用终端来模拟服务器,向我们的app发送消息,上面我们已经说过,怎么使用终端来建立一个服务器端口,建立连接。好了,这个时候,把我们的App跑起来吧,看看有什么神奇的效果
在终端输入:

nc -l 6969 //这是建立服务器端口,再把APP跑起来,这时应该就会发现控制台打印“连接成功”。这时,就可以任意发送消息了
123456 //这是发送的消息
1213131SAS //这是发送的消息

遇到的问题:

int转byte:

    /// 后面四个字节是内容的长度
    NSInteger dataLength = data.length;

/// 大小端转换,下面两种方式都可以

//    int8_t ch[4];
//    for(int32_t i = 0;i<4;i++){
//        ch[i] = ((dataLength >> ((3 - i)*8)) & 0x0ff);
//    }


    
    Byte ch[4];
    OSWriteBigInt32(ch, 0, dataLength);

byte转int:

/// 系统自带的方法,result就是最终的int
    int result = 0;
    [data2 getBytes:&result range:NSMakeRange(0, len2)];




+ (int)data2Int:(NSData *)data{
    if (data.length==0) return 0;
    Byte *byte = (Byte *)[data bytes];
    // 有大小端模式问题?
     return (byte[0] << 24) + (byte[1] << 16) + (byte[2] << 8) + (byte[3]);
}

大小端问题:

ios默认是小端,而网络传输规定是大端传输,所以一定要注意。

粘包问题:

数据传输会有粘包,分包问题

与iOS关联应用通信可以参考我这篇文章

参考文献:

<iOS开发>之CocoaAsyncSocket使用
iOS Socket通信
ios socket即时通讯
iOS 用原生代码写一个简单的socket连接
iOS 关于socket通讯中字节序

相关文章

网友评论

      本文标题:iOS - socket通信(实现iwatch与安卓手机通信)

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