背景
因项目需要,需要在规定的时间内根据特定的信号,将大量的图片和文件下载/上传到iOS本地/服务器进行实时渲染,因之前也尝试了socket通讯,但还是会引起通讯阻塞(因还有大量接口需要采用socket通讯)。故采用FTP协议传输。
FTP
FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。FTP允许用户以文件操作的方式(如文件的增、删、改、查、传送等)与另一主机相互通信
联调工具
- 推荐使用工具: FileZilla
- 也可以用终端ssh指令,将特定的文件上传/下载到FTP服务器,做完辅助测试用
上传
scp "需要上传的文件" root@182.168.1.88:/home/pi/App
下载,先登录到远程设备(我这边是Linux系统服务器)
scp "需要下载的文件" root@182.168.1.3:/Users/peng***/Documents/1-21
FTP传输
因我这边项目需要只需要上传和下载文件(登录每次请求的时候携带用户名和密码),故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];
- 输入输出流回调处理
- (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(@"流出现异常");
[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;
}
}
- 这个demo里面异常处理以及流处理中都用到了RunLoop处理RunLoop 说明
网友评论