1. iOS网络编程层次结构也分为三层:Cocoa层:NSURL,Bonjour,Game Kit,WebKit
Core Foundation层:基于 C 的CFNetwork 和 CFNetServices
OS层:基于 C 的 BSD socket
Cocoa层是最上层的基于 Objective-C 的 API,比如 URL访问,NSStream,Bonjour,GameKit等,这是大多数情况下我们常用的 API。Cocoa 层是基于 Core Foundation 实现的。
Core Foundation层:因为直接使用 socket 需要更多的编程工作,所以苹果对 OS 层的 socket 进行简单的封装以简化编程任务。该层提供了 CFNetwork 和 CFNetServices,其中 CFNetwork 又是基于 CFStream 和 CFSocket。
OS层:最底层的 BSD socket 提供了对网络编程最大程度的控制,但是编程工作也是最多的。因此,苹果建议我们使用 Core Foundation 及以上层的 API 进行编程。
实际项目中,常使用成熟的第三方网络库:
(1)ASIHTTPRequest,停止更新,应该放弃。
2. CFNetwork API 简介
CFNetwork 接口是基于 C 的,下面的接口用于创建一对 socket stream,一个用于读取,一个用于写入:void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);
该函数使用 host 以及 port,CFNetwork 会将该 host 转换为 IP 地址,并转换为网络字节顺序。如果我们只需要一个 socket stream,我们可以将另外一个设置为 NULL。还有另外两个“重载”的创建 socket sream的接口:CFStreamCreatePairWithSocket 和 CFStreamCreatePairWithPeerSocketSignature,在这里就不一一介绍了。
注意:在使用这些 socket stream 之前,必须显式地调用其 open 函数:Boolean CFReadStreamOpen(CFReadStreamRef stream);
Boolean CFWriteStreamOpen(CFWriteStreamRef stream);
但与 socket 不同的是,这两个接口是异步的,当成功 open 之后,如果调用方设置了获取 kCFStreamEventOpenCompleted 事件的标志的话就会其调用回调函数。
而该回调函数及其参数设置是通过如下接口进行的:Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext);
Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext);
该函数用于设置回调函数及相关参数。通过 streamEvents 标志来设置我们对哪些事件感兴趣;clientCB 是一个回调函数,当事件标志对应的事件发生时,该回调函数就会被调用;clientContext 是用于传递参数到回调函数中去。
当设置好回调函数之后,我们可以将 socket stream 当做事件源调度到 run-loop 中去,这样 run-loop 就能分发该 socket stream 的网络事件了。void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
注意,在我们不再关心该 socket stream 的网络事件时,记得要调用如下接口将 socket stream 从 run-loop 的事件源中移除。void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode);
当我们将 socket stream 的网络事件调度到 run-loop 之后,我们就能在回调函数中相应各种事件,比如 kCFStreamEventHasBytesAvailable 读取数据:Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream);
CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength);
或 kCFStreamEventCanAcceptBytes 写入数据:Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream);
CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength);
最后,我们调用 close 方法关闭 socket stream:void CFReadStreamClose(CFReadStreamRef stream);
void CFWriteStreamClose(CFWriteStreamRef stream);