这两天做了iOS蓝牙的数据交互,起初公司的方案是手机作为蓝牙从设备让公司平台扫描手机上面代码获取的蓝牙地址作为唯一标示去定向连接的。不过由于苹果手机关于用户个人隐私的安全性在iOS7之后就已经失效了,所以这个方案被驳回。
后来公司提议手机作为主设备不通过扫描直接连接给定的蓝牙,因为本人刚刚学习蓝牙就我了解iOS这边蓝牙必须通过扫描哪怕给定扫描设备的名称也需要扫描才能建立蓝牙连接。最后的方案公司还在商议中,不过蓝牙与外设的交互要做起来了。期间遇到很多很多问题,还好公司有安卓的大神耐心给我讲解。例如蓝牙的通信协议:
说实话刚开始看到这个协议我内心是崩溃的,因为都不知道怎么下手去做就比如将一个字符串转成NSData类型我一开始就直接使用的
[text dataUsingEncoding:NSUTF8StringEncoding]
但是如论我怎么转换使用什么编码格式都是错误的,跟预期都不一样。就这一点我自己就纠结了一天时间各种实验各种尝试。
后来我邀来安卓大神给我讲一下这个协议,她专门抽来十来分钟给我详细讲了下,真是听她十分钟胜我一下午啊。首先第一个字符串是一个16进制字符串,将一个16进制字符串转成NSData并不是我之前这样的方式,而是
#pragma mark - 将字符表示的16进制数转化为二进制数据
+ (NSMutableData *)dataWithHexString:(NSString *)hexString
{
if (!hexString || [hexString length] == 0) {
return nil;
}
NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:8];
NSRange range;
if ([hexString length] % 2 == 0) {
range = NSMakeRange(0, 2);
} else {
range = NSMakeRange(0, 1);
}
for (NSInteger i = range.location; i < [hexString length]; i += 2) {
unsigned int anInt;
NSString *hexCharStr = [hexString substringWithRange:range];
NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr];
[scanner scanHexInt:&anInt];
NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1];
[hexData appendData:entity];
range.location += range.length;
range.length = 2;
}
// NSLog(@"hexdata: %@", hexData);
return hexData;
}
当然转换方式不止一种例如下面这中也是可以的当然我用的是第一种:
#pragma mark - 将字符表示的16进制数转化为二进制数据
+ (NSMutableData *)dataWithHexString:(NSString *)hexString
{
// hexString的长度应为偶数
if ([hexString length] % 2 != 0)
return nil;
NSUInteger len = [hexString length];
NSMutableData *retData = [NSMutableData data];
const char *ch = [[hexString dataUsingEncoding:NSASCIIStringEncoding] bytes];
for (int i=0 ; i2) {
int height=0;
if (ch[i]>='0' && ch[i]<='9')
height = ch[i] - '0';
else if (ch[i]>='A' && ch[i]<='F')
height = ch[i] - 'A' + 10;
else if (ch[i]>='a' && ch[i]<='f')
height = ch[i] - 'a' + 10;
else
// 错误数据
return nil;
int low=0;
if (ch[i+1]>='0' && ch[i+1]<='9')
low = ch[i+1] - '0';
else if (ch[i+1]>='A' && ch[i+1]<='F')
low = ch[i+1] - 'A' + 10;
else if (ch[i+1]>='a' && ch[i+1]<='f')
low = ch[i+1] - 'a' + 10;
else
// 错误数据
return nil;
int byteValue = height*16 + low;
[retData appendBytes:&byteValue length:1];
}
return retData;
}
其次,也是最让我头疼的地方-->NSData的CRC验证首先我看我的crc验证源文件:
unsigned short get_crc16(unsigned char *buf, int len)
首先我需要给它传进去一个unsigned char *类型的数据然后我就各种找NSData转unsigned char*的方法,却发现怎么做都不对。然我我就开始找NSData的CRC验证,中间发现其实unsigned char *可以是Byte数组即:
#pragma mark - 将16进制字符串转成NSData+CRC数据
+ (NSData *)calCRCWithData:(NSData *)visibleData
{
//将16进制字符串转成NSData数据
NSMutableData *visibleDataM = [visibleData mutableCopy];
// Byte byte[5] = {0x00,0xc0,0x00,0x00,0x32};
//NSData做crc验证得到short返回值
Byte *byte = (Byte *)[visibleDataM bytes];
unsigned short crcShort = get_crc16(byte, (int)visibleDataM.length);
//将short返回值转成byte类型添加到可变数组末尾
Byte bytes[2];
bytes[0] = (Byte) (crcShort >> 8);
bytes[1] = (Byte) (crcShort);
[visibleDataM appendBytes:bytes length:sizeof(bytes)];
return visibleDataM;
}
返回的unsigned short再转换成NSData发现正常了,真是内心都松了一口气。
接下来就是NSData转义,转义的时候是一个字节一个字节对比的,例如NSData数据<00238900a0>你发现里面出现了0a却没有转义是不是有错但其实上面的NSData可以转换成这样的数据0x00,0x23,0x89,0x00,0xa0.里面却没有0a所以转义也是字节对比的例如我的转义方法:
#pragma mark - 对Data+CRC数据进行转义
+ (NSData *)EscapedData:(NSData *)converData
{
/**
对Data+CRC数据进行转义
转移规则:0xc0->0xc0 0x00; 0x45->0xc0 0x01; 0x32->0xc0 0x02; 0x0b-> 0xc0 0x03; 0x0a->0xc0 0x04
*/
Byte *myBytes = (Byte *)[converData bytes];
NSMutableData *finalDataM = [NSMutableData data];
for (int i = 0; i < converData.length; i++) {
if (myBytes[i] == 0xc0) {
Byte temByte[2];
temByte[0] = 0xc0;
temByte[1] = 0x00;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}
else if (myBytes[i] == 0x45) {
Byte temByte[2];
temByte[0] = 0xc0;
temByte[1] = 0x01;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}
else if (myBytes[i] == 0x32) {
Byte temByte[2];
temByte[0] = 0xc0;
temByte[1] = 0x02;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}
else if (myBytes[i] == 0x0b) {
Byte temByte[2];
temByte[0] = 0xc0;
temByte[1] = 0x03;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}
else if (myBytes[i] == 0x0a) {
Byte temByte[2];
temByte[0] = 0xc0;
temByte[1] = 0x04;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}else{
Byte temByte[1];
temByte[0] = myBytes[i];
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}
}
return finalDataM;
}
同样反转义:
#pragma mark - 对Data+CRC数据进行反转义
+ (NSData *)UnescapesData:(NSData *)converData
{
/**
对Data+CRC数据进行转义
转移规则:0xc0->0xc0 0x00; 0x45->0xc0 0x01; 0x32->0xc0 0x02; 0x0b-> 0xc0 0x03; 0x0a->0xc0 0x04
*/
Byte *myBytes = (Byte *)[converData bytes];
NSMutableData *finalDataM = [NSMutableData data];
for (int i = 0; i < converData.length; i++) {
if (myBytes[i] == 0xc0 && myBytes[i+1] == 00) {
Byte temByte[1];
temByte[0] = 0xc0;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
i++;
}
else if (myBytes[i] == 0xc0 && myBytes[i+1] == 01) {
Byte temByte[1];
temByte[0] = 0x45;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
i++;
}
else if (myBytes[i] == 0xc0 && myBytes[i+1] == 02) {
Byte temByte[1];
temByte[0] = 0x32;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
i++;
}
else if (myBytes[i] == 0xc0 && myBytes[i+1] == 03) {
Byte temByte[1];
temByte[0] = 0x0b;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
i++;
}
else if (myBytes[i] == 0xc0 && myBytes[i+1] == 04) {
Byte temByte[2];
temByte[1] = 0x0a;
[finalDataM appendBytes:temByte length:sizeof(temByte)];
i++;
}else{
Byte temByte[1];
temByte[0] = myBytes[i];
[finalDataM appendBytes:temByte length:sizeof(temByte)];
}
}
return finalDataM;
}
我总的目录结构是这样的:
暴露出去的只有加解密两个方法。
另外突然看到这个.c文件的导入工程的使用就顺带提一下吧。因为Xcode是支持C语言的,所以不需要额外做什么其他东西只需要把你的.c文件导入Xcode然后Ctrl+N新建.h文件:
把你想要暴露的.c文件的方法名称写到.h文件中
在你需要的地方调用.h文件即可,如我的目录总纲所示。如有不足敬请指出��