IOS 能够採用三类api 接口进行网络编程,依据抽象层次从低到高分别为socket方式、stream方式、url 方式。
一 、socket 方式
IOS 提供的socket 方式的网络编程接口为CFSocket。CFSocket是BSD sockets的抽象和封装,CFSocket提供BSD sockets差点儿全部的功能,并与run loop集成。用来实现多线程网络编程和网络事件监听。基于 CFSocket能够实现各种类型的 socket编程,包含stream-based 的sockets(如tcp)和packet-based 的sockets(如udp)。须要注意的是在iOS中CFSocket接口在须要时不自己主动激活设备的 cellular modem或on-demand VPN。
CFSocket包含下面编程接口,包含Socket的 创建、配置。以及依据创建和配置好的Socket 进行 远程通讯等接口。
1 Socket的 创建
1 .1、CFSocketCreate
创建一个特定协议和类型的 CFSocket对象
1.2、CFSocketCreateWithSocketSignature
该接口依据一个包含通讯协议和地址的CFSocketSignature结构来创建一个CFSocket对象
1.3、 CFSocketCreateConnectedToSocketSignature
该接口在创建一个CFSocket对象的同一时候还与一个远端主机进行连接。
1.4、CFSocketCreateWithNative
该接口通过封装一个存在的 BSD socket来创建一个CFSocket对象。
2 Socket的配置
2.1 CFSocketCopyAddress
功能: 返回一个 CFSocket对象的本地地址。
语法:
SWIFT
func CFSocketCopyAddress(_ s: CFSocket!) -> CFData!
2.2、CFSocketCopyPeerAddress
功能:返回与一个 CFSocket对象连接的远端地址。
语法:
SWIFT
func CFSocketCopyPeerAddress(_ s: CFSocket!) -> CFData!
2.3 CFSocketDisableCallBacks
功能:暂时取消一个CFSocket对象创建时指定的某种类型的事件回调。
语法:
SWIFT
func CFSocketDisableCallBacks(_ s: CFSocket!,
_ callBackTypes: CFOptionFlags)
2.4 CFSocketEnableCallBacks
功能:又一次同意先前CFSocketDisableCallBacks函数取消的某种类型的事件回调。
语法:
SWIFT
func CFSocketEnableCallBacks(_ s: CFSocket!,
_ callBackTypes: CFOptionFlags)
2.5 CFSocketGetContext
功能:返回一个CFSocket对象的上下文信息。
语法:
SWIFT
func CFSocketGetContext(_ s: CFSocket!,
_ context: UnsafeMutablePointer<CFSocketContext>)
2.6 CFSocketGetNative
返回与一个CFSocket对象相关的本地 BSD socket。
语法:
SWIFT
func CFSocketGetNative(_ s: CFSocket!) -> CFSocketNativeHandle
2.7 CFSocketGetSocketFlags
功能:返回控制一个CFSocket对象的确定行为的 标志。
语法:
SWIFT
func CFSocketGetSocketFlags(_ s: CFSocket!) -> CFOptionFlags
2.8 CFSocketSetSocketFlags
功能:设置控制一个CFSocket对象的确定行为的 标志。
语法:
SWIFT
func CFSocketSetSocketFlags(_ s: CFSocket!,
_ flags: CFOptionFlags)
2.9 CFSocketSetAddress
语法:
SWIFT
func CFSocketSetAddress(_ s: CFSocket!,
_ address: CFData!) -> CFSocketError
功能:为一个CFSocket对象绑定一个本地地址并在本地socket支持的情况下对socket进行配置使其处于监听状态。该函数相应本地socket的 bind以及listen功能。
一旦CFSocket对象绑定地址。依赖于socket的协议,其他进程和主机能连接到该CFSocket对象。
3、Sockets的使用
3.1 CFSocketConnectToAddress
功能:打开与一个远程socket的一个连接。
语法:
SWIFT
func CFSocketConnectToAddress(_ s: CFSocket!,
_ address: CFData!,
_ timeout: CFTimeInterval) -> CFSocketError
3.2 CFSocketCreateRunLoopSource
语法:
SWIFT
func CFSocketCreateRunLoopSource(_ allocator: CFAllocator!,
_ s: CFSocket!,
_ order: CFIndex) -> CFRunLoopSource!
功能:为一个CFSocket对象创建一个CFRunLoopSource对象。该创建的 CFRunLoopSource对象不自己主动加入到一个run loop。为了添加该run loop source到某个run loop。须要调用CFRunLoop对象 的CFRunLoopAddSource函数来为该CFRunLoop对象加入run loop source。
3.3 CFSocketGetTypeID
功能:返回CFSocket对象的 opaque类型相应的类型标示符。
语法:
SWIFT
func CFSocketGetTypeID() -> CFTypeID
3.4 CFSocketInvalidate
功能:使一个CFSocket对象无效,使其停止接收和发送不论什么消息。
语法:
SWIFT
func CFSocketInvalidate(_ s: CFSocket!)
3.5 CFSocketIsValid
功能:返回一个指示一个CFSocket对象是否有效及能否够发送和接收消息的布尔值。
语法:
SWIFT
func CFSocketIsValid(_ s: CFSocket!) -> Boolean
3.6 CFSocketSendData
功能:该函数用来通过一个CFSocket对象发送数据。
语法:
SWIFT
func CFSocketSendData(_ s: CFSocket!,
_ address: CFData!,
_ data: CFData!,
_ timeout: CFTimeInterval) -> CFSocketError
二、stream编程模式
stream编程模式提供了与 unix 的文件操作类似的模式。首先创建和设置流。接着打开流,然后读写流,在流存在时还能够通过查询流的相关属性来读取流的相关信息。在流使用完毕后关闭流。
iOS 为stream编程模式提供的api编程接口包含两大类,一类是Core Foundation框架层用C语言实现的CFStream API(包含CFStream、 CFReadStream 、CFWriteStream等),一类是基于其上的在Foundation框架层用Objective-C语言实现的NSStream API(包含NSStream、NSInputStream NSOutputStream等),两者提供类似的接口和行为。当中某些对象是toll-free bridged类型的。如CFStream 与NSStream。CFReadStream与NSInputStream。CFWriteStream与NSOutputStream之间,因此能够混合使用。
开发者能够依据自己的语言偏好选择使用。
CFStream API的主要接口:
1、CFStream 创建接口
1.1 CFStreamCreatePairWithPeerSocketSignature
功能:创建一对到一个socket的可读和可写流。
1.2 CFStreamCreatePairWithSocketToHost
功能:创建连接到一个特定主机的特定port的一对可读写流。
1.3 CFStreamCreatePairWithSocket
功能:创建一对连接到一个socket的可读写流
1.4 CFStreamCreateBoundPair
功能:创建一对读写流。
其他可读写流创建接口:
1.5 CFReadStreamCreateForHTTPRequest
功能:为一个CFHTTP请求创建一个可读流。
1.6 CFReadStreamCreateForStreamedHTTPRequest
功能:为一个HTTP请求的body保持在内存的CFHTTP请求创建一个可读流。
1.7 CFReadStreamCreateWithFTPURL
功能:创建一个FTP可读流
1.8 CFWriteStreamCreateWithFTPURL
功能:创建一个FTP可读流
2. CFReadStream接口
2.1 流的打开和关闭
CFReadStreamOpen
CFReadStreamClose
2.2 读取数据
CFReadStreamRead
2.3. 调度一个可读流
CFReadStreamScheduleWithRunLoop(_:_:_:)
CFReadStreamUnscheduleFromRunLoop(_:_:_:)
2.4 检查可读流的属性
CFReadStreamCopyProperty(_:_:)
CFReadStreamGetBuffer(_:_:_:)
CFReadStreamCopyError(_:)
CFReadStreamGetError(_:)
CFReadStreamGetStatus(_:)
CFReadStreamHasBytesAvailable(_:)
2.5 设置可读流的属性
CFReadStreamSetClient(_:_:_:_:)
CFReadStreamSetProperty(_:_:_:)
2.6 得到 CFReadStream的 Type ID
CFReadStreamGetTypeID()
3.CFWriteStream 相关接口
3.1 CFWriteStreamClose(_:)
3.2 CFWriteStreamOpen(_:)
3.3 CFWriteStreamWrite(_:_:_:)
3.4 CFWriteStreamScheduleWithRunLoop(_:_:_:)
3.5 CFWriteStreamUnscheduleFromRunLoop(_:_:_:)
3.6 CFWriteStreamCanAcceptBytes(_:)
3.7 CFWriteStreamCopyProperty(_:_:)
3.8 CFWriteStreamCopyError(_:)
3.9 CFWriteStreamGetError(_:)
3.10 CFWriteStreamGetStatus(_:)
3.11 CFWriteStreamSetClient(_:_:_:_:)
3.12 CFWriteStreamSetProperty(_:_:_:)
3.13 CFWriteStreamGetTypeID()
CFStream API的使用步骤:
2、NSStream API的使用1) 利用流创建接口创建相关流;
2)、调用CFReadStreamSetClient (可读流)或CFWriteStreamSetClient (可写流)来登记要接收的流相关的事件;
3)、调用CFReadStreamScheduleWithRunLoop(可读流)或CFWriteStreamScheduleWithRunLoop(可写流)来使在流在一个run loop上进行调度以便接收相关事件;
4)、调用CFReadStreamOpen 或CFWriteStreamOpen 来打开已创建的流。
5)、在读取流的创建时登记的回调中,在接收到kCFStreamEventHasBytesAvailable事件时来读取数据。 在可写流已登记的回调中,在接收到kCFStreamEventCanAcceptBytes 事件时開始发送数据或请求;
6) 传输数据完毕,关闭和释放打开和创建的相关流。
在ios 中因为NSStream类不支持 与一个远程主机连接,而CFStream支持。因此为了使用 NSStream。你须要使用流创建函数CFStreamCreatePairWithSocketToHost或CFStreamCreatePairWithSocketToCFHost来打开一个与远程主机连接的socket并分配一对CFStream 对象(CFReadStream和CFWriteStream)。并cast这些对象到NSStream 对象(相应NSInputStream 和 NSOutputStream)。从而能够使用NSStream类的相关接口进行相关网络编程。如设置接收网络事件的代理对象,调度到当前的run loop,然后打开它们进行相应处理。
代码片段例如以下:
{
NSURL *website = [NSURL URLWithString:urlStr];
if (!website) {
NSLog(@"%@ is not a valid URL");
return;
}
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 80, &readStream, &writeStream);
NSInputStream *inputStream = (__bridge_transfer NSInputStream *)readStream;
NSOutputStream *outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
/* Store a reference to the input and output streams so that
they don't go away.... */
...
}
在NSStream对象打开后。当接收到相关的stream-event网络消息,其代理对象中的handleEvent: 函数被调用,从而进行流相关的网络消息处理, 如发送相关协议的请求或接收应答等。
下面为handleEvent: 函数进行事件处理的代码片段:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSLog(@"stream:handleEvent: is invoked...");
switch(eventCode) {
case NSStreamEventHasSpaceAvailable:
{
if (stream == oStream) {
NSString * str = [NSString stringWithFormat:
@"GET / HTTP/1.0\r\n\r\n"];
const uint8_t * rawstring =
(const uint8_t *)[str UTF8String];
[oStream write:rawstring maxLength:strlen(rawstring)];
[oStream close];
}
break;
}
// continued ...
}
}
三、url 编程模式
url 编程模式通过URL 的方式来实现网络编程。不论什么要存取的网络资源(包含局域网和广域网)都能够用一个URL来表示和存取,并支持设备间的资源共享。url 编程模式系统提供http, https, file, ftp, data等五种协议支持,并同意用户自己开发和登记相关类来支持另外的应用层网络协议,进行协议的扩展。
url 编程模式在IOS系统能够使用两种编程接口:NSURLSession 和NSURLConnection。
对于iOS 7 以后的最新系统推荐使用NSURLSession API。对于老版本号因为不支持NSURLSession,因此必须使用NSURLConnection API。
NSURLSession编程模式是对相关的连接请求通过一个会话来完毕,应用通过创建一系列sessions来实现网络通讯。每个session协调一组相关数据的传输任务。在每个session内,应用加入一系列任务。每个任务表现一个特定URL 请求。
NSURLSession相比NSURLConnection的长处是支持在应用挂起、停止或crashed时能够在后台继续下载数据,即支持任务的取消、重新启动(恢复)、挂起,以及支持从已挂起、取消或失败的下载中又一次恢复下载的能力。
对于简单的请求,还能够直接通过一个简单的NSURL对象来发出请求。并使用一个NSData内存对象或者一个文件的方式来引出NSURL指向的内容。而NSURLConnection API仅仅能通过构造一个NSURLRequest对象或其子类来发出URL请求来请求下载或上传URL数据。 使用一个NSURLRequest请求对象封装一个URL请求,比如HTTP协议方法。除了能够封装一些协议特定的属性外。还能够规定随意本地cached数据的使用策略。
对于NSURLRequest请求对象的应答包含两部分:描写叙述内容的元数据metadata及内容数据本身。两种API对于使用NSURLRequest请求接收的元数据metadata都由NSURLResponse类来封装。当中包含MIME类型、内容长度、编码及提供应答的URL等内容。NSURLResponse协议特定的子类还能提供额外的元数据。如NSHTTPURLResponse提供协议头和WEBserver返回的状态码 等信息。
NSURLSession API的使用:
NSURLSession类支持三种会话类型(默认会话类型、暂时会话、后台会话)以及三种类型的任务(数据任务、下载任务、上传任务)。
数据任务使用NSData 对象来发送和接收内存数据,不存储数据到一个文件,因此不支持后台会话。
下载任务以一个文件的形式引出数据。并支持在应用没有执行时的后台下载。
上传任务用来上传数据(文件),也能够支持应用没有执行时的后台上传。
默认会话和后台会话的差别是后台会话使用一个分离的进程处理全部的传输数据任务,并带有一些限制:后台会话必须使用特定应用代理来提供事件提交。并仅支持HTTP和HTTPS 协议,不支持其他定制协议,并仅支持上传和下载任务,不支持数据任务。
暂时会话不存储不论什么数据到磁盘。全部接收的内容都保存到与会话关联的RAM中。当会话无效时。RAM中接收的内容自己主动被清除。
NSURLSession API的使用步骤:
1 、创建一个NSURLSessionConfiguration配置对象
NSURLSessionConfiguration配置对象提供广泛的配置选项。包含:
1)、特定于单个会话的私有数据存储,包含caches, cookies, credentials, 和protocols;
2)、与一个特定请求或一个会话关联的Authentication;
3)、与一个主机的最大连接数;
4)、与一个资源关联的超时;
5)、最小和最大TLS版本号支持;
6)、定制的代理词典。
7)、cookie策略的控制;
8)、HTTP pipelining行为的控制
2、依据配置创建相应的NSURLSession。
例如以下代码片段展示了依据不同的配置对象创建不同类型的NSURLSession会话对象。
/* Create a session for each configurations. */
self.defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
self.backgroundSession = [NSURLSession sessionWithConfiguration: backgroundConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
self.ephemeralSession = [NSURLSession sessionWithConfiguration: ephemeralConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSession API通过代理来实现异步URL内容存取。代理能够是系统提供的代理,还能够是应用提供的特定代理对象。任务对象当从server接收到数据或传输完毕时调用这些代理对象的方法。
在创建会话指定相应的代理对象。
3、为会话加入任务;
使用例如以下方法来加入数据任务到一个会话。
dataTaskWithURL(_:)
dataTaskWithURL(_:completionHandler:)
dataTaskWithRequest(_:)
dataTaskWithRequest(_:completionHandler:)
使用例如以下方法来加入下载任务到一个会话。
downloadTaskWithURL(_:)
downloadTaskWithURL(_:completionHandler:)
downloadTaskWithRequest(_:)
downloadTaskWithRequest(_:completionHandler:)
downloadTaskWithResumeData(_:)
downloadTaskWithResumeData(_:completionHandler:)
使用例如以下方法来加入上传任务到一个会话
uploadTaskWithRequest(_:fromData:)
uploadTaskWithRequest(_:fromData:completionHandler:)
uploadTaskWithRequest(_:fromFile:)
uploadTaskWithRequest(_:fromFile:completionHandler:)
uploadTaskWithStreamedRequest(_:)
例如以下是数据任务创建代码片段:
NSURL *url = [NSURL URLWithString: @"http://www.example.com/"];
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithURL: url];
[dataTask resume];
下面是下载任务创建代码片段:
NSURL *url = [NSURL URLWithString: @"https://developer.apple.com/library/ios/documentation/Cocoa/Reference/"
"Foundation/ObjC_classic/FoundationObjC.pdf"];
NSURLSessionDownloadTask *downloadTask = [self.backgroundSession downloadTaskWithURL: url];
[downloadTask resume];
4、使用代理方法接收数据及状态信息
会话的数据任务在使用应用特定代理接收数据时必须实现例如以下两个代理方法:
URLSession:dataTask:didReceiveData:
一次一片的提供请求的数据给会话任务。
URLSession:task:didCompleteWithError:
指示请求数据已经全部接收。
会话的下载任务在下载文件时应该实现例如以下代理方法:
URLSession:downloadTask:didFinishDownloadingToURL:
下载内容存储到一个URL指定的一个暂时文件。在该方法返回之前,必须把暂时文件的内容移到一个永久位置。而暂时文件被删除。
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:
为应用提供关于当前下载进度的状态信息
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:
告诉应用已经从从先前的失败下载中恢复。
URLSession:task:didCompleteWithError:
告诉应用下载已经失败。
下载失败恢复处理:
在应用使用cancelByProducingResumeData: 方法取消下载任务时,能够使用downloadTaskWithResumeData: 或 downloadTaskWithResumeData:completionHandler:方法又一次创建一个新下载任务并传送cancelByProducingResumeData:产生的恢复数据从而接着继续下载。
在传输失败时。假设任务可恢复,则调用URLSession:task:didCompleteWithError: 方法。
在传送给URLSession:task:didCompleteWithError: 方法的參数NSError中的 userInfo 词典中包含键值为NSURLSessionDownloadTaskResumeData的恢复数据,因此能够使用downloadTaskWithResumeData: 或 downloadTaskWithResumeData:completionHandler:方法又一次创建一个新下载任务来接着恢复数据继续下载。
系统代理仅能支持主要的URL资源存取任务,不支持认证和后台下载,而且还必须提供一个completion handler block来把返回的URL数据提交到应用。例如以下是一个使用系统代理的代码样例:
NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString: @"http://www.example.com/"]
completionHandler:^(NSData *data, NSURLResponse *response,
NSError *error) {
NSLog(@"Got response %@ with error %@.\n", response, error);
NSLog(@"DATA:\n%@\nEND DATA\n",
[[NSString alloc] initWithData: data
encoding: NSUTF8StringEncoding]);
}] resume];
会话的上传任务的任务创建和相关代理方法:
会话的上传任务使用HTTP POST方法来上传数据。
能够以一个NSData对象、一个文件或使用一个流为HTTP POST请求的body提供内容。
在以NSData对象提供上传数据时,应用调用uploadTaskWithRequest:fromData: 或uploadTaskWithRequest:fromData:completionHandler: 方法来创建上传任务。
在以文件形式提供上传数据时,应用调用uploadTaskWithRequest:fromFile: 或 uploadTaskWithRequest:fromFile:completionHandler:方法来创建上传任务
在以流方式提供上传数据时,应用调用uploadTaskWithStreamedRequest:方法来创建上传任务。
应用特定代理能够通过实现URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:方法来获得上传进度信息。