数据交互协议
主要是用于数据封包,拆包的数据协议。
协议规则:dataType(sizeof(uint8_t))->fileSizeLen(sizeof(NSUInteger))->fileNameLen(sizeof(uint8_t),当是文本和图片以及声音的时候为0,即仅仅是传文件的时候有效)->fileName(自身决定,当是文本和图片以及声音的时候为0,即仅仅是传文件的时候有效)->fileData(自身决定)
- 数据流按这样的顺序。
packWithDataInfo 用于把我们的数据封装成NSData,然后在转化成流发送出去。
unPackWithData 就用拆封刚才的数据,转成我们需要的可识别的对象。
Demo:
一、 封包
DataInfo *dataInfo = [[DataInfo alloc] init];
- 发送文件类
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"suff" ofType:@"zip"];
dataInfo.dataType = FileDataType;
dataInfo.fileName = @"suff.zip";
dataInfo.fileData = [NSData dataWithContentsOfFile:filePath];
- 发送文本类
dataInfo.dataType = TextDataType;
dataInfo.text = textField.text;
- 发送图片和声音是一样的
dataInfo.dataType = VoiceDataType OR PictureDataType;
dataInfo.fileData = data;
[self.socket sendData:[PeerFileManager packWithDataInfo:dataInfo]];
二、拆包
NSInteger temp = 0;
DataInfo *dataInfo = [PeerFileManager unPackWithData:data surplusValue:&temp];
//ps:如果temp是>=0说明数据包是个完整的,否则说明数据不完整
switch (dataInfo.dataType) {
case TextDataType:
return dataInfo.text;
case VoiceDataType:
[dataInfo.fileData writeToFile:[DocumentPath stringByAppendingPathComponent:@"voice.wav"] atomically:YES];
break;
case PictureDataType:
[dataInfo.fileData writeToFile:[DocumentPath stringByAppendingPathComponent:@"picture.png"] atomically:YES];
break;
case FileDataType:
[dataInfo.fileData writeToFile:[DocumentPath stringByAppendingPathComponent:dataInfo.fileName] atomically:YES];
break;
default:
break;
}
- 数据类型
typedef NS_ENUM(uint8_t, DataType) {
TextDataType,
PictureDataType,
VoiceDataType,
FileDataType
};
自定义对象
//定义一个中间消息体
@interface DataInfo : NSObject
@property (nonatomic, assign) DataType dataType;
@property (nonatomic, assign) uint8_t fileSize;
@property (nonatomic, copy) NSString *fileName;
@property (nonatomic, copy) NSData *fileData;
@property (nonatomic, copy) NSString *text;
@end
//文件处理类
@interface PeerFileManager : NSObject
+ (NSData *)packWithDataInfo:(DataInfo *)theDataInfo;
+ (DataInfo *)unPackWithData:(NSData *)theData surplusValue:(NSInteger *)theValue;
@end
实现类
#import "PeerFileManager.h"
@implementation DataInfo
@synthesize dataType,fileSize,fileName,fileData,text;
@end
// 定义些常量
static const int kDataTypeSize = sizeof(uint8_t);
static const int kFileNameLengthSize = sizeof(uint8_t);
static const int kFileSizeLengthSize = sizeof(NSUInteger);
//dataType->fileSizeLen->fileNameLen->fileName->fileData;
@implementation PeerFileManager
//封包操作
+ (NSData *)packWithDataInfo:(DataInfo *)theDataInfo {
NSMutableData *mergeData = [NSMutableData data];
uint8_t dataType = theDataInfo.dataType;
NSUInteger fileSize;
[mergeData appendBytes:&dataType length:kDataTypeSize];
if(theDataInfo.dataType == TextDataType) {
fileSize = [[theDataInfo.text dataUsingEncoding:NSUTF8StringEncoding] length];
[mergeData appendBytes:&fileSize length:kFileSizeLengthSize];
[mergeData appendData:[theDataInfo.text dataUsingEncoding:NSUTF8StringEncoding]];
}
else {
fileSize = theDataInfo.fileData.length;
[mergeData appendBytes:&fileSize length:kFileSizeLengthSize];
if(theDataInfo.dataType == FileDataType) {
NSString *fileName = theDataInfo.fileName;
uint8_t fileNameLength = fileName.length;
[mergeData appendBytes:&fileNameLength length:kFileNameLengthSize];
[mergeData appendData:[fileName dataUsingEncoding:NSUTF8StringEncoding]];
}
[mergeData appendData:theDataInfo.fileData];
}
return mergeData;
}
//拆包操作
+ (DataInfo *)unPackWithData:(NSData *)theData surplusValue:(NSInteger *)surplusValue {
//dataType->fileSizeLen->fileNameLen->fileName->fileData;
DataInfo *dataInfo = [[DataInfo alloc] init];
NSInteger tempSurplusValue = 0;
tempSurplusValue = theData.length - kDataTypeSize;
*surplusValue = tempSurplusValue;
// 头里数据类型长度和文件长度的数据是否存在
if(tempSurplusValue < 0) {
return nil;
}
int dataType;
long fileSizeLen;
// 得到数据类型
[theData getBytes:&dataType range:NSMakeRange(0, sizeof(dataType))];
// 主要是处理不同CPU下数据流大小端的问题。
BOOL isBigEndian = YES;
if(dataType > FileDataType) {
isBigEndian = NO;
}
dataType = isBigEndian ? dataType : CFSwapInt32BigToHost(dataType);
dataInfo.dataType = dataType;
if(dataInfo.dataType == BreathDataType) {
return dataInfo;
}
tempSurplusValue -= kFileSizeLengthSize;
*surplusValue = tempSurplusValue;
if(tempSurplusValue < 0) {
NSLog(@"数据包不完整,发生黏包");
return nil;
}
// 得到可用数据长度
[theData getBytes:&fileSizeLen range:NSMakeRange(kDataTypeSize, kFileSizeLengthSize)];
fileSizeLen = isBigEndian ? fileSizeLen : CFSwapInt64BigToHost(fileSizeLen);
tempSurplusValue -= fileSizeLen;
*surplusValue = tempSurplusValue;
if(tempSurplusValue < 0) {
NSLog(@"实体数据不足,发生黏包");
return nil;
}
if(dataType == TextDataType) {
dataInfo.text = [[NSString alloc] initWithData:[theData subdataWithRange:NSMakeRange(kDataTypeSize + kFileSizeLengthSize, fileSizeLen)] encoding:NSUTF8StringEncoding];
return dataInfo;
}
NSUInteger headerSize = kDataTypeSize + kFileSizeLengthSize;
NSString *fileName;
int fileNameLen = 0;
int fileNameSizeLen = 0;
if(dataType == FileDataType) {
fileNameSizeLen = kFileNameLengthSize;
tempSurplusValue -= kFileNameLengthSize;
*surplusValue = tempSurplusValue;
if(tempSurplusValue < 0) {
NSLog(@"数据包里文件长度不够,发生黏包");
return nil;
}
// 得到文件名长度
[theData getBytes:&fileNameLen range:NSMakeRange(kDataTypeSize + kFileSizeLengthSize, kFileNameLengthSize)];
fileNameLen = isBigEndian ? fileNameLen : CFSwapInt32BigToHost(fileNameLen);
tempSurplusValue -= fileNameLen;
*surplusValue = tempSurplusValue;
if(tempSurplusValue < 0) {
NSLog(@"数据包里文件名的数据不完整");
return nil;
}
if(fileNameLen > 0) {
fileName = [[NSString alloc] initWithData:[theData subdataWithRange:NSMakeRange(headerSize + kFileNameLengthSize, fileNameLen)] encoding:NSUTF8StringEncoding];
}
}
if(tempSurplusValue < 0 ) {
NSLog(@"文件内容不存在");
return nil;
}
NSData *fileData = [theData subdataWithRange:NSMakeRange(headerSize + fileNameSizeLen + fileNameLen , fileSizeLen)];
dataInfo.fileName = fileName;
dataInfo.fileData = fileData;
dataInfo.fileSize = fileSizeLen;
return dataInfo;
}
@end