原文地址:http://www.cnblogs.com/hanjun/archive/2012/11/16/2774107.html
iphone包含了很多框架和库,从底层的套接字到不同层次的封装,可以方便地给程序添加网络功能。
(1)BSD套接字。最底层的套接字,这是Unix网络开发常用的API。如果从其他系统移植程序,而程序用的是BSD套接字,那么网络部分可以继续使用这些API。
(2)CFNetwork framework 。CFNetwork 也是比较底层的, 是对BSD套接字的一个扩展 。它是一个C语言的库,它是基于BSD套接字,提供了对网络协议的抽象。这些抽象使得用户更容易地操作套接字、处理网络的各种连接。。它集成了run-loop,因此使用CFNetwork不用自己去实现事件循环。CFNetwork 还包括了一些网络协议(如HTTP、FTP)的实现,可以在不了解这些协议细节的情况下直接使用。
(3) Foundation framework是基于Objectice-C语言的库,它为CFNetwork API 提供了面向对象的抽象。
(4)CFURL(C)/NSURL(Objective-C)是比较高层的API,它们提供了从Web和FTP服务器下载文件或其他资源的一种简单的方法。其中,CFURL是CFNetwork的一部分,NSURL是Foundation的一部分。
(5)CFNetServices/NSNetServices提供了使用Bonjour注册和发现网络服务的方法。
总之,CFNetwork framework 和 Foundation framework是最为强大的两个库,它们是比较底层、高效,提供的接口也非常丰富。
1.CFSocket
CFSocket是对BSD套接字的一个抽象封装,它提供了BSD套接字的几乎全部功能,还集成了 run-loop。CFSocket可以处理任何类型的套接字,而不仅仅限于流式套接字。
(1)创建CFSocket
常用的方法是CFSocketCreate 和CFSocketCreateWithNative。
CFSocketCreate 方法声明如下:
CFSocketRef CFSocketCreate (
CFAllocatorRef allocator, //指定创建新套接字的内存分配器类型,传入NULL或kCFAllocatorDefault可以使用默认的分配器。一般默认即可。
SInt32 protocolFamily, //指定套接字的协议族。默认使用PF_INET,也就是平时说的IPV4。指定PF_INET6以使用IPV6协议。
SInt32 socketType, //套接字类型,SOCK_STREAM或SOCK_DGRAM。
SInt32 protocol, //套接字使用的协议,IPPROTO_TCP或IPPROTO_UDP。此项需要与套接字类型一致,设为0取默认值(如果socketType为SOCK_STREAM,此项默认值为IPPROTO_TCP,否则为IPPROTO_UDP)。
CFOptionFlags callBackTypes, //CFSocket提供的run-loop可以在特定件发生时回调指定的函数。这个参数使用了位掩码,因此可以使用按位或运算指定多个类型。苹果公司将一些类型定义为枚举值如下所示:
CFSocketCallBack callout,//指定回调函数,当指定的事件类型中的一个发生时函数被调用。这样,不用自己写循环等待连接、发送数据了。
const CFSocketContext *context //存储与套接字相关信息的数据结构,里面可以包含自定义数据。函数会将里面的内容拷出来,因此函数调用完后参数指向的内存不必再保留。可以为NULL。
);
CFSocketCreateWithNative方法,可以使用一个已经存在的BSD套接字来创建CRSocket,
CFSocketRef CFSocketCreateWithNative
{
CFAllocatorRef allocator,
CFSocketNativeHandle sock,
CFOptionFlags callBackTypes,
CFSocketCallBack callout,
const CFSocketContext *context
};
2.套接字函数
创建好CFSocket后,就可以使用它提供的一系列函数了。通过CFSocketNative函数,还可以操作更底层的BSD套接字。以下几个函数式CFSocket中常用的。
(1)CFSocketGetNative 返回系统套接字,通常是int类型的。获得这个套接字,可以使用Unix系统原生的套接字操作。调用setsockopt函数。
(2) CFSocketConnectToAddress 用于将套接字连接到一个正在监听的套接字(服务器)。
(3)CFSocketCopyAddress 返回CFSocket的地址,可以知道套接字正在哪个IP地址上监听。
(4)CFSocketCopyPeerAddress 获得CFSocket连接到的远程套接字的地址。
(5)CFSocketCreateRunLoopSource 为CFSocket创建一个CFRunLoop。
(6)CFSocketSendData 发送数据,需要传入CFSocket、地址(NULL则发送到套接字已经连接到的地址)、要发送的数据(CFDataRef类型)、超时时间。返回值有kCFSocketSuccess、kCFSocketError和kCFSocketTimeout 3种。
3.回调函数
每个回调函数可以获得以下信息,根据类型不同,一些信息也会不一样。
(1)CFSocketRefs 事件对应的CFSocket,这样可以区分不同套接字产生的事件。
(2)CFSocketCallBackType callbackType 标识哪一种事件发生了。
(3)CFDataRef address 包含底层sockaddr信息的CFData指针。可以通过这个参数获得的套接字连接到的远程地址。只有在kCFSocketAcceptCallBack和kCFSocketDataCallBack事件发生时才会提供这个参数。
(4)const void *data 根据回调类型指向不同的数据。如果是kCFSocketDataCallBack类型,这个参数为CFDataRef类型,包含接收到的数据;如果是kCFSocketAcceptCallBack类型,这个参数为指向CFSocketNativeHandle的指针;如果为kCFSocketConnectCallBack,连接在后台失败了,这个参数是指向SInt32类型错误代码的一个指针。其他情况这个参数为NULL。
(5)void *info CFSocketContext结构体中的info成员,是自定义数据。CFSocketContext是在创建CFSocket时被指定的。
4.CFSocketContext
它是包含自定义数据及一些回调的结构体。 因为CFSocket通过run-loop异步通知发生的事件,当有很多连接的时候,如何保持对每个连接相关数据的跟踪变得困难起来。CFSocketContext允许为套接字绑定任何类型的数据,每个回调函数都可以得到这个数据。
CFStream
读写流以设备依赖的方式为交换数据提供了一个简单的途径。可以为内存中的数据、文件中的数据或网络上的数据(使用套接字)创建流,而不用把全部数据一次性读到内存中去。套接字流为通过套接字读写数据提供了一个简单易用的接口。每个套接字都可以与输入流和输出流绑定,允许异步的通信。CFStream包装了大部分读写字节流需要的工作,替换了C语言中传统的send和recv方法。CFSream对象包括两种类型:CFReadStream和CFWriteStream。
1.CFReadStream
它提供了一系列从套接字读取数据相关的方法,下面列出其中一些常用的函数。
(1)CFReadStreamOpen和CFReadStreamClose 打开和关闭一个读取流(read stream),分配或释放相关资源。读取流打开后,才能从中读取数据。
(2)CFReadStreamRead 从流中读取数据的函数,返回实际读取的字节数。这个方法会阻塞直到接收到数据。
(3)CFReadStreamGetBufer 返回指向读取流内部未读数据缓存区的指针。
(4)CFReadStreamGetStatus 返回读取流的当前状态。
(5)CFReadStreamHasBytesAvailable。
返回一个布尔值,表示是否存在数据可以不阻塞地读取。如果不通过实际读取操作函数无法确定是否存在数据,则返回TRUE。
(6) CFReadStreamSheduleWithRunLoop和CFReadStreamUnscheduleFromRunLoop。
通过调用CFReadStreamSheduleWithRunLoop,指定run loop和run loop模式,使用CFReadStreamSetClient方法设置的回调函数在事件发生时会被调用。比如,在流被打开、有数据可以读取等事件发生时调用回调函数。使用 CFReadStreamUnscheduleFromRunLoop函数从run loop中移除。
(7)CFReadStreamSetClient。
设置回调函数在一些事件发生时被调用。回调函数的声明类似于如下所示。
void CallBackTest(
CFReadStreamRef stream,
CFStreamEventType eventType,
void *clientCallBackInfo
)
与CFSocket的回调函数类似,回调函数可以得到事件类型和相关的数据。事件类型的定义如下。
typedef enum
{
kCFStreamEventNone = 0,(没有事件发生)
kCFStreamEventOpenCompleted = 1,(流被成功打开)
kCFStreamEventHasBytesAvailable = 2,(有数据可以读取)
kCFStreamEventCanAcceptBytes = 4,(流可以接受写入数据(用于写入流))
kCFStreamEventErrorOccurred = 8(在流上有错误发生)
kCFStreamEventEndEncountered = 16 (到达了流的结束位置)
} CFStreamEventType;
2.CFWriteStream
CFWriteStream和CFReadStream相对应,它提供了向套接字流写入数据(发送数据)的方法。下面列出一些常用的函数。
(1)CFWriteStreamOpen和CFWriteStreamClose
打开和关闭一个写入流(write stream),分配或释放相关资源。写入流打开后,才能向其中写入数据。
(2)CFWriteStreamWrite
向流中写入数据的函数,返回实际发送的字节数。这个方法会阻塞,直到数据被发送完。
(3)CFWriteStreamStatus
返回写入流的当前状态,为以下几种。
- kCFStreamStatusNotOpen(写入流没有被打开)
- kCFStreamStatusOpening(写入流正在被打开)
- kCFStreamStatusOpen(写入流已经被打开)
- kCFStreamWriting(正在对流执行写入操作)
- kCFStreamStatusAtEnd(没有数据可以向流中写入)
- kCFStreamStatusClosed(写入流已经被关闭)
- kCFStreamStatusError (写入流的时候发生了错误)
(4)CFWriteStreamCanAcceptBytes
返回一个布尔值,表示是否可以不阻塞地向流中写入数据。如果不通过实际写入操作函数无法确定是否可以写入,则返回TRUE。
(5)CFWriteStreamSheduleWithRunloop 和 CFWriteStreamUnscheduleFromRunLoop
通过调用 CFWriteStreamSheduleWithRunloop ,指定run loop和run loop模式,使用CFWriteStreamSetClient方法设置的回调函数在事件发生时会被调用。比如,在流被打开、可以发送数据事件发生时调用回调函数。使用CFWriteStreamUnscheduleFromRunLoop函数从run loop中移出。
(6)CFWriteStreamSetClient
设置回调函数在一些事件发生时被调用。回调函数的声明类似于如下所示。
void CallBackTest (
CFWriteStreamRef stream,
CFStreamEventType eventType,
void *clientCallBackInfo
) ;
与CFSocket的回调函数类似,回调函数可以得到事件类型和相关的数据。