iOS服务器iOS点点滴滴

局域网内通过Socket传输文件(Android&iOS

2017-11-22  本文已影响101人  _令_狐_冲_

实现步骤

  1. 读取设备当前连接的网络信息
  2. 服务端监听端口
  3. 客户端与服务端建立连接
  4. 发送文件/接收文件

读取设备的网络信息

/**
 获取本机wifi名称
 @return wifi名称
 */
+ (NSString *)getWifiName {
    NSArray *ifs = (__bridge_transfer NSArray *)CNCopySupportedInterfaces();
    if (!ifs) {
        return nil;
    }
    NSString *WiFiName = nil;
    for (NSString *ifnam in ifs) {
        NSDictionary *info = (__bridge_transfer NSDictionary *)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
        if (info && [info count]) {
            // 这里其实对应的有三个key:kCNNetworkInfoKeySSID、kCNNetworkInfoKeyBSSID、kCNNetworkInfoKeySSIDData,
            // 不过它们都是CFStringRef类型的
            WiFiName = [info objectForKey:(__bridge NSString *)kCNNetworkInfoKeySSID];
            break;
        }
    }
    return WiFiName;
}

/**
 获取本机wifi环境下本机ip地址
 @return ip地址
 */
+ (NSString *)localIpAddressForCurrentDevice
{
    NSString *address = nil;
    struct ifaddrs *interfaces = NULL;
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    success = getifaddrs(&interfaces);
    if (success == 0) {
        temp_addr = interfaces;
        while(temp_addr != NULL) {
            if(temp_addr->ifa_addr->sa_family == AF_INET) {
                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {
                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
                    return address;
                }
            }
            temp_addr = temp_addr->ifa_next;
        }
        freeifaddrs(interfaces);
    }
    return nil;
}

服务端端口的监听 / 客户端连接

监听端口

BOOL isSucces = [[ServerSocketManager shareServerSocketManager] startListenPort:portStr.intValue];

// 实现
- (BOOL)startListenPort:(uint16_t)prot{
  if (prot <= 0) {
      NSAssert(prot > 0, @"prot must be more zero");
  }
  if (!self.serverSocket) {
      self.serverSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
  }
  [self.serverSocket disconnect];
  NSError *error = nil;
  BOOL result = [self.serverSocket acceptOnPort:prot error:&error];
  if (result && !error) {
      return YES;
  }else{
      return NO;
  }
}

// 通过二维码扫描获取到 ip prot 在建立连接
[[SocketManager shareSocketManager] connentHost:CURRENT_HOST prot:CURRENT_PORT];

- (BOOL)connentHost:(NSString *)host prot:(uint16_t)port{
    if (host==nil || host.length <= 0) {
        NSAssert(host != nil, @"host must be not nil");
    }

    [self.tcpSocketManager disconnect];
    if (self.tcpSocketManager == nil) {
        self.tcpSocketManager = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
    }
    NSError *connectError = nil;
    [self.tcpSocketManager connectToHost:host onPort:port error:&connectError];
    
    if (connectError) {
        return NO;
    }
    // 可读取服务端数据
    [self.tcpSocketManager readDataWithTimeout:-1 tag:0];
    return YES;
}

客户端: IMG_2390.PNG IMG_2391.PNG
服务端: IMG_0135.PNG

#pragma mark - GCDSocketDelegate

- (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket{
    if (!self.clientSocketArray) {
        self.clientSocketArray = [NSMutableArray array];
    }
    [self.clientSocketArray addObject:newSocket];
    [newSocket readDataWithTimeout:- 1 tag:0];
    if ([self.delegate respondsToSelector:@selector(serverSocketManager:connect:connectIp:)]) {
        [self.delegate serverSocketManager:self connect:YES connectIp:newSocket.connectedHost];
    }
}
创建需要传输的文件模型 SockerSendItem

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, SENDFILE_TYPE)
{
    SENDFILE_TYPE_FILEINFOLIST = 0,// 列表
    SENDFILE_TYPE_IMAGE = 1,   // 图片
    SENDFILE_TYPE_VIDEO,       // 视频
    SENDFILE_TYPE_AUDIO,       // 音频
    SENDFILE_TYPE_TEXT         // 文字
};

@interface SockerSendItem : NSObject
/// 文件名称
@property (nonatomic, copy) NSString *fileName;
/// 文件类型
@property (nonatomic, assign) SENDFILE_TYPE type;
/// 文件总大小
@property (nonatomic, assign) NSInteger fileSize;
/// 文件已上传大小
@property (nonatomic, assign) NSInteger upSize;
/// 资源路径
@property (nonatomic, copy) NSURL *filePath;
/// id 序列号
@property (nonatomic, assign) NSInteger index;
/// 是否正在上传中
@property (nonatomic, assign) BOOL isSending;
/// 当前文件是否已经全部传输完毕
@property (nonatomic, assign) BOOL isSendFinish;
/// 文件类型
@property (nonatomic, copy) NSString *typeStr;
/// 资源文件
@property (nonatomic, strong) id asset;
/// 缩略图路径
@property (nonatomic, copy) NSString *thumImgPath;
/// 是否需要取消传输(isSending = NO && isSendFinish = NO)时有效
@property (nonatomic, assign) BOOL isCancleSend;
@end
客户端发送列表文件json信息 (这个可以自己定义)

- (void)setNeedSendItems:(NSMutableArray *)needSendItems{
    _needSendItems = needSendItems;
    
    if (needSendItems.count <= 0) {
        return;
    }
   
    // 固定头部
    SockerSendItem *headItem = [[SockerSendItem alloc] init];
    headItem.index = LISTTAG;
    headItem.fileName = @"列表";
    NSData *headData = [self creationHeadStr:headItem];

    // 列表数据
    NSInteger count = needSendItems.count;
    NSMutableArray *itemDicArray = [NSMutableArray arrayWithCapacity:count];
    for (NSInteger i = 0; i < count; i++) {
        SockerSendItem *item = needSendItems[i];
        item.index = i;
        NSMutableDictionary *dic = [NSMutableDictionary dictionary];
        dic[@"fileName"] = item.fileName;
        dic[@"fileType"] = [NSNumber numberWithInteger:item.type];
        dic[@"fileSize"] = [NSNumber numberWithInteger:item.fileSize];
        dic[@"id"] = [NSNumber numberWithInteger:item.index];
        [itemDicArray addObject:dic];
    }
    
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:itemDicArray
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:nil];

    // 尾部拼接
    NSString *s = @"\nend\n";
    NSData *data = [s dataUsingEncoding:NSUTF8StringEncoding];

    NSMutableData *listHeadData = [NSMutableData dataWithData:headData];
    [listHeadData appendData:jsonData];
    [listHeadData appendData:data];
    [self.tcpSocketManager writeData:listHeadData withTimeout:-1 tag:LISTTAG];
    MYLog(@"listHeadData = %@",[[NSString alloc] initWithData:listHeadData encoding:NSUTF8StringEncoding]);
    
}
客服端传输列表: IMG_2393.PNG
客户端开始传输: IMG_2394.PNG
客户端取消某个文件的传输: IMG_2395.PNG
服务端接收到列表信息并将数据写入本地

/// 接收到消息
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{
    NSString *readStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if ([readStr containsString:SENDFILEINFOLIST]) { // 接受到列表
        // 解析列表头部
        NSString *jsonStr = [self listOrHeadStr:readStr OfString:SENDFILEINFOLIST];
        NSData *jsonData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
        NSArray *array = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil];
        _socketItemArray = [ServerSocketItem mj_objectArrayWithKeyValuesArray:array];
        if ([self.delegate respondsToSelector:@selector(serverSocketManager:fileListAccept:)]) {
            [self.delegate serverSocketManager:self fileListAccept:_socketItemArray];
        }
        NSLog(@"listjsonStr = %@",jsonStr);
        
        for (GCDAsyncSocket *clientSock in self.clientSocketArray) {
            [clientSock writeData:[FILE_LIST_SEND_END dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
        }
    }else if ([readStr containsString:SENDFILEHEADINFO]){ // 接受到头部
        // 解析头部信息
        NSString *jsonStr = [self listOrHeadStr:readStr OfString:SENDFILEHEADINFO];
        NSLog(@"headjsonStr = %@",jsonStr);
        NSDictionary *dic = [jsonStr hj_jsonStringToDic];
        ServerSocketItem *item = [ServerSocketItem mj_objectWithKeyValues:dic];
        self.currentSendItem = item;
        if (item.ID < self.socketItemArray.count) {
            [self.socketItemArray replaceObjectAtIndex:item.ID withObject:item];
            if ([self.delegate respondsToSelector:@selector(serverSocketManager:fileHeadAccept:)]) {
                [self.delegate serverSocketManager:self fileHeadAccept:item];
            }
        }
        // 通知客户端已经接受完成
        for (GCDAsyncSocket *clientSock in self.clientSocketArray) {
            NSString *str = [NSString stringWithFormat:@"%@%zd\n",FILE_HEAD_SEND_END,item.ID];
            [clientSock writeData:[str dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:0];
        }
    }else{
    
        if (_currentSendItem && _currentSendItem.isCancel == 0) {
            _currentSendItem.acceptSize += data.length;
            _currentSendItem.beginAccept = YES;
            NSLog(@"acceptSize = %zd",_currentSendItem.acceptSize);
            if (!self.outputStream) {
                _currentSendItem.filePath = [self.dataSavePath stringByAppendingPathComponent:[_currentSendItem.fileName lastPathComponent]];
                self.outputStream = [[NSOutputStream alloc] initToFileAtPath:_currentSendItem.filePath append:YES];
                [self.outputStream open];
            }
            // 输出流 写数据
            NSInteger byt = [self.outputStream write:data.bytes maxLength:data.length];
            NSLog(@"byt = %zd",byt);
            
            if (_currentSendItem.acceptSize >= _currentSendItem.fileSize) {
                _currentSendItem.finishAccept = YES;
                [self.outputStream close];
                self.outputStream = nil;
            }
            
            if ([self.delegate respondsToSelector:@selector(serverSocketManager:fileAccepting:)]) {
                [self.delegate serverSocketManager:self fileAccepting:_currentSendItem];
            }
        }
        
    }
    
    [sock readDataWithTimeout:- 1 tag:0];
}
IMG_0139.PNG
IMG_0140.PNG
文件传输进度的读取

// 分段传输完成后的 回调
- (void)socket:(GCDAsyncSocket *)sock didWritePartialDataOfLength:(NSUInteger)partialLength tag:(long)tag {
    self.currentSendItem.upSize += partialLength;
    if ([self.delegate respondsToSelector:@selector(socketManager:itemUpingrefresh:)] && (tag<self.needSendItems.count)) {
        SockerSendItem *item = self.needSendItems[tag];
        item.isSending = YES;
        [self.delegate socketManager:self itemUpingrefresh:item];
    }
    MYLog(@"%f--tag = %zd",((self.currentSendItem.upSize * 1.0) / self.currentSendItem.fileSize),tag);
}

/// 接收到消息
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{}

- (void)writeDataWithItem:(SockerSendItem *)sendItem{
    NSData *sendData = [NSData dataWithContentsOfURL:sendItem.filePath options:NSDataReadingMappedIfSafe error:nil];
    MYLog(@"sendData = %zd",sendData.length);
    [self.tcpSocketManager writeData:sendData withTimeout:-1 tag:sendItem.index];
}
上一篇 下一篇

猜你喜欢

热点阅读