cfsocket使用有哪组建团队有几个步骤骤

socket 简单使用
关于socket的文章非常多,别人写 的也很清楚,我敲这个demo的时候,也是看着别人的博客一步一步敲的,这样才能发现其中的问题。在这里socket的原理、TCP、http协议就不做介绍了。下面的代码只是实现简单的客户端和服务端的通信,服务端给指定客户端发送消息。当然也可以实现客户端给客户端发送消息,想给谁发送,在消息里加上一个标志,服务端收到时,再简单解析一下就可以了。
因为写博客的时候和学习socket的时候隔的时间有点长,所以参考的文章不知道是哪篇了,就不关联了。
注:代码里面的控件全是在storyboard拖拽出来的
使用socket首先导入这几个头文件
#import &sys/socket.h&
#import &netinet/in.h&
#import &arpa/inet.h&
#import &unistd.h&
#import &netinet/tcp.h&
创建一个静态变量
static ClientViewController *selfClass =nil;//在c函数里可使用其调自身方法或属性
- (void)viewDidLoad {
viewDidLoad];
selfClass=self;
creatConnect];
-(void)creatConnect{
CFSocketContext sockContext={0,NULL,
NULL,NULL,NULL};
创建socket
_socket=CFSocketCreate(kCFAllocatorDefault,//为对象分配内存
PF_INET,//协议族,0或负数
默认为 PF_INET
SOCK_STREAM,//套接字类型,协议族为PF_INET
IPPROTO_TCP,//套接字协议
kCFSocketConnectCallBack,//触发回调消息类型
TCPServerConnectCallBack ,//回调函数
&sockContext//一个持有CFSocket结构信息的对象,可以为nil
if (_socket!=nil) {
配置服务器地址
sockaddr_in addr4;
memset(&addr4,
0, sizeof(addr4));
addr4.sin_len=sizeof(addr4);
addr4.sin_family=AF_INET;//协议族
addr4.sin_port=htons(8888);//端口号
addr4.sin_addr.s_addr=inet_addr([@"服务端IP地址"
UTF8String]); //
把字符串的地址转换为机器可识别的网络地址
CFDataRef address=CFDataCreate(kCFAllocatorDefault, (UInt8
*)&addr4 , sizeof(addr4));
//绑定socket
CFSocketConnectToAddress(_socket, address, -1);//
连接超时时间,如果为负,则不尝试连接,而是把连接放在后台进行,如果_socket消息类型为kCFSocketConnectCallBack,将会在连接成功或失败的时候在后台触发回调函数
CFRunLoopRef cRunRef=CFRunLoopGetCurrent();
CFRunLoopSourceRef sourceRef=CFSocketCreateRunLoopSource(kCFAllocatorDefault,
CFRunLoopAddSource(cRunRef, sourceRef,
kCFRunLoopCommonModes);
释放sourceRef
CFRelease(sourceRef);
socket 回调函数,函数格式可在socket创建函数里查看
(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
static void TCPServerConnectCallBack(CFSocketRef s,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info){
NSLog(@"%@",info);
if (data!=NULL && type ==kCFSocketConnectCallBack) {
NSLog(@"连接失败");
//使用异步线程监听有没有收到数据
detachNewThreadSelector:@selector(readMessage)
toTarget:selfClass
withObject:nil];
- (IBAction)send:(id)sender {
sendMessage];
[self.view
endEditing:YES];
-(void)readMessage{
监听收到的数据
char buf[2048];
NSString *logS
// 接收数据
ssize_t recvLen =
recv(CFSocketGetNative(_socket), buf,
sizeof(buf), 0);
if (recvLen &
logStr = [NSString
stringWithFormat:@"%@\n", [NSString
stringWithFormat:@"%s", buf]];
回到主线程刷新UI
performSelectorOnMainThread:@selector(showMessage:)
withObject:logStr
waitUntilDone:YES];
} while (strcmp(buf,
"exit") != 0);
// 发送数据
- (void)sendMessage {
NSString*stringTosend =
self.textF.text;
char *data = [stringTosend
UTF8String];
send(CFSocketGetNative(_socket), data,
strlen(data) + 1,
-(void)showMessage:(NSString *)message{
self.receivedLabl.text=
@property (weak,
nonatomic) IBOutlet
UITextField *textF;
//消息输入框
@property (weak,
nonatomic) IBOutlet
UILabel *receLa;//消息展示
@property (weak,
nonatomic) IBOutlet
UILabel *//客户端IP地址显示
@property (weak,
nonatomic) IBOutlet
UITextField *clientTf;//指定第几个连接的客户端
ServiceViewController.m
socket_Test
Created by Chu on 17/8/4.
Copyright (C) 2017年 Chu. All rights reserved.
#import "ServiceViewController.h"
#import &sys/socket.h&
#import &netinet/in.h&
#import &arpa/inet.h&
#import &unistd.h&
#import &netinet/tcp.h&
@interface
ServiceViewController ()
static CFWriteStreamRef outputS
static ServiceViewController *selfClass=nil;
@implementation ServiceViewController
CFSocketRef _
NSMutableArray *addressA
- (void)viewDidLoad {
viewDidLoad];
// Do any additional setup after loading the view.
selfClass=self;
addressArr=[NSMutableArray
安装时我的真机不申请网络,很无奈加了一句这
dataWithContentsOfURL:[NSURL
URLWithString:@"https://image.baidu.com/search/detail?ct=&z=0&ipn=false&word=风景电脑壁纸&step_word=&hs=0&pn=0&spn=0&di=&pi=0&rn=1&tn=baiduimagedetail&is=0%2C0&istype=0&ie=utf-8&oe=utf-8&in=&cl=2&lm=-1&st=-1&cs=%2C&os=%2C&simid=%2C&adpicid=0&lpn=0&ln=3994&fr=&fmq=1_R&fm=rs3&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&ist=&jit=&cg=wallpaper&bdtype=0&oriquery=风景&objurl=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2Fe%2F53ec732e6142b.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Botg9aaa_z%26e3Bv54AzdH3Fowssrwrj6_kt2_cd9dm_8_z%26e3Bip4s&gsm=0&rpstart=0&rpnum=0"]];
outputStream=NULL;
creatConnect];
if ([self creatConnect]) {
CFRunLoopRun();
-(BOOL)creatConnect{
_socket=CFSocketCreate(kCFAllocatorDefault,
PF_INET, SOCK_STREAM,
IPPROTO_TCP, kCFSocketAcceptCallBack,
TCPServerAcceptCallBack,
if (_socket==NULL) {
NSLog(@"cannot creat socket");
int optval=1;
setsockopt(CFSocketGetNative(_socket),
SOL_SOCKET, SO_REUSEADDR, (void *)&optval,
sizeof(optval));
sockaddr_in addr4;
memset(&addr4,
0, sizeof(addr4));
addr4.sin_len=sizeof(addr4);
addr4.sin_family=PF_INET;
addr4.sin_port=htons(8888);
addr4.sin_addr.s_addr=htonl(INADDR_ANY);
CFDataRef address=CFDataCreate(kCFAllocatorDefault, (UInt8*)&addr4,
sizeof(addr4));
if (CFSocketSetAddress(_socket, address) !=
kCFSocketSuccess) {
NSLog(@"Bind to address failed!");
if (_socket) {
CFRelease(_socket);
_socket=NULL;
CFRunLoopRef cRunLoop=CFRunLoopGetCurrent();
CFRunLoopSourceRef source=CFSocketCreateRunLoopSource(kCFAllocatorDefault,
CFRunLoopAddSource(cRunLoop, source,
kCFRunLoopCommonModes);
CFRelease(source);
static void TCPServerAcceptCallBack(CFSocketRef socket,CFSocketCallBackType
type,CFDataRef address,const
void *data,void *info){
if (kCFSocketAcceptCallBack == type) {
CFSocketNativeHandle nativeSocketHandle=*(CFSocketNativeHandle *)
uint8_t name[SOCK_MAXADDRLEN];
socklen_t nameLen=sizeof(name);
if (getpeername(nativeSocketHandle, (struct
sockaddr*)name, &nameLen)) {
NSLog(@"error");
NSLog(@"%s connected",inet_ntoa(((struct
sockaddr_in*)name)-&sin_addr));
CFReadStreamRef readS
CFWriteStreamRef writeS
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
if (readStream && writeStream) {
[selfClass-&addressArr
addObject:(__bridge
id _Nonnull)(writeStream)];
NSString *addre=[NSString
stringWithUTF8String:inet_ntoa(((struct
sockaddr_in*)name)-&sin_addr)];
selfClass.listlabl.text=[selfClass.listlabl.text
stringByAppendingString:addre];
CFStreamClientContext streamContext={0,(__bridge
void*)(addre),NULL,NULL};
if (!CFReadStreamSetClient(readStream,
kCFStreamEventHasBytesAvailable,
readStreamFunc, &streamContext)) {
if (!CFWriteStreamSetClient(writeStream,
kCFStreamEventCanAcceptBytes,
writeStreamFunc, &streamContext)) {
CFReadStreamScheduleWithRunLoop(readStream,
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes);
CFWriteStreamScheduleWithRunLoop(writeStream,
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes);
CFReadStreamOpen(readStream);
CFWriteStreamOpen(writeStream);
close(nativeSocketHandle);
void readStreamFunc(CFReadStreamRef stream,
CFStreamEventType type,
void *clientCallBackInfo){
UInt8 buff[255];
CFReadStreamRead(stream, buff,
printf("received %s",buff);
NSString *str=[NSString
stringWithCString:(char *)buff
encoding:NSUTF8StringEncoding];
[selfClass
showReciveData:str];
void writeStreamFunc(CFWriteStreamRef stream,
CFStreamEventType type,
void *clientCallBackInfo){
-(void)sendStream{
int index=[self.clientTf.text
intValue];
UInt8 buff[1024];
memcmp(buff, [self.textF.text
UTF8String], sizeof(buff));
给指定客户端发送消息
outputStream=(__bridge
CFWriteStreamRef)(addressArr[index]);
CFWriteStreamWrite(outputStream, (UInt8
*)self.textF.text.UTF8String,
strlen(_textF.text.UTF8String)+1
[self.view
endEditing:YES];
-(void)showReciveData:(NSString *)str{
self.receLa.text=
- (IBAction)send:(id)sender {
sendStream];
没有更多推荐了,CFNetwork&编程指南(与流的相关操作)
  处理读操作流
  我们从创建一个读操作流开始。列表 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.
  // 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 numBytesR
  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.
  // 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
  bufLen = bufLen - bytesW
  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,
  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...
  列表 2-7 是一个写操作流的轮询范例。
  列表 2-7 轮询一个写操作流
  UInt8 buf[] = “Hello, world”;
  UInt32 bufLen = strlen(buf);
  while (!done) {
  if (CFWriteStreamCanAcceptBytes(myWriteStream)) {
  int 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
  bufLen = bufLen - bytesW
  memmove(buf, buf + bytesWritten, bufLen);
  // Figure out what went wrong with the write stream
  CFStreamError error = CFWriteStreamGetError(myWriteStream);
  reportError(error);
  } else {
  // ...do something else while you wait...
  利用循环防止阻塞
  线程的循环会监控某些事件的发生。当这些事件发生的时候,循环会调用某个特定的函数。循环会一直监测它的输入源的事件。在进行网络传输的时候,当你注册的事件发生时,你的回调函数会被循环所执行。这使你不必轮询你的
socket 操作流,这种轮询会降低线程执行效率。如果对循环不是很熟悉,可以阅读循环中的内容。
  本例以创建一个 socket 读操作流开始:
  CFStreamCreatePairWithSocketToCFHost(kCFAllocatorDefault, host,
  &myReadStream, NULL);
  其中 CFHost 引用对象 host 确定了读操作流指向的远程主机,port
参数确定了这个主机所使用的端口号码。CFStreamCreatePairWithSocketToCFHost
函数返回了新的读操作流引用 myReadStream。最后一个参数 NULL
确定调用者不想创建一个写操作流。如果你想要创建一个写操作流,那么最后一个参数应该是,比如
&myWriteStream 这样的形式。
  在打开 socket 读操作流之前,需要创建一个上下文对象,这个对象在你注册接收流相关的事件时会用到:
  CFStreamClientContext myContext = {0, myPtr, myRetain, myRelease,
myCopyDesc};
  第一个参数为 0 表示版本号码。info 参数,也就是 myPtr,它指向你想要传递给回调函数的数据的指针。通常情况下,myPtr
是一个指向你所定义的数据结构的指针,这个结构中包含了与这个流相关的指针。retain 参数指向一个能够保存 info
参数的函数,因此如果你象上面的代码那样将这个参数设置为 myRetain, CFStream 会调用 myRetain(myPtr)
函数来保留 info 指针。类似的,release 参数myRelease 指向一个释放 info
参数的函数。当操作流与上下文分离开时,CFStream 可以调用
myRelease(myPtr)。最后,copyDescription
参数指向一个函数,这个函数能够提供对操作流的描述。比如,如果你需要利用上面的操作流客户端上下文调用CFCopyDesc(myReadStream)
函数,CFStream 就会调用 myCopyDesc(myPtr)。
  客户端上下文还允许你将 retain、release 和 copyDescription 等参数设置为 NULL。如果你将
retain 和 release 参数设置为NULL,那么系统就会认为你会一直保存 info 指针直到操作流本身被销毁。如果你设置
copyDescription 参数为 NULL,那么如果需要的话,系统将会提供一个 info
指针指向的内存的基本描述信息。
  在客户端上下文设置好之后,可以调用函数 CFReadStreamSetClient
登记接收与操作流相关的事件。CFReadStreamSetClient 要求你指定一个回调函数,以及想要接收的事件。下面列表 2-8
的范例中,回调函数需要接收
kCFStreamEventHasBytesAvailable,kCFStreamEventErrorOccurred 以及
kCFStreamEventEndEncountered
事件。然后利用函数CFReadStreamScheduleWithRunLoop 在循环中调度这个操作流。列表 2-8
中的范例说明了如何进行这个操作。
  列表 2-8 在循环中调度一个操作流
  CFOptionsFlags registeredEvents = kCFStreamEventHasBytesAvailable
  kCFStreamEventErrorOccurred | kCFStreamEventEndEncount
  if (CFReadStreamSetClient(myReadStream, registeredEvents,
myCallBack, &myContext)
  CFReadStreamScheduleWithRunLoop(myReadStream,
CFRunLoopGetCurrent(),
  kCFRunLoopCommonModes);
  当已经在循环中调度了操作流之后,你就可以象列表 2-9 中那样打开操作流了。
  列表 2-9 打开一个非阻塞的读操作流
  if (!CFReadStreamOpen(myReadStream)) {
  CFStreamError myErr = CFReadStreamGetError(myReadStream);
  if (myErr.error != 0) {
  // An error has occurred.
  if (myErr.domain == kCFStreamErrorDomainPOSIX) {
  // Interpret myErr.error as a UNIX errno.
  strerror(myErr.error);
  } else if (myErr.domain == kCFStreamErrorDomainMacOSStatus)
  OSStatus macError = (OSStatus)myErr.
  // Check other domains.
  } else
  // start the run loop
  CFRunLoopRun();
  现在,只需要等待回调函数被执行到了。在你的回调函数中,要检查事件代码并采取相应的措施。参见列表 2-10。
  列表 2-10 网络事件回调函数
  void myCallBack (CFReadStreamRef stream, CFStreamEventType event,
void *myPtr) {
  switch(event) {
  case kCFStreamEventHasBytesAvailable:
  // It is safe to call CFReadStreamR it won’t block because
  // are available.
  UInt8 buf[BUFSIZE];
  CFIndex bytesRead = CFReadStreamRead(stream, buf, BUFSIZE);
  if (bytesRead & 0) {
  handleBytes(buf, bytesRead);
  // It is safe to ignore a value of bytesRead that is less than
  // equal to zero because these cases will generate other
  case kCFStreamEventErrorOccurred:
  CFStreamError error = CFReadStreamGetError(stream);
  reportError(error);
  CFReadStreamClose(stream);
  CFRelease(stream);
  case kCFStreamEventEndEncountered:
  reportCompletion();
  CFReadStreamClose(stream);
  CFRelease(stream);
  当回调函数接收到 kCFStreamEventHasBytesAvailable 事件代码时,它会调用
CFReadStreamRead 读取数据。
  当回调函数接收到 kCFStreamEventErrorOccurred 事件代码时,它会调用
CFReadStreamGetError 来获取错误信息以及它自己的错误处理函数 (reportError)
来处理这个错误.
  当回调函数接收到 kCFStreamEventEndEncountered
事件代码时,它会调用它自己的函数(reportCompletion) 处理数据结尾,然后调用函数 CFReadStreamClose
关闭操作流和 CFRelease 函数释放操作流引用。调用 CFReadStreamClose
会导致操作流被取消调度,因此回调函数不需要从循环中删除操作流。
  利用防火墙
  有两种方式设置操作流的防火墙。对大多数流来说,可以通过 SCDynamicStoreCopyProxies
函数获取代理设置,然后通过设置kCFStreamHTTPProxy (或者 kCFStreamFTPProxy)
参数将结果设置到操作流上。SCDynamicStoreCopyProxies 函数是系统配置框架的一部分,因此需要在项目中包含
&SystemConfiguration/SystemConfiguration.h&
,这样才能使用该函数。接着在使用之后把代理词典引用释放调。这个过程看起来类似列表 2-11 中的例子。
  列表 2-11 通过代理服务器浏览一个操作流
  CFDictionaryRef proxyDict =
SCDynamicStoreCopyProxies(NULL);
  CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy,
proxyDict);
  不过,如果你需要在多个操作流中使用代理设置的话,就会稍微复杂一点。这种情况下,获取用户机器的防火墙设置需要5个步骤:
  创建一个指向动态存储会话的单独持久性句柄,比如 SCDynamicStoreRef。
  把指向动态存储会话的句柄放入循环当中,使它能够得到代理变化的通知。
  利用 SCDynamicStoreCopyProxies 获取最新的代理设置。
  在得到变动通知后更新代理的拷贝。
  在使用完毕后清除 SCDynamicStoreRef 。
  可以使用函数 SCDynamicStoreCreate
来创建一个指向动态存储会话的句柄,并向它传递一个定位器、一个描述过程的名称、一个回调函数和一个动态存储上下文
SCDynamicStoreContext。在你的应用进行初始化的时候就会执行这个步骤。具体代码类似列表 2-12。
  列表 2-12 创建一个指向动态存储会话的句柄
  SCDynamicStoreContext context = {0, self, NULL, NULL,
  systemDynamicStore = SCDynamicStoreCreate(NULL,
  CFSTR(&SampleApp&),
  proxyHasChanged,
  &context);
  在创建了指向动态存储的引用之后,你需要把它加入循环当中。首先,对这个动态存储引用进行设置,使它能够监控代理的任何变化。这个可以通过函数
SCDynamicStoreKeyCreateProxies 和 SCDynamicStoreSetNotificationKeys
来实现。接着,你可以利用函数SCDynamicStoreCreateRunLoopSource 和
CFRunLoopAddSource 把动态存储引用加入到循环当中。你的代码应该类似列表 2-13。
  列表 2-13 把一个动态存储引用加入循环
  // Set up the store to monitor any changes to the proxies
  CFStringRef proxiesKey =
SCDynamicStoreKeyCreateProxies(NULL);
  CFArrayRef keyArray = CFArrayCreate(NULL,
  (const void **)(&proxiesKey),
  &kCFTypeArrayCallBacks);
  SCDynamicStoreSetNotificationKeys(systemDynamicStore, keyArray,
  CFRelease(keyArray);
  CFRelease(proxiesKey);
  // Add the dynamic store to the run loop
  CFRunLoopSourceRef storeRLSource =
  SCDynamicStoreCreateRunLoopSource(NULL, systemDynamicStore,
  CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLSource,
kCFRunLoopCommonModes);
  CFRelease(storeRLSource);
  一旦动态存储引用被加入了循环,可以利用它调用函数 SCDynamicStoreCopyProxies
加载代理词典的当前代理设置,如何做请参考列表2-14 。
  列表 2-14 加载代理词典
  gProxyDict = SCDynamicStoreCopyProxies(systemDynamicStore);
  一旦把动态存储引用加入了循环当中,每当代理发生变化,你的回调函数就会被调用。当前的代理词典会被释放掉,并利用新的代理设置重新载入这个代理词典。列表
2-15 中是一个回调函数的范例。
  列表 2-15 代理回调函数
  void proxyChanged() {
  CFRelease(gProxyDict);
  gProxyDict = SCDynamicStoreCopyProxies(systemDynamicStore);
  由于所有的代理信息都是最新的,这样就可以使用这些代理。在创建了您的读写操作流之后, 调用函数
CFReadStreamSetProperty 或者CFWriteStreamSetProperty 设置
kCFStreamPropertyHTTPProxy 代理。如果你的操作流是一个读操作流,名称为
readStream,那么你的函数调用将如列表 2-16 所示。
  列表 2-16 把代理信息加入一个操作流
  CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPProxy,
gProxyDict);
  在使用完毕代理设置信息之后,请确保释放了词典和动态存储引用,并将动态存储引用从循环中清除。请参考列表 2-17。
  列表 2-17 清理代理信息
  if (gProxyDict) {
  CFRelease(gProxyDict);
  // Invalidate the dynamic store's run loop
  // to get the store out of the run loop
  CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL,
systemDynamicStore, 0);
  CFRunLoopSourceInvalidate(rls);
  CFRelease(rls);
  CFRelease(systemDynmaicStore);
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

我要回帖

更多关于 程序生成有哪几个步骤 的文章

 

随机推荐