转载自 http://blog.csdn.net/kesalin/article/details/8798039 git
http://blog.csdn.net/kesalin/article/details/8801156github
1. iOS网络编程层次结构也分为三层:编程
Cocoa层:NSURL,Bonjour,Game Kit,WebKit网络
Core Foundation层:基于 C 的 CFNetwork 和 CFNetServices异步
OS层:基于 C 的 BSD socketsocket
Cocoa层是最上层的基于 Objective-C 的 API,好比 URL访问,NSStream,Bonjour,GameKit等,这是大多数状况下咱们经常使用的 API。Cocoa 层是基于 Core Foundation 实现的。ide
Core Foundation层:由于直接使用 socket 须要更多的编程工做,因此苹果对 OS 层的 socket 进行简单的封装以简化编程任务。该层提供了 CFNetwork 和 CFNetServices,其中 CFNetwork 又是基于 CFStream 和 CFSocket。函数
OS层:最底层的 BSD socket 提供了对网络编程最大程度的控制,可是编程工做也是最多的。所以,苹果建议咱们使用 Core Foundation 及以上层的 API 进行编程。oop
实际项目中,常使用成熟的第三方网络库:spa
(1)ASIHTTPRequest,中止更新,应该放弃。
(2)AFNetworking,轻量级。项目源码: https://github.com/AFNetworking/AFNetworking
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);