iOS 实现简单的Http 服务

http 是计算机之间通讯协议的比较简单的一种。在iPhone上,由于没有同步数据和文件共享,所以实现PC与设备之间的数据传输的最佳方式就是在程序中嵌套一个http 服务器。在这篇帖子中,我将简单的演示第三方的http 服务器的使用。

示例程序运行如下(在PC端输入设备的IP和端口,便可选取当前PC的文件进行上传至当前的设备)

技术分享

该服务器涉及的相关类如下图所示

技术分享

技术分享

代码实现,创建一个自己需求的HttpConnection类继承于第三方的HttpConnection,代码如下

MyHTTPConnection.h

#import "HTTPConnection.h"

 

@class MultipartFormDataParser;

 

@interface MyHTTPConnection : HTTPConnection

{

    MultipartFormDataParser * parser;

    NSFileHandle * storeFile;

    NSMutableArray * uploadedFiles;

}

@end

 MyHTTPConnection.m

#import "MyHTTPConnection.h"

 

#import "HTTPMessage.h"

#import "HTTPDataResponse.h"

#import "DDNumber.h"

#import "HTTPLogging.h"

 

#import "MultipartFormDataParser.h"

#import "MultipartMessageHeaderField.h"

#import "HTTPDynamicFileResponse.h"

#import "HTTPFileResponse.h"

 

static const int httpLogLevel = HTTP_LOG_LEVEL_VERBOSE;

 

 

@interface MyHTTPConnection ()

{

    float totalSize;

    float progress;

}

 

 

@end

 

 

 

@implementation MyHTTPConnection

 个人需要,1-8的方法都有实现

1:- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path

{

    NSLog(@"%s",__FUNCTION__);

    HTTPLogTrace();

    if (  [ method isEqualToString:@"POST"] ) {

        if ( [path isEqualToString:@"/upload.html"] ) {

            return YES;

        }

    }

    return  [super supportsMethod:method atPath:path];

}

 

2:-(BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path{

if([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.html"]) {

        // here we need to make sure, boundary is set in header

        NSString* contentType = [request headerField:@"Content-Type"];

        NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;

        if( NSNotFound == paramsSeparator ) {

            return NO;

        }

        if( paramsSeparator >= contentType.length - 1 ) {

            return NO;

        }

        NSString* type = [contentType substringToIndex:paramsSeparator];

        if( ![type isEqualToString:@"multipart/form-data"] ) {

            // we expect multipart/form-data content type

            return NO;

        }

        

// enumerate all params in content-type, and find boundary there

        NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];

        for( NSString* param in params ) {

            paramsSeparator = [param rangeOfString:@"="].location;

            if( (NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1 ) {

                continue;

            }

            NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];

            NSString* paramValue = [param substringFromIndex:paramsSeparator+1];

            

            if( [paramName isEqualToString: @"boundary"] ) {

                // let‘s separate the boundary from content-type, to make it more handy to handle

                [request setHeaderField:@"boundary" value:paramValue];

            }

        }

        // check if boundary specified

        if( nil == [request headerField:@"boundary"] )  {

            return NO;

        }

        return YES;

    }

    

return [super expectsRequestBodyFromMethod:method atPath:path];

}

3:- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path{

if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.html"])

{

    

// this method will generate response with links to uploaded file

NSMutableString* filesStr = [[NSMutableString alloc] init];

        

for( NSString* filePath in uploadedFiles ) {

//generate links

[filesStr appendFormat:@"%@<br/><br/>", [filePath lastPathComponent]];

}

NSString* templatePath = [[config documentRoot] stringByAppendingPathComponent:@"upload.html"];

NSDictionary* replacementDict = [NSDictionary dictionaryWithObject:filesStr forKey:@"MyFiles"];

// use dynamic file response to apply our links to response template

return [[HTTPDynamicFileResponse alloc] initWithFilePath:templatePath forConnection:self separator:@"%" replacementDictionary:replacementDict];

}

if( [method isEqualToString:@"GET"] && [path hasPrefix:@"/upload/"] ) {

// let download the uploaded files

return [[HTTPFileResponse alloc] initWithFilePath: [[config documentRoot] stringByAppendingString:path] forConnection:self];

}

 

return [super httpResponseForMethod:method URI:path];

}

4:- (void)prepareForBodyWithSize:(UInt64)contentLength{

HTTPLogTrace();

    totalSize = contentLength/1000.0/1000.0;

NSLog(@"%f",contentLength/1000.0/1000.0);

// set up mime parser

    NSString* boundary = [request headerField:@"boundary"];

    parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];

    parser.delegate = self;

    

uploadedFiles = [[NSMutableArray alloc] init];

}

5:- (void)processBodyData:(NSData *)postDataChunk{

HTTPLogTrace();

    progress += postDataChunk.length/1024.0/1024.0;

    NSLog(@"%f",progress);

    NSString * temp  = [ NSString stringWithFormat:@"上传进度:%.2fMB/%.2fMB",progress,totalSize ];

    dispatch_async(dispatch_get_main_queue(), ^{

         [SVProgressHUD showProgress:progress/totalSize status:temp maskType:SVProgressHUDMaskTypeBlack];

    });

 

    // append data to the parser. It will invoke callbacks to let us handle

    // parsed data.

    [parser appendData:postDataChunk];

}

6:- (void) processStartOfPartWithHeader:(MultipartMessageHeader*) header{

 NSLog(@"%s",__FUNCTION__);

    MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"];

NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent];

    NSLog(@"####filename=%@",filename);

    if ( (nil == filename) || [filename isEqualToString: @""] ) {

        // it‘s either not a file part, or

// an empty form sent. we won‘t handle it.

return;

}

     

    NSString* uploadDirPath = [self CreatreTempDir] ;

    

   // NSLog(@"uploadDirPath%@",[self CreatDir]);

BOOL isDir = YES;

if (![[NSFileManager defaultManager]fileExistsAtPath:uploadDirPath isDirectory:&isDir ]) {

[[NSFileManager defaultManager]createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];

}

    NSString * filePath = [uploadDirPath stringByAppendingPathComponent:filename];

    NSFileManager * fileManager = [NSFileManager defaultManager];

if( [[NSFileManager defaultManager] fileExistsAtPath:filePath] ) {

        storeFile = nil;

    }

    else {

HTTPLogVerbose(@"Saving file to %@", filePath);

        NSError *error;

if(![[NSFileManager defaultManager] createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:&error]) {

HTTPLogError(@"Could not create directory at path: %@----%@", filePath,error);

}

if(![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]) {

HTTPLogError(@"Could not create file at path: %@", filePath);

}

storeFile = [NSFileHandle fileHandleForWritingAtPath:filePath];

[uploadedFiles addObject: [NSString stringWithFormat:@"/upload/%@", filename]];

    }

 

}

7:- (void) processContent:(NSData*) data WithHeader:(MultipartMessageHeader*) header{

 if(!storeFile)

    {

        MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"];

        NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent];

        NSLog(@"####filename=%@",filename);

        if ( (nil == filename) || [filename isEqualToString: @""] ) {

            // it‘s either not a file part, or

            // an empty form sent. we won‘t handle it.

            return;

        }

        NSString* uploadDirPath = [self CreatreTempDir] ;

        NSString * filePath = [uploadDirPath stringByAppendingPathComponent:filename];

        storeFile = [NSFileHandle fileHandleForWritingAtPath:filePath];

        [uploadedFiles addObject: [NSString stringWithFormat:@"/upload/%@", filename]];

    }

if( storeFile ) {

[storeFile writeData:data];

}

 

}

8:- (void) processEndOfPartWithHeader:(MultipartMessageHeader*) header{

dispatch_async(dispatch_get_main_queue(), ^{

        [SVProgressHUD dismiss];

    });

    progress = 0.f;

    

    MultipartMessageHeaderField* disposition = [header.fields objectForKey:@"Content-Disposition"];

NSString* filename = [[disposition.params objectForKey:@"filename"] lastPathComponent] ;

   // NSLog(@"####END ---filename=%@",filename);

    NSString * tempFilePath = [[ self CreatreTempDir ] stringByAppendingPathComponent:filename];

    

    NSMutableArray *arraynumber = [NSMutableArray arrayWithContentsOfFile:[Global getChangePathName]];

    NSString *strnumber = [arraynumber lastObject];

    

    NSString * uploadFilePath = [[ self CreatDir ] stringByAppendingPathComponent: [[NSString stringWithFormat:@"%d.",[strnumber intValue] + 1 ] stringByAppendingString:filename]];

    

    [arraynumber addObject:[NSString stringWithFormat:@"%d", [strnumber intValue] + 1]];

    

    [arraynumber writeToFile:[Global getChangePathName] atomically:YES];

 

    BOOL result = [ self copyMissFile:tempFilePath toPath:uploadFilePath ];

    

    if (result) {

        NSLog(@"移动成功");

    }else{

        NSLog(@"移动失败");

    }

    

[storeFile closeFile];

storeFile = nil;

}

- (BOOL)copyMissFile:(NSString * )sourcePath toPath:(NSString *)toPath{

BOOL retVal ;

    NSLog(@"%s",__FUNCTION__);

//    if ( [fileManger fileExistsAtPath:toPath]  ) {

//        [fileManger removeItemAtPath:toPath error:nil];

//    }

    retVal = [[ NSFileManager defaultManager ] moveItemAtPath:sourcePath toPath:toPath error:nil];

    

    return retVal;

}

下面两个方法都是创建文件,大家自己可以随意定义

-(NSString *)CreatDir;

-(NSString *)CreatreTempDir;

下面根据需求实现以下这些方法,我是在全局的类里面实现的

Global.m

+ (BOOL)createiPhoneServer  //创建并设置iPhone的服务器

{

    Global * global = [ Global sharedGlobal ];

    

    global.httpServer = [[HTTPServer alloc] init];

    [global.httpServer setType:@"_http._tcp."];

    NSString *docRoot = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"MyResources"] stringByAppendingPathComponent:@"Web"];

 

    BOOL  isDir  = YES ;

    if ( [[NSFileManager defaultManager] fileExistsAtPath:docRoot isDirectory:&isDir  ]  ) {

        NSLog(@"找到Web文件夹");

    }

    [global.httpServer setDocumentRoot:docRoot];

    [global.httpServer setPort:Http_Port];//本地端口

    [global.httpServer setConnectionClass:[MyHTTPConnection class]];//设置连接类为我们之前代码实现的连接类

    

    return YES;

}

//开启服务器

+ (BOOL)startServer

{

    Global * global = [ Global sharedGlobal];

    NSError * error;

    

    

    if ( [global.httpServer start:&error] ) {

        NSLog(@"开启成功%u",[global.httpServer listeningPort]);

        return YES;

    }else

    {

        NSLog(@"开启失败,%@",error);

        return NO;

 

    }

    

}

+ (BOOL)stopServer

{

    [[Global sharedGlobal].httpServer stop:YES];

    return YES;

}

 //获得当前设备的IP

+(NSString *)getIPAddress

{

    NSString *address = @"开启失败";

    struct ifaddrs *interfaces = NULL;

    struct ifaddrs *temp_addr = NULL;

    int success = 0;

    // retrieve the current interfaces - returns 0 on success

    success = getifaddrs(&interfaces);

    

    

    if (success == 0) {

        // Loop through linked list of interfaces

        temp_addr = interfaces;

        

        

        while(temp_addr != NULL) {

            if(temp_addr->ifa_addr->sa_family == AF_INET) {

                // Check if interface is en0 which is the wifi connection on the iPhone

                if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) {

                    // Get NSString from C String

                    address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];

                }

            }

            temp_addr = temp_addr->ifa_next;

        }

    }

    // Free memory

    freeifaddrs(interfaces);

    

    NSString * str = nil;

    if ( [address isEqualToString:@"开启失败"] ) {

        str = @"开启失败";

    }else {

        str = [NSString stringWithFormat:@"http://%@:%d",address, Http_Port ];

    }

    return str;

}

 

 

 //获取当前设备的网络状态

+(NSString *) getDeviceSSID

 

{

    

    NSArray *ifs = (__bridge id)CNCopySupportedInterfaces() ;

    

    id info = nil;

    

    for (NSString *ifnam in ifs) {

        

        info = (__bridge id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);

        

        if (info && [info count]) {

            

            break;

            

        }

        

    }

    

    NSDictionary *dctySSID = (NSDictionary *)info;

    

    NSString *ssid = [ dctySSID objectForKey:@"SSID" ];

    

    

    ssid = ssid== nil?@"无网络":ssid;

    return ssid;

    

}

 然后再你需要的地方开启这个服务即可,至此http 服务搭建完成,开启服务之后就可在PC的浏览器输入当前的设备IP地址和端口,如http://192.168.2.155:8088,然后上传所需的文件即可,根据程序设计,这些文件会存在相应的地方

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。