CFNetWork
iOS网络编程层次模型分为三层:
- Cocoa层:NSURL,Bonjour,Game Kit,WebKit
- Core Foundation层:基于 C 的 CFNetwork 和 CFNetServices
- OS层:基于 C 的 BSD socket
CFNetwork是一种低层的高性能架构,它是基于BSD sockets的基础上做了拓展,提供标准化抽象的API简化FTP HTTP服务器交互任务以及解决DNS主机解析。CFNetwork不管是从理论上还是实际上都是基于BSD sockets的。很多Cocoa类均是基于CFNetwork(例如,NSURL)。Web Kit是用于在窗口显示web内容的一系列Cocoa类。NSURL是使用标准Internet协议与服务器交互的cocoa类。这些类在CFNetwork基础上进行高层封装,自行实现网络交互协议。
CFNetwork的基础API,而这些API都是基于Core Foundation Framework 的 CFSocket 和CFStream。
CFSocket API
Sockets 是网络通讯的最基本一层。一个 socket 起的作用类似与一个电话线接口,它可以使你连接到另一个 socket 上(不论是本地的还是网络另一端的),并且向那个 socket 发送数据。最常见的 socket 抽象概念就是 BSD sockets,而 CFSocket 则是 BSD sockets 的抽象。CFSocket 中包含了少数开销,它几乎可以提供 BSD sockets 所具有的一切功能,并且把 socket 集成进一个“运行循环”当中。CFSocket 并不仅仅限于基于流的 sockets (比如 TCP),它可以处理任何类型的 socket。
你可以利用CFSocketCreate 功能从头开始创建一个 CFSocket 对象,或者利用CFSocketCreateWithNative 函数从 BSD socket 创建。然后,需要利用函数CFSocketCreateRunLoopSource 创建一个“运行循环”源,并利用函数 CFRunLoopAddSource把它加入一个“运行循环”。这样不论 CFSocket 对象是否接收到信息, CFSocket 回调函数都可以运行。
CFStream API
对流的读写操作使我们可以以一种设备无关的方式在各种媒体之间交换数据。你可以为内存、文件或者网络(通过sockets)里面的数据创建流。另外在操作流的时候,所有数据可以分次加载。
数据流本质上是在通信通道中串行传输的一个字节序列,它是单向的,所以如果需要双向传输的话必须操作一个输入流(读操作)和一个输出流(写操作)。除了基于文件的流以外,其他流都是不可搜索的,也就是说:在流数据被提供或者接收之后,就不能再从这个流当中获取数据了。
CFStream API 用两个新的 CFType 对象提供了对这些流的一个抽象:CFReadStream 和 CFWriteStream。两个类型的流都遵循常见的核心基础 API 惯例。
CFStream 的构建基于 CFSocket,同时也是 CFHTTP 和 CFFTP 的基础。尽管 CFStream 并不是 CFNetwork的正式成员,它却是几乎所有 CFNetwork 成员的基础。
CFNetWork基础:
CFFTP API
void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);该函数使用 host 以及 port,CFNetwork 会将该 host 转换为 IP 地址,并转换为网络字节顺序。如果我们只需要一个 socket stream,我们可以将另外一个设置为 NULL。还有另外两个“重载”的创建 socket sream的接口:CFStreamCreatePairWithSocket 和 CFStreamCreatePairWithPeerSocketSignature,在这里就不一一介绍了。
Boolean CFReadStreamOpen(CFReadStreamRef stream); Boolean CFWriteStreamOpen(CFWriteStreamRef stream);
而该回调函数及其参数设置是通过如下接口进行的:
Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext); Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext);
该函数用于设置回调函数及相关参数。通过 streamEvents 标志来设置我们对哪些事件感兴趣;clientCB 是一个回调函数,当事件标志对应的事件发生时,该回调函数就会被调用;clientContext 是用于传递参数到回调函数中去。
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);
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);
CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream); CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream);我们还可以调用如下接口获取 socket stream 的当前状态:
CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream); CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream);