使用socket(AsyncSocket、GCDAsyncSocket)进行文件的断点上传操作

来源:互联网 发布:艳子钩把淘宝店 编辑:程序博客网 时间:2024/06/09 17:55

https://github.com/potato512/SYDemo_SYSocket

通常使用网络进行文件的断点续传时,都是文件的断点下载,很少有文件的断点上传的例子。现在通过socket方式做了一个文件的断点上传。socket使用了AsyncSocket,或是GCDAsyncSocket。

AsyncSocket:http://code.google.com/p/cocoaasyncsocket/

GCDAsyncSocket:https://github.com/robbiehanson/CocoaAsyncSocket

在使用时,先将socket进行了封装。然后再使用封装类。

封装类文件

// .h文件////  SocketManager.h//  zhangshaoyu////  Created by zhangshaoyu on 15/9/22.//  Copyright (c) 2015年 zhangshaoyu. All rights reserved.//  socket断点上传文件#import <Foundation/Foundation.h>/// 掉线类型(服务端掉线,或用户主动退出)typedef NS_ENUM(NSInteger, SocketDisconnectType){    /// 服务器掉线,默认为0    SocketOfflineByServer = 0,        /// 用户主动cut    SocketOfflineByUser = 1};@interface SocketManager : NSObject/// 掉线类型@property (nonatomic, assign) SocketDisconnectType disconnecType;/// 文件路径@property (nonatomic, copy) NSString *filePath;/// 单例+ (SocketManager *)sharedSocket;/// socket连接- (void)socketConnectWithHost:(NSString *)host port:(UInt16)port;/// socket连接断开- (void)socketDisconnect;/// GCDSocket连接- (void)GCDSocketConnectWithHost:(NSString *)host port:(UInt16)port;;/// GCDSocket连接断开- (void)GCDSocketDisconnect;@end
// .m文件////  SocketManager.m//  zhangshaoyu////  Created by zhangshaoyu on 15/9/22.//  Copyright (c) 2015年 zhangshaoyu. All rights reserved.//#import "SocketManager.h"#import "AsyncSocket.h"#import "GCDAsyncSocket.h"unsigned long long OffSet = 1024 * 200;static NSInteger const timeWithout = -1;static NSInteger const tagWriteData = 1;static NSInteger const tagReadData = 2;static NSString *const keyOffset = @"keyOffset";static NSString *const keySouceId = @"keySouceId";@interface SocketManager () <AsyncSocketDelegate, GCDAsyncSocketDelegate>@property (nonatomic, strong) GCDAsyncSocket *GCDSocket;@property (nonatomic, strong) AsyncSocket *socket;  // socket@property (nonatomic, copy) NSString *socketHost;   // socket的Host@property (nonatomic, assign) UInt16 socketPort;    // socket的prot@property (nonatomic, strong) NSTimer *socketTimer; // 计时器@property (nonatomic, assign) BOOL isUploadHead;                // 文件上传头文件协议@property (nonatomic, assign) unsigned long long filelength;    // 文件上传大小@property (nonatomic, strong) NSString *fileName;               // 文件上传名称@property (nonatomic, strong) NSString *fileSouceId;            // 文件上传id@property (nonatomic, strong) NSFileHandle *fileHandle;         // 文件操作@property (nonatomic, assign) unsigned long long currentOffset; // 当前累计读取文件大小@end@implementation SocketManager/// 单例+ (SocketManager *)sharedSocket{    static SocketManager *sharedInstace = nil;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        sharedInstace = [[self alloc] init];    });        return sharedInstace;}#pragma mark - setter- (void)setDisconnecType:(SocketDisconnectType)disconnecType{    _disconnecType = disconnecType;    self.socket.userData = _disconnecType;}#pragma mark - asyncSocket连接掉线操作// socket连接- (void)socketConnectWithHost:(NSString *)host port:(UInt16)port{    if (!host || 0 >= host.length)    {        return;    }        self.socketHost = host;    self.socketPort = port;        // 在连接前先进行手动断开    self.disconnecType = SocketOfflineByUser;    [self socketDisconnect];    // 确保断开后再连,如果对一个正处于连接状态的socket进行连接,会出现崩溃    self.disconnecType = SocketOfflineByServer;        [self socketConnect];}// 连接- (void)socketConnect{    /*    dispatch_async(dispatch_get_global_queue(0, 0), ^{            // 处理耗时操作的代码块...        self.isUploadHead = YES;        //        NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:keyOffset];//        self.currentOffset = number.longLongValue;        self.currentOffset = 0;                self.socket = [[AsyncSocket alloc] initWithDelegate:self];        NSError *error = nil;        [self.socket connectToHost:self.socketHost onPort:self.socketPort withTimeout:3 error:&error];        //        //通知主线程刷新//        dispatch_async(dispatch_get_main_queue(), ^{//            //回调或者说是通知主线程刷新, //        });    });    */        self.isUploadHead = YES;        NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:keyOffset];    self.currentOffset = number.longLongValue;//    self.currentOffset = 0;        self.socket = [[AsyncSocket alloc] initWithDelegate:self];    NSError *error = nil;    [self.socket connectToHost:self.socketHost onPort:self.socketPort withTimeout:timeWithout error:&error];}// socket断开连接- (void)socketDisconnect{    [self stopTimer:NO];}#pragma mark - AsyncSocketDelegate// 连接成功回调- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{    NSLog(@"socket连接成功");    //    // 获取服务端返回数据//    [self.socket readDataWithTimeout:timeWithout tag:tagReadData];        // 每隔30s像服务器发送心跳包    // 在longConnectToSocket方法中进行长连接需要向服务器发送的讯息    [self startTimer:NO];}// 重连- (void)onSocketDidDisconnect:(AsyncSocket *)sock{    NSLog(@"sorry the connect is failure %ld",sock.userData);    if (SocketOfflineByServer == self.socket.userData)    {        // 服务器掉线,重连        [self socketConnect];    }    else if (SocketOfflineByUser == self.socket.userData)    {        // 如果由用户断开,不进行重连        return;    }}- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{    NSLog(@"sock read");        // 对得到的data值进行解析与转换即可    [self fileInfoWithData:data];    //    if (tagReadData == tag)//    {//        // 对得到的data值进行解析与转换即可//        NSString *receiveStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];//        NSLog(@"receiveStr %@", receiveStr);//        NSArray *receiveArray = [receiveStr componentsSeparatedByString:@";"];//        NSString *sourceid = [receiveArray firstObject];//        NSRange rangeSource = [sourceid rangeOfString:@"sourceid="];//        self.fileSouceId = [sourceid substringFromIndex:(rangeSource.location + rangeSource.length)];//        //        NSString *position = [receiveArray lastObject];//        NSRange rangePosition = [position rangeOfString:@"position="];//        self.currentOffset = [position substringFromIndex:(rangePosition.location + rangePosition.length)].integerValue;//        //        [self startTimer:NO];//    }}- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag{    NSLog(@"sock wirte");        if (tagWriteData == tag)    {        [self startTimer:NO];    }}#pragma mark - timer- (void)startTimer:(BOOL)isGCD{    [self uploadFileData:isGCD];}- (void)stopTimer:(BOOL)isGCD{    if (self.fileHandle)    {        [self.fileHandle closeFile];        self.fileHandle = nil;    }        if (isGCD)    {        if (self.GCDSocket)        {            if ([self.GCDSocket isConnected])            {                [self.GCDSocket disconnect];            }            self.GCDSocket.delegate = nil;            self.GCDSocket = nil;        }    }    else    {        // 声明是由用户主动切断        self.socket.userData = SocketOfflineByUser;                if (self.socket)        {            if ([self.socket isConnected])            {                [self.socket disconnect];            }            self.socket.delegate = nil;            self.socket = nil;        }    }        self.isUploadHead = NO;        NSNumber *number = [NSNumber numberWithLongLong:self.currentOffset];    [[NSUserDefaults standardUserDefaults] setObject:number forKey:keyOffset];    [[NSUserDefaults standardUserDefaults] synchronize];}#pragma mark - 信息处理// 文件长度,即大小- (unsigned long long)filelength{    if (!_filelength)    {        NSData *fileData = [[NSData alloc] initWithContentsOfFile:self.filePath];        _filelength = fileData.length;        NSLog(@"fileData length %llu", _filelength);                return _filelength;    }        return _filelength;}// 文件名称- (NSString *)fileName{    if (!_fileName)    {        NSRange fileRange = [self.filePath rangeOfString:@"/" options:NSBackwardsSearch];        if (fileRange.location != NSNotFound)        {            _fileName = [self.filePath substringFromIndex:(fileRange.location + fileRange.length)];            NSLog(@"filename %@", _fileName);        }                return _fileName;    }        return _fileName;}// 对得到的data值进行解析与转换即可- (void)fileInfoWithData:(NSData *)data{    // 对得到的data值进行解析与转换即可    NSString *receiveStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    NSLog(@"receiveStr %@", receiveStr);    NSArray *receiveArray = [receiveStr componentsSeparatedByString:@";"];    NSString *sourceid = [receiveArray firstObject];    NSRange rangeSource = [sourceid rangeOfString:@"sourceid="];    self.fileSouceId = [sourceid substringFromIndex:(rangeSource.location + rangeSource.length)];    [[NSUserDefaults standardUserDefaults] setObject:self.fileSouceId forKey:keySouceId];    [[NSUserDefaults standardUserDefaults] synchronize];        NSString *position = [receiveArray lastObject];    NSRange rangePosition = [position rangeOfString:@"position="];    self.currentOffset = [position substringFromIndex:(rangePosition.location + rangePosition.length)].integerValue;}// 上传文件- (void)uploadFileData:(BOOL)isGCD{    if (self.isUploadHead)    {        // 判断文件是否已有上传记录        self.fileSouceId = [[NSUserDefaults standardUserDefaults] objectForKey:keySouceId];        // 构造拼接协议        NSString *headStr = [[NSString alloc] initWithFormat:@"Content-Length=%llu;filename=%@;sourceid=%@\r\n", self.filelength, self.fileName, ((self.fileSouceId && 0 < self.fileSouceId.length) ? self.fileSouceId : @"")];        NSData *headData = [headStr dataUsingEncoding:NSUTF8StringEncoding];        if (isGCD)        {            [self.GCDSocket writeData:headData withTimeout:timeWithout tag:tagWriteData];                        // 获取服务端返回数据            [self.GCDSocket readDataWithTimeout:timeWithout tag:tagReadData];        }        else        {            [self.socket writeData:headData withTimeout:timeWithout tag:tagWriteData];                        // 获取服务端返回数据            [self.socket readDataWithTimeout:timeWithout tag:tagReadData];        }        headData = nil;                self.isUploadHead = NO;    }    else    {        if (!self.fileHandle)        {            self.fileHandle = [NSFileHandle fileHandleForReadingAtPath:self.filePath];        }                if (self.fileHandle)        {            // 文件当前上传记录位置,及是否继续上传            if (self.filelength > self.currentOffset)            {                NSLog(@"currentOffset %llu", self.currentOffset);                                [self.fileHandle seekToFileOffset:self.currentOffset];                self.currentOffset += OffSet;                NSData *bodyData = [self.fileHandle readDataOfLength:self.currentOffset];                if (isGCD)                {                    [self.GCDSocket writeData:bodyData withTimeout:timeWithout tag:tagWriteData];                }                else                {                    [self.socket writeData:bodyData withTimeout:timeWithout tag:tagWriteData];                }                bodyData = nil;            }            else            {                // 停止上传,即上传完成                [self stopTimer:isGCD];            }        }    }}#pragma mark - gcdasyncSocket连接掉线操作- (void)GCDSocketConnectWithHost:(NSString *)host port:(UInt16)port;{    if (!host || 0 >= host.length)    {        return;    }        self.socketHost = host;    self.socketPort = port;        self.isUploadHead = YES;        NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:keyOffset];    self.currentOffset = number.longLongValue;//    self.currentOffset = 0;        // Create our GCDAsyncSocket instance.    // dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) dispatch_get_main_queue()    self.GCDSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];        // Now we tell the ASYNCHRONOUS socket to connect.    NSError *error = nil;    if (![self.GCDSocket connectToHost:self.socketHost onPort:self.socketPort error:&error])    {        NSLog(@"Unable to connect to due to invalid configuration: %@", error);    }    else    {        NSLog(@"Connecting to \"%@\" on port %hu...", self.socketHost, self.socketPort);    }}- (void)GCDSocketDisconnect{    [self stopTimer:YES];}#pragma mark GCDAsyncSocketDelegate- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err{    // Since we requested HTTP/1.0, we expect the server to close the connection as soon as it has sent the response.    NSLog(@"6 socketDidDisconnect:%@", err);        [self GCDSocketDisconnect];}- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port{    NSLog(@"1 didConnectToHost:%@ port:%hu", host, port);    [self startTimer:YES];}- (void)socket:(GCDAsyncSocket *)sock didReceiveTrust:(SecTrustRef)trust completionHandler:(void (^)(BOOL shouldTrustPeer))completionHandler{    NSLog(@"2 didReceiveTrust:");}- (void)socketDidSecure:(GCDAsyncSocket *)sock{    // This method will be called if USE_SECURE_CONNECTION is set    NSLog(@"3 socketDidSecure:");}- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag{    NSLog(@"4 didWriteDataWithTag:");        if (tagWriteData == tag)    {        [self startTimer:YES];    }}- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{    NSLog(@"5 didReadData:");    if (tagReadData == tag)    {        // 对得到的data值进行解析与转换即可        [self fileInfoWithData:data];    }}@end

使用

// 1 导入头文件#import "SocketManager.h"
// 2 启动socket连接- (void)socketStart:(UIButton *)button{    // 设置IP地址    NSString *host = @"192.168.54.160"; // @"192.186.100.21"; // kTestHost    // 设置端口    UInt16 port = 7876; // 10045; // 23    // 设置文件路径    NSString *filePaht = [[NSBundle mainBundle] pathForResource:@"text" ofType:@"txt"];//    NSString *filePaht = [[NSBundle mainBundle] pathForResource:@"dawan" ofType:@"mp4"];//    NSString *filePaht = [[NSBundle mainBundle] pathForResource:@"hongri" ofType:@"mp3"];    // 设置socket要上传的文件路径    [SocketManager sharedSocket].filePath = filePaht;    // 启动socket连接//    [[SocketManager sharedSocket] socketConnectWithHost:host port:port];    [[SocketManager sharedSocket] GCDSocketConnectWithHost:host port:port];}
// 3 关闭socket连接- (void)socketStop:(UIButton *)button{    // 关闭socket连接//    [[SocketManager sharedSocket] socketDisconnect];    [[SocketManager sharedSocket] GCDSocketDisconnect];}





0 0