没有合适的资源?快使用搜索试试~ 我知道了~
iOS CFNetwork编程指南
5星 · 超过95%的资源 需积分: 10 47 下载量 196 浏览量
2013-05-21
18:05:37
上传
评论
收藏 672KB PDF 举报
温馨提示
试读
21页
CFNetwork+编程指南 详细讲解了iOS CFNetwork编程对于想深入研究CFNetwork框架的同学非常 值得下载学习
资源推荐
资源详情
资源评论
所有内容来自:
http://www.apple.com.cn/developer/mac/library/documentation/IntelWeb/Conceptual/CFNetwork/Concepts/chapter_2_section_4.html
介绍
CFNetwork 是核心服务框架中的个框架,它提供了个抽象化的网络协议库。这种抽象使得进行各种网络任务都非常容易,比如:
▪ 使用 BSD sockets
▪ 利用 SSL 或者 TLS 创建加密的连接
▪ 解析 DNS 主机
▪ 涉及到 HTTP 的任务,对 HTTP 和 HTTPS 服务器进行认证
▪ 涉及到 FTP 服务器的任务
▪ 发布、解析以及浏览 Bonjour 服务
本书面向那些希望在自己的应用中使用网络协议的开发者。为了更好的理解这本书的内容,读者需要对些网络编程方面的概念有所了解,比如 BSD sockets,流以及 HTTP 协议。另外,读者还需要熟悉 Mac
OS X 编程方面的概念,包括循环运行等等。更多有关 Mac OS X 系统的内容请参考 Mac OS X 技术概述。
本文档的结构
本文档包含如下章节:
▪ "CFNetwork 概念" 介绍了每个 CFNetwork API 以及相互之间的互动关系。
▪ 与流相关的操作" 描述了如何利用 CFStream API 发送和接收网络数据。
▪ "与 HTTP 服务器通讯" 描述了如何发送和接收 HTTP 消息。
▪ "与认证 HTTP 服务器通讯" 描述了如何与安全 HTTP 服务区进行通讯。
▪ "操作 FTP 服务器" 描述了如何从一个 FTP 服务器那里上载和下载文件,以及如何下载字典列表。
▪ "使用网络诊断功能" 描述了如何在自己的应用中增加网络诊断功能。
参考
更多有关 Mac OS X 系统的网络 API,可以阅读如下文档:
▪ 从网络开始
下面的参考文档都是有关 CFNetwork 的:
▪ CFFTPStream 参考 是有关 CFFTPStream API 的参考文档。
▪ CFHTTPMessage 参考 是有关 CFHTTPMessage API 的参考文档。
▪ CFHTTPAuthentication 参考 是有关 CFHTTPAuthentication API 的参考文档。
▪ CFHost 参考 是有关 CFHost API 的参考文档。
▪ CFNetServices 参考 是有关 CFNetServices API 的参考文档。
▪ CFNetDiagnostics 参考 是有关 CFNetDiagnostics API 的参考文档。
除了 Apple 公司提供的文档之外,下面还有个有关 socket 层编程的参考文档:
▪ UNIX 网络编程,第卷(Stevens, Fenner 和 Rudo")
CFNetwork 概念
CFNetwork 是一个低层次、高性能的框架,它可以让你能够灵活操纵协议栈。它是 BSD sockets 的扩展,作为个标准 socket 抽象 API ,它可以提供一些对象,使得与 FTP 和 HTTP 服务器进行通讯或者解
析 DNS 主机这样的任务变得更加简单。CFNetwork is 不论从物理上还是理论上都是基于 BSD sockets。
就像 CFNetwork 依赖于 BSD sockets,有一些 Cocoa 类也是基于 CFNetwork。NSURL 就是这样的个类,它被用来和使用标准 Internet 协议的服务器进行通讯。另外,Web Kit 是一些 Cocoa 类的集合,
可以用来在窗体中显示网络内容。以上这些类都用于高层次的操作,它们内部已经实现了大部分网络协议的细节。综上所述,整个软件层次结构看起来就如图 1-1 中的图片。
图 1-1!!CFNetwork 以及 Mac OS X 中的其他软件层
内容:
何时使用 CFNetwork
CFNetwork 基本结构
CFNetwork API 概念
何时使用 CFNetwork
CFNetwork 与 BSD sockets 相比有很多优势。它集成了“运行循环”,因此如果你的应用是基于“运行循环”的,那么你可以在使用网络协议的同时不需要实现线程。CFNetwork 还包含些对象,它们能够帮助你
在使用网络协议的时候不用自己完成实现细节。比如,你可以使用 FTP 协议,而不需要实现 CFFTP API 的所有细节。如果你理解了这些网络协议,需要用到它们提供的底层控制同时又不想自己实现所有细节,
那么 CFNetwork 应该是你正确的选择。
使用 CFNetwork 而不是 Cocoa 框架 NSURL 有几点好处。CFNetwork 更加专注于网络协议,而 NSURL 更加专注于数据访问,比如通过 HTTP 或者 FTP 传输数据。尽管 NSURL的确也提供了一些可配置功
能,可是 CFNetwork 提供的要多得多。另外,NSURL 还需要你使用 Objective-C。如果做不到这点的话,还是应该使用 CFNetwork。有关更多基本网络框架的内容,请阅读 URL 加载系统。
在理解了 CFNetwork 与 Mac OS X 系统中其他的网络 API 的关系之后,你应该对 CFNetwork API 有一定了解,包括构成 CFNetwork 基本结构的两个 API。
CFNetwork 基本结构
在学习 CFNetwork API 之前,你必须首先理解作为 CFNetwork 基础的最主要的 API。CFNetwork 的存在依赖两个 API,这两个 API 是 Core Foundation 框架的一部分,CFSocket 和 CFStream。要使用
CFNetwork 就必须理解这些 API。
本节内容:
CFSocket API
CFStream API
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 回调函数都可以运行。
请阅读 CFSocket 参考 中有关 CFSocket API 的更多内容。
CFStream API
对流的读写操作使我们可以以种设备无关的方式在各种媒体之间交换数据。你可以为内存、文件或者网络(通过sockets)里面的数据创建流。另外在操作流的时候,所有数据可以分次加载。
数据流本质上是在通信通道中串行传输的个字节序列,它是单向的,所以如果需要双向传输的话必须操作个输入流(读操作)和个输出流(写操作)。除了基于文件的流以外,其他流都是不可搜索的,也
就是说:在流数据被提供或者接收之后,就不能再从这个流当中获取数据了。
CFStream API 用两个新的 CFType 对象提供了对这些流的个抽象:CFReadStream 和 CFWriteStream。两个类型的流都遵循常见的核心基础 API 惯例。有关核心基础类型的更多信息,请参考设计概念。
CFStream 的构建基于 CFSocket,同时也是 CFHTTP 和 CFFTP 的基础。在图 1-2 中你可以看到,尽管 CFStream 并不是 CFNetwork的正式成员,它却是几乎所有 CFNetwork 成员的基础。
图 1-2!!CFStream API 的结构
你几乎可以用操作 UNIX 文件描述符的方式对流进行读写操作。首先,实例化流对象的时候需要指定流的类型(内存、文件或者socket)并且设置任何个可选项。然后,打开流并可以进行任意次的读写操作。
当流还存在的时候,你可以通过流的属性获取有关它的信息。流属性包括有关流的任何信息,比如它的数据源或者目标,这些都不属于被读写的实际数据范畴之内。当你不再需要个流的时候,需要关闭并把它
丢弃。
CFStream 的函数如果不能进行至少个字节数据的读写操作的话,它们可能会暂停或者阻塞当前的进程。为了避免在阻塞的时候从个流读数据或者向个流写数据,可以使用这些函数的异步操作版本,并且
把有关这个流的操作放入个循环当中。当可以从流中读写数据的时候,你的回调函数就会被调用。
另外,CFStream 还内置了对安全 Sockets 层 (SSL) 协议的支持。你可以建立个包含流的 SSL 信息的字典,其中的信息包括需要的安全级别或者自签署的认证。然后把这些信息当作
kCFStreamPropertySSLSettings 属性传递给流,这样个流就被转换成了个 SSL 流。
要创建个客户定制的 CFStream 是不可能的。比如,如果你想要对客户数据库文件当中的对象进行数据流操作,那么仅仅希望通过创建具有自己风格的 CFStream 对象是办不到这点的,而只有通过定制
NSStream 的子类(利用 Objective-C)才可以做到。由于 NSStream 对象可以很容易的被转换为 CFStream 对象,所以你创建的 NSStream 子类可以被用在任何需要 CFStream 的地方。任何有关 NSStream 所
属类的信息,请参考Cocoa 流编程指南。
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
CFNetwork API 概念
如果要理解 CFNetwork 框架,你必须首先对组成这个框架的每个部分都要熟悉。CFNetwork 框架被划分为独立的 API,每个 API 都负责个特定的网络协议。可以把这些 API 结合在起使用,也可以单独使
用,这取决于你的应用需求。在不同的 API 之间,大部分程序开发惯例都是类似的,所以理解每个 API 是非常重要的。
本节内容:
CFFTP API
CFHTTP API
CFHTTP认证 API
CFHost API
CFNetServices API
CFNetDiagnostics API
CFFTP API
与一个 FTP 服务器之间的通讯过程如果利用 CFFTP 就会变得简单些。通过 CFFTP API,你可以创建 FTP 读操作流(用于下载)以及 FTP 写操作流(用于上载)。利用 FTP 读操作和写操作流你可以完成下列功
能:
▪ 从一个 FTP 服务器下载个文件
▪ 上载个文件到个 FTP 服务器
▪ 从 FTP 服务器上下载个目录
▪ 在一个 FTP 服务器上创建目录
FTP 操作流与其他任何 CFNetwork 操作流的工作方式都很类似。比如,你可以通过调用函数 CFReadStreamCreateWithFTPURL 创建个 FTP 读操作流,然后可以在任何时候调用函数
CFReadStreamGetError 检查这个操作流的状态。
通过设置 FTP 操作流的各种属性,你可以把这个流用于各种应用当中。例如,如果操作流连接到的服务器需要输入个用户名和密码,你需要设置适当的属性,使操作流可以正常的工作。更多有关 FTP 操作流
的各种可见属性,可以参考"对操作流进行设置"。
CFFTP 操作流可以用于同步模式或者异步模式。为了打开在 FTP 读操作流创建的时刻就指定的与 FTP 服务器的连接,需要调用函数 CFReadStreamOpen。如果要从该流读取数据,使用 CFReadStreamRead
函数,这个要用到读操作流的引用 CFReadStreamRef,该引用是 FTP 读操作流创建时候的返回值。CFReadStreamRead 函数可以把 FTP 服务器的输出填充到个缓冲区中。
有关使用 CFFTP API 的更多内容,请参考"操作 FTP 服务器"。
CFHTTP API
如果需要发送和接收 HTTP 消息,可以使用 CFHTTP API。就像 CFFTP 是 FTP 协议的抽象样,CFHTTP 也是 HTTP 协议的个抽象。
超文本传输协议 (HTTP) 是在客户端和服务器之间的种请求/相应协议。客户端创建个请求消息后,这个会被串行化,也就是说这个消息会被转换成原始的字节流(消息在串行化以前是不能被传输的)接着
请求消息被发送到服务器端。请求消息通常会要求访问个文件,比如个网页。服务器应答、发送回来个字符串接下来就是个消息。必要的时候,这个过程会重复多次。
创建个 HTTP 请求消息,需要指定如下参数:
▪ 请求方法。它可以是超文本传输协议定义的多个请求方法之,比如 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE 和 CONNECT
▪URL。比如 http://www.apple.com
▪HTTP 的版本。比如版本 1.0 或者 1.1
▪ 消息头。指定消息头名称,比如 User-Agent,以及它的值比如 MyUserAgent
▪ 消息体
消息被创建之后,你就必须将它串行化。个请求消息在串行化之后应该看起来是这个样子:
GET / HTTP/1.0\r\nUser-Agent: UserAgent\r\nContent-Length: 0\r\n\r\n
反串行化是串行化的相反操作。在反串行化之后,从客户端或者服务器端接收到的原始字节流就会被恢复成它的本地表现形式。CFNetwork 提供了从接收到的串行化消息中获取如下信息所需要的所有函数:消
息类型(请求还是应答)、HTTP 版本、URL、消息头和消息体。
CFHTTP 的更多使用范例在"与 HTTP 服务器通讯"中可以找到。
CFHTTPAuthentication API
如果你在向一个认证服务器发送的 HTTP 请求中没有包含证书(或者带有无效证书),服务器就会返回个挑战认证要求(就是大家通常知道的 401 或者 407 应答)。CFHTTPAuthentication API 会把认证证书传
递给 HTTP 挑战认证消息。CFHTTPAuthentication 支持下面几种认证方案:
▪ 基本
▪ 摘要
▪NT LAN 管理器 (NTLM)
▪ 简单保护 GSS-API 协商机制 (SPNEGO)
Mac OS X v10.4 版本中新增加的一个功能就是在不同请求之间的持久性。在 Mac OS X v10.3 版本中,每当一个请求被挑战的时候,你就必须重新开始个认证对话。而现在你可以为每个服务器维护组
CFHTTPAuthentication 对象。当你接到个 401 或者 407 应答时,就找到适用于当前服务器的的对象和证书并加以使用即可。CFNetwork 会利用对象中存储的信息尽快的对请求进行处理。
通过不同请求之间的持久性,新版本的 CFHTTPAuthentication 的性能得到大大提高。更多有关如何使用 CFHTTPAuthentication 的内容可以参考"与认证 HTTP 服务器通讯"。
CFHost API
利用 CFHost API 可以获取主机信息,包括主机名、地址以及可达性等信息。这种获取有关主机的信息的过程叫做解析。
CFHost 的用法基本类似于 CFStream:
▪ 创建个 CFHost 对象。
▪ 开始解析 CFHost 对象。
▪ 获取地址、主机名称或者可达性等信息。
▪ 在完成所有任务之后销毁 CFHost 对象。
就像所有 CFNetwork 类一样,CFHost 同时兼容于 IPv4 和 IPv6。利用 CFHost,你写的代码可以完全透明的处理 IPv4 和 IPv6 。
CFHost 与 CFNetwork 的其他部分结合的非常紧密。比如,有个 CFStream 的函数叫做 CFStreamCreatePairWithSocketToCFHost ,它可以直接利用 CFHost 对象创建 CFStream 对象。更多有关
CFHost 对象函数的内容,请参考 CFHost 参考。
CFNetServices API
如果你希望在自己的应用中利用 Bonjour 来注册一个服务或者发现些服务,那么可以使用 CFNetServices API。Bonjour 是 Apple 的一个“零配置网络(ZEROCONF)”的实现,它可以帮助你发布、发现以及解
析那些网络服务。
为了实现 Bonjour ,CFNetServices API 中定义了三个对象类型:CFNetService、CFNetServiceBrowser 和 CFNetServiceMonitor。一个 CFNetService 对象表示个单独的网络服务,比如个打印机或者
个文件服务器。它包含了另个计算机识别这个服务器所需要的所有信息,比如名称、类型、域和端口号。CFNetServiceBrowser 对象用来在域中搜索域以及网络服务。CFNetServiceMonitor 对象则被用来
监控 CFNetService 对象的变化,比如在 iChat 中的状态消息。
有关 Bonjour 的完整描述,请参考 Bonjour 概述。有关使用 CFNetServices 和实现 Bonjour 的更多内容,可以参考 NSNetServices 和 CFNetServices 编程指南。
CFNetDiagnostics API
连接到网络的应用程序都需要个稳定的连接。如果网络中断,那么应用程序就会出现问题。利用 CFNetDiagnostics API,用户可以自己诊断网络的下面这些问题:
▪ 物理连接错误(比如网线被拔掉)
▪ 网络错误(比如 DNS 或者 DHCP 服务器不再相应)
▪ 配置错误(比如代理设置错误)
一旦诊断到网络错误,CFNetDiagnostics 就会指导用户修复问题所在。你可能在 Safari 不能连接到个网站的时候真正见到过 CFNetDiagnostics。CFNetDiagnostics 辅助界面如图 1-3。
图 1-3!!网络诊断辅助
通过向 CFNetDiagnostics 提供有关网络错误的各种信息,你可以调用函数 CFNetDiagnosticDiagnoseProblemInteractively 引导用户通过提示信息找到个解决方案。另外,你还可以利用
CFNetDiagnostics 查询网络的连接状态,并且向用户提供统的错误信息。
如果想知道如何将 CFNetDiagnotics 集成到自己的应用程序当中,可以阅读"使用网络诊断功能"。CFNetDiagnostics 是 Mac OS X v10.4 版本中新出现的 A
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
与流相关的操作
本章讨论如何创建、打开流,以及如何检查读写流过程中发生的错误。另外还描述了如何从个读操作流当中读取数据、如何向个写操作流当中写入数据、如何在读写操作流的时候防止死锁以及如何通过个
代理服务器获取流。
本章内容:
与读操作流相关的操作
与写操作流相关的操作
处理操作流的时候防止死锁
利用防火墙
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
处理读操作流
我们从创建个读操作流开始。列表 2-1 为个文件创建了读操作流。
列表 2-1!!为文件创建读操作流
CFReadStreamRef myReadStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL);
在这行代码中,kCFAllocatorDefault 参数指定了当前缺省的系统定位符,它用于定位操作流的内存地址,fileURL 指定了创建的读操作流所对应的文件名称,比如file:///Users/joeuser/
Downloads/MyApp.sit。
一旦操作流被创建,它就可以被打开。打开个操作流会导致这个流占用它所需要的任何系统资源,比如用于打开文件的文件描述符。列表 2-2 中的示例代码说明了如何打开读操作流。
列表 2-2!!打开一个读操作流
if (!CFReadStreamOpen(myReadStream)) {
CFStreamError myErr = CFReadStreamGetError(myReadStream);
// An error has occurred.
if (myErr.domain == kCFStreamErrorDomainPOSIX) {
// Interpret myErr.error as a UNIX errno.
} else if (myErr.domain == kCFStreamErrorDomainMacOSStatus) {
// Interpret myErr.error as a MacOS error code.
OSStatus macError = (OSStatus)myErr.error;
// Check other error domains.
}
}
如果操作流打开成功,那么 CFReadStreamOpen 函数返回 TRUE ,如果因为某种原因打开失败就会返回 FALSE 。如果 CFReadStreamOpen 返回 FALSE,示例程序调用了CFReadStreamGetError 函数,它
返回了一个 CFStreamError 类型的结构,其中包含两个值:个域代码和个错误代码。域代码决定了错误代码将被怎样解释。比如,如果域代码是 kCFStreamErrorDomainPOSIX,错误代码是个 UNIX
errno 值。其他的错误域比如是 kCFStreamErrorDomainMacOSStatus,这说明错误代码是MacErrors.h 中定义的个 OSStatus 值,如果是 kCFStreamErrorDomainHTTP,这说明错误代码是枚举对象
CFStreamErrorHTTP 中定义的个值。
打开一个操作流可能会耗费比较长的时间,因此 CFReadStreamOpen 和 CFWriteStreamOpen 两个函数都不会被阻塞,它们返回 TRUE 表示操作流的打开过程已经开始。如果想要检查打开过程的状态,可以
调用函数 CFReadStreamGetStatus 和 CFWriteStreamGetStatus,如果返回 kCFStreamStatusOpening 说明打开过程仍然在进行中,如果返回 kCFStreamStatusOpen 说明打开过程已经完成,而返回
kCFStreamStatusErrorOccurred 说明打开过程已经完成,但是失败了。大部分情况下,打开过程是否完成并不重要,因为 CFStream 中负责读写操作的函数在操作流打开以前会被阻塞。
想要从读操作流中读取数据的话,需要调用函数 CFReadStreamRead,它类似于 UNIX 的 read() 系统调用。二者的相同之处包括:都需要缓冲区和缓冲区大小作为参数,都会返回读取的字节数,如果到了文
件末尾会返回 0 ,如果遇到错误就会返回 -1。另外,二者都会在至少一个字节可以被读取之前被阻塞,并且如果没有遇到阻塞的情况下都会继续读取。列表 2-3 是从读操作流中获取数据的示例程序。
列表 2-3!!从读操作流中获取数据 (阻塞)
CFIndex numBytesRead;
do {
UInt8 buf[kReadBufSize];
numBytesRead = CFReadStreamRead(myReadStream, buf, sizeof(buf));
if( numBytesRead > 0 ) {
handleBytes(buf, numBytesRead);
} else if( numBytesRead < 0 ) {
CFStreamError error = CFReadStreamGetError(myReadStream);
reportError(error);
}
} while( numBytesRead > 0 );
当所有数据都被读取之后,你应该调用 CFReadStreamClose 函数关闭操作流,这样可以释放与它相关的系统资源。接着通过调用函数 CFRelease 释放操作流的引用对象。你也可以通过把引用对象设置为
NULL 使它无效。请参考列表 2-4 中的示例说明。
列表 2-4!!释放个读操作流
CFReadStreamClose(myReadStream);
CFRelease(myReadStream);
myReadStream = NULL;
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
处理写操作流
处理写操作流的方式与处理读操作流的方式非常类似。其中个主要的区别就是,函数 CFWriteStreamWrite 不会保证接受你所传递给它的所有数据。相反,CFWriteStreamWrite 会返回它所接受的字节
数。你会留意到在列表 2-5 中,如果写入的字节数与需要写入的总字节数不相同的话,缓冲区会根据这个情况进行调整。
列表 2-5!!创建、打开、写入和释放个写操作流
CFWriteStreamRef myWriteStream =
CFWriteStreamCreateWithFile(kCFAllocatorDefault, fileURL);
if (!CFWriteStreamOpen(myWriteStream)) {
CFStreamError myErr = CFWriteStreamGetError(myWriteStream);
// An error has occurred.
if (myErr.domain == kCFStreamErrorDomainPOSIX) {
// Interpret myErr.error as a UNIX errno.
} else if (myErr.domain == kCFStreamErrorDomainMacOSStatus) {
// Interpret myErr.error as a MacOS error code.
OSStatus macError = (OSStatus)myErr.error;
// Check other error domains.
}
}
UInt8 buf[] = “Hello, world”;
UInt32 bufLen = strlen(buf);
while (!done) {
CFTypeRef bytesWritten = CFWriteStreamWrite(myWriteStream, buf, strlen(buf));
if (bytesWritten < 0) {
CFStreamError error = CFWriteStreamGetError(myWriteStream);
reportError(error);
} else if (bytesWritten == 0) {
if (CFWriteStreamGetStatus(myWriteStream) == kCFStreamStatusAtEnd) {
done = TRUE;
}
} else if (bytesWritten != strlen(buf)) {
// Determine how much has been written and adjust the buffer
bufLen = bufLen - bytesWritten;
memmove(buf, buf + bytesWritten, bufLen);
// Figure out what went wrong with the write stream
CFStreamError error = CFWriteStreamGetError(myWriteStream);
reportError(error);
}
}
CFWriteStreamClose(myWriteStream);
CFRelease(myWriteStream);
myWriteStream = NULL;
CFWriteStreamRef myWriteStream =
CFWriteStreamCreateWithFile(kCFAllocatorDefault, fileURL);
if (!CFWriteStreamOpen(myWriteStream)) {
CFStreamError myErr = CFWriteStreamGetError(myWriteStream);
// An error has occurred.
if (myErr.domain == kCFStreamErrorDomainPOSIX) {
// Interpret myErr.error as a UNIX errno.
} else if (myErr.domain == kCFStreamErrorDomainMacOSStatus) {
// Interpret myErr.error as a MacOS error code.
OSStatus macError = (OSStatus)myErr.error;
// Check other error domains.
}
}
UInt8 buf[] = “Hello, world”;
UInt32 bufLen = strlen(buf);
while (!done) {
CFTypeRef bytesWritten = CFWriteStreamWrite(myWriteStream, buf, strlen(buf));
if (bytesWritten < 0) {
CFStreamError error = CFWriteStreamGetError(myWriteStream);
reportError(error);
} else if (bytesWritten == 0) {
if (CFWriteStreamGetStatus(myWriteStream) == kCFStreamStatusAtEnd) {
done = TRUE;
}
} else if (bytesWritten != strlen(buf)) {
// Determine how much has been written and adjust the buffer
bufLen = bufLen - bytesWritten;
memmove(buf, buf + bytesWritten, bufLen);
// Figure out what went wrong with the write stream
CFStreamError error = CFWriteStreamGetError(myWriteStream);
reportError(error);
}
}
CFWriteStreamClose(myWriteStream);
CFRelease(myWriteStream);
myWriteStream = NULL;
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
处理操作流的时候防止阻塞
当利用流进行通讯的时候,常常会发生数据传输耗费大量时间,尤其是进行基于 socket 的流操作时。如果流操作是同步方式,那么整个应用都不得不停下来等待数据传输完成。因此,强烈建议您的代码中利用
其它方法防止阻塞。
在读写 CFStream 对象的时候,有两种方法防止阻塞:
▪ 轮询 — 在进行读操作的时候,在从流中读取数据以前检查是否有字节可读。在进行写操作的时候,在写入流之前检查数据是否可以无阻塞的写入。
▪ 利用一个循环 — 注册接收一个与流相关的事件,并把流放入一个循环当中。当与流相关的事件发生时,你的回调函数(由注册函数指定) 就会被调用。
下面几节将介绍这些方法。
本节内容:
利用轮询防止阻塞
利用循环防止阻塞
利用轮询防止阻塞
轮询的时候,需要确定读写操作流的状态是否就绪。在写入个写操作流的时候,这是通过调用函数 CFWriteStreamCanAcceptBytes 来实现的。如果返回 TRUE,那么你就可以利用 CFWriteStreamWrite
函数,因为它可以马上进行写入操作而不会被阻塞。类似的,对于个读操作流,在调用 CFReadStreamRead 函数之前,可以调用函数CFReadStreamHasBytesAvailable。通过这种流轮询方式,你可以避
免为了等待操作流就绪而阻塞整个线程。
列表 2-6 是针对读操作流的个轮询范例。
列表 2-6!!轮询个读操作流
while (!done) {
if (CFReadStreamHasBytesAvailable(myReadStream)) {
UInt8 buf[BUFSIZE];
CFIndex bytesRead = CFReadStreamRead(myReadStream, buf, BUFSIZE);
if (bytesRead < 0) {
CFStreamError error = CFReadStreamGetError(myReadStream);
reportError(error);
} else if (bytesRead == 0) {
if (CFReadStreamGetStatus(myReadStream) == kCFStreamStatusAtEnd) {
done = TRUE;
}
} else {
handleBytes(buf, bytesRead);
}
} else {
// ...do something else while you wait...
}
}
剩余20页未读,继续阅读
qi_ruihua
- 粉丝: 71
- 资源: 57
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
- 1
- 2
前往页