背景
因項(xiàng)目需要间景,需要在規(guī)定的時(shí)間內(nèi)根據(jù)特定的信號(hào),將大量的圖片和文件下載/上傳到iOS本地/服務(wù)器進(jìn)行實(shí)時(shí)渲染寨典,因之前也嘗試了socket通訊调鬓,但還是會(huì)引起通訊阻塞(因還有大量接口需要采用socket通訊)。故采用FTP協(xié)議傳輸补憾。
FTP
FTP(File Transfer Protocol,文件傳輸協(xié)議) 是 TCP/IP 協(xié)議組中的協(xié)議之一漫萄。FTP協(xié)議包括兩個(gè)組成部分,其一為FTP服務(wù)器,其二為FTP客戶端。FTP允許用戶以文件操作的方式(如文件的增盈匾、刪腾务、改、查削饵、傳送等)與另一主機(jī)相互通信
聯(lián)調(diào)工具
- 推薦使用工具: FileZilla
- 也可以用終端ssh指令岩瘦,將特定的文件上傳/下載到FTP服務(wù)器,做完輔助測(cè)試用
上傳
scp "需要上傳的文件" root@182.168.1.88:/home/pi/App
下載窿撬,先登錄到遠(yuǎn)程設(shè)備(我這邊是Linux系統(tǒng)服務(wù)器)
scp "需要下載的文件" root@182.168.1.3:/Users/peng***/Documents/1-21
FTP傳輸
因我這邊項(xiàng)目需要只需要上傳和下載文件(登錄每次請(qǐng)求的時(shí)候攜帶用戶名和密碼)启昧,故FTP的其它功能沒有封裝(采用CFWiterRef),大同小異劈伴。
- 上傳
CFWriteStreamRef writeStreamRef = CFWriteStreamCreateWithFTPURL(NULL, ( __bridge CFURLRef) url);
//url :上傳的地址
CFWriteStreamSetProperty(writeStreamRef,
kCFStreamPropertyFTPAttemptPersistentConnection,
kCFBooleanFalse);
CFWriteStreamSetProperty(writeStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStreamRef, kCFStreamPropertyFTPUsePassiveMode, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStreamRef, kCFStreamPropertyFTPFetchResourceInfo, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStreamRef, kCFStreamPropertyFTPUserName, (__bridge CFStringRef) self.ftpUsername);
CFWriteStreamSetProperty(writeStreamRef, kCFStreamPropertyFTPPassword, (__bridge CFStringRef) self.ftpPassword);
self.commandStream = ( __bridge_transfer NSOutputStream *) writeStreamRef;
//得到輸出流
self.commandStream.delegate = self;
[self performSelector:@selector(scheduleInCurrentThread:)
onThread:[[self class] networkThread]
withObject:self.commandStream
waitUntilDone:YES];
[self.commandStream open]
- 下載
CFReadStreamRef readStreamRef = CFReadStreamCreateWithFTPURL(NULL, ( __bridge CFURLRef) url);
CFReadStreamSetProperty(readStreamRef,
kCFStreamPropertyFTPAttemptPersistentConnection,
kCFBooleanFalse);
CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyFTPUsePassiveMode, kCFBooleanTrue);
CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyFTPFetchResourceInfo, kCFBooleanTrue);
CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyFTPUserName, (__bridge CFStringRef) self.ftpUsername);
CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyFTPPassword, (__bridge CFStringRef) self.ftpPassword);
//
CFReadStreamSetProperty(readStreamRef, kCFStreamPropertyFTPProxy, kCFBooleanTrue);
//
self.dataStream = ( __bridge_transfer NSInputStream *) readStreamRef;
self.dataStream.delegate = self;
if (self.dataStream == nil) {
[self.delegate ftpError:self withErrorCode:FTPClientCantReadStream];
}
[self performSelector:@selector(scheduleInCurrentThread:)
onThread:[[self class] networkThread]
withObject:self.dataStream
waitUntilDone:YES];
// [self.dataStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[self.dataStream open];
- 輸入輸出流回調(diào)處理
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
// An NSStream delegate callback that's called when events happen on our
// network stream.
{
#pragma unused(aStream)
switch (eventCode) {
case NSStreamEventOpenCompleted: {
self.didOpenStream = true;
if ([self.currentOperation isEqualToString:@"GET"]){
self.maximumSize = [[aStream propertyForKey:(id)kCFStreamPropertyFTPResourceSize] integerValue];
//NSLog(@"%luself.maximumSize:%d: %f",self.dType,self.currentIndex,self.maximumSize );
}
self.uploadBytesTotal = 0;
self.bytesRemaining = 0;
self.bytesTotal = 0;
} break;
case NSStreamEventHasBytesAvailable: {
if ([self.currentOperation isEqualToString:@"GET"]){
NSData *data = [self read];
if (data) {
[self.receivedData appendData:data];
}else {
NSLog(@"流出現(xiàn)異常");
[self.delegate ftpError:self withErrorCode:FTPClientCantReadStream];
[self closeAll];
}
}
} break;
case NSStreamEventHasSpaceAvailable: {
if (self.bytesRemaining == 0) {
self.sentData = [NSData dataWithContentsOfFile:self.uploadFilePath];
self.bytesRemaining = [_sentData length];
self.bytesIndex = 0;
if (self.sentData == nil) {
[self.delegate ftpUploadFinishedWithSuccess:self];
}
}
NSUInteger nextPackageLength = MIN(kGRDefaultBufferSize, self.bytesRemaining);
NSRange range = NSMakeRange(self.bytesIndex, nextPackageLength);
NSData *packetToSend = [self.sentData subdataWithRange: range];
[self uploadWrite:packetToSend];
self.bytesIndex += self.bytesThisIteration;
self.bytesRemaining -= self.bytesThisIteration;
} break;
case NSStreamEventErrorOccurred: {
[self.delegate ftpError:self withErrorCode:FTPClientCantOpenStream];
[self closeAll];
} break;
case NSStreamEventEndEncountered: {
// ignore
if ([self.currentOperation isEqualToString:@"GET"]){
//NSLog(@"%luself.maximumSizeEnd:%d:%.2f",(unsigned long)self.dType,self.currentIndex,self.maximumSize );
//NSLog(@"%luself.maximumSizeData:%d:%lu",(unsigned long)self.dType,self.currentIndex,self.receivedData.length );
if (self.maximumSize != self.receivedData.length) {
//下載異常
NSLog(@"下載異常:%d",self.currentIndex);
[self.delegate ftpError:self withErrorCode:FTPClientCantOpenStream];
[self closeAll];
return;
}
if ([self downOk]) {
if ([self.delegate respondsToSelector:@selector(ftpDownloadFinishedWithSuccess:withIndex:withPath:withData:)]) {
[self.delegate ftpDownloadFinishedWithSuccess:self withIndex:self.currentIndex withPath:self.localname withData:self.receivedData];
}
}
[self closeAll];
}
if ([self.currentOperation isEqualToString:@"upload"]) {
[self.delegate ftpError:self withErrorCode:FTPClientCantOpenStream];
[self closeAll];
}
} break;
default: {
assert(NO);
} break;
}
}
- 這個(gè)demo里面異常處理以及流處理中都用到了RunLoop處理RunLoop 說明