最近公司有个需求,需要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通讯中字节序
网友评论