求助关于CFNetwork中CFSocketCallBackangularjs中回调函数数问题

今天看啥 热点:
iOS-----使用CFNetwork实现TCP协议的通信,ios-----cfnetwork使用CFNetwork实现TCP协议的通信&&&
TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个通信接口,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信.CFNetwork对基于TCP协议的网络通信提供了良好的封装,CFNetwork使用CFSocket来代表两端的通信接口,还可以通过CFStream读/写数据.
IP地址与端口号
IP地址用于唯一地标识网络中的一个通信实体,这个通信实体既可以是一台主机,也可以是一台打印机,或者是路由器的某一个端口.在基于IP协议的网络中传输的数据包,都必须使用IP地址来进行标识.
&& IP地址是数字型的,是一个32位(32Bit)整数,通常把它分成4个8位的二进制数,每8为之间用圆点隔开,每个8位整数可以转换成一个0-255的十进制整数,因此看到的IP地址常常是这样的形式:198.162.8.10.
&& NIC(Internet Network Information Center)统一负责全球Internet IP地址的规划、管理,而Inter NIC、APNIC、RIPE三大网络信息中心具体负责美国及其他地区的IP地址分配。其中APNIC负责亚太地区的IP管理,我国申请IP地址也要通过APNIC,APNIC的总部设在日本东京大学。
& IP地址被分成了A、B、C、D、E五类,每个类别的网络标识和主机标识各有规则。
IP地址用于唯一地标识网络上的一个通信实体,但一个通信实体可以有多个通信程序同时提供网络服务,此时还需要使用端口.
端口是一个16位的整数,用于标识数据交给那个通信程序处理.因此,端口就是应用程序与外界交流的出入口,它是一种抽象的软件结构,包括一些数据结构和I/O(基本输入/输出)缓冲区.
不同的应用程序处理不同端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口号可以从0到65535,通常将它分为3类.
TCP协议基础
IP协议是 Internet 上使用的一个关键协议,它是全称是 Internet Protocol, 即Internet协议,通常简称 IP 协议.通过使用 IP协议,使 Internet 成为一个允许连接不同类型的计算机和不同操作系统的网络.
要使两台计算机之间彼此进行通信,必须使两台计算机使用同一种&语言&,IP 协议只保证计算机能发送和接收分组数据. IP协议负责将消息从一个主机传送到另一个主机,消息在传送的过程中被分割成一个个小包.
安装 IP 协议之后,可保证计算机之间发送和接收数据,但 IP协议还不能解决数据分组在传输过程中可能出现的问题.因此,若要解决可能出现的问题,连接上 Internet 的计算机还需要安装 TCP 协议来提供可靠并且无差错的通信服务.
TCP协议被称作一种端对端协议.这是因为它为两台计算机之间的连接起到了重要作用-----当一台计算机需要与另一台远程计算机连接时, TCP 协议会让它们建立一个连接:用于发送和接收数据的虚拟链路.
TCP 协议负责收集这些信息包,并将其按适当的次序放好传送,在接收端收到后再将其正确地还原. TCP协议保证了数据包在传送中准备无误. TCP 协议使用重发机制:当一个通信实体发送一个消息给另一个通信实体后,需要收到另一个通信实体的确认信息,如果没有收到另一个通信实体的确认信息,则会再次重发刚才发送的信息.
通过这种重发机制, TCP协议向应用程序提供可靠的通信连接,使它能够自动适应网上的各种变化.即使在 Internet 暂时出现堵塞的情况下, TCP也能够保证通信的可靠性.
使用 CFSocket 实现 TCP服务器端
使用CFSocket建立服务器的步骤如下.
创建一个监听Socket Accept(Socket连接)的CFSocket,并为kCFSocketAcceptCallBack事件绑定回调函数.
调用CFSocketSetAddress()函数,将服务器端的CFSocket绑定到本地IP地址和端口
将CFSocket作为source添加到指定线程的CFRunLoop上,并运行该线程的CFRunLoop,从而保证该CFSocket能持续不断地接受来自客户端的连接.
1 #import &sys/socket.h&
3 #import &arpa/inet.h&
5 #import&Foundation/Foundation.h&
7 // 读取数据的回调函数
9 void readStream(CFReadStreamRef
iStream, CFStreamEventType eventType, void *clientCallBackInfo)
UInt 8 buff[2048];
CFIndex hasRead = CFReadStreamRead(iStream, buff, 2048);
if(hasRead & 0)
21 // 强制只处理hasRead前面的数据
23 buff[hasRead]=&\0&;
25 printf(&接收到数据: %s\n&, buff);
31 // 有客户端连接进来的回调函数
33 void TCPServerAcceptCallBack(CFSocketRef
socket, CFSocketCallBackType type, CFDataRef
address, const void *data, void *info )
// 如果有客户端Socket连接进来
if(kCFSocketAcceptCallBack == type)
43 // 获取本地Socket的Handle
45 CFSocketNativeHandle
nativeSocketHandle = *(CFSocketNativeHandle*)
47 uint8_t name[SOCk_MAXADDRLEN];
49 socklen_t
nameLen = sizeof(name);
51 // 获取对方Socket信息,还有getsocketname()函数则用于获取本程序所在Socket信息
53 if(getpeername(nativeSocketHandle, (struct sockaddr *)name, &nameLen) != 0)
NSLog(@&error&);
63 // 获取连接信息
65 struct sockeaddr_in * addr_in = (struct socketaddr_in*)
67 NSLog(@&%s: %d 连接进来了&,
inet_ntoa(addr_in-&sin_addr)
addr_in-&sin_port);
71 CFReadStreamRef
73 CFWriteStreamRef
75 // 创建一组可读/写的CFStream
77 CFStreamCreatePairWithSocket(kCFAllocatorDefault , nativeSocketHandle, &iStream, &oStream);
79 if(iStream && oStream)
// 打开输入流和输入流
CFReadStreamOpen(iStream);
CFWriteStreamOpen(oStream);
CFStreamClientContext streamContext = {0, NULL, NULL, NULL};
if(!CFReadStreamSetClient(iStream, kCFStreamEventHasBytesAvailable,
93 readStream/*回调函数,当有可读的数据时调用*/, &streamContext))
97 exit(1);
101 CFReadStreamScheduleWithRunLoop(iStream,
CFRunLoopGetCurrent(),
103 kC FRunLoopCommonModes);
105 const char* str = &您好,您收到Mac服务器的新年祝福!\n&;
107 // 向客户端输出数据
109 CFWriteStreamWrite(oStream, (UInt8 *)str, strlen(str) + 1);
117 int main(int argc, char * argv[])
@autoreleasepool{
// 创建Socket,指定TCPServerAcceptCallBack
// 作为kCFSocketAcceptCallBack事件的监听函数
CFSocketRef _socket = CFSocketCreate(kCFAllocatorDefault
129 , PF_INEF // 指定协议族,如果该参数为0或者负数,则默认为PF_INEF
131 // 指定Socket类型,如果协议族为PF_INEF,且该参数为0或负数
// 则它会默认为SOCK_STREAM,如果要使用UDP协议,则该参数指定为SOCK_DGRAM
135 , SOCK_STREAM
// 指定通信协议。如果前一个参数为SOCK_STREAM,则默认使用TCP协议
// 如果前一个参数为SOCK_DGRAM,则默认使用UDP协议
141 ,IPPROTO_TCP
143 // 该参数指定下一个回调函数所监听的事件类型
,kCFSocketAcceptCallBack
,TCPServerAcceptCallBack // 回调函数
if(_socket == NULL)
NSLog(@&创建Socket失败!&);
161 int optval = 1;
163 // 设置允许重用本地地址和端口
165 setsockopt(CFSocketGetNative(_socket), SOL_SOCKET,
SO_REUSEADDR,
167 (void *)&optval, sizeof(optval));
169 // 定义sockaddr_in类型的变量,该变量将作为CFSocket的地址
171 struct sockaddr_in addr4;
173 memset(&addr4, 0 , sizeof(addr4));
175 addr4.sin_len = sizeof(addr4);
177 addr4.sin_family = AF_INEF;
设置该服务器监听本机任意可用的IP地址
181 // addr4.sin_addr.s_addr =htonl(INADDR_ANY);
183 // 设置服务器监听地址
185 addr4.sin_addr.s_addr = inet_addr(&192.168.1.100&);
187 // 设置服务器监听端口
189 addr4.sin_port = htons(30000);
191 // 将IPv4的地址转换成CFDataRef
193 CFDataRef address = CFDataCreate(kCFAllocatorDefault
, (UInt8 *)&addr4, sizeof(addr4));
将CFSocket绑定到指定IP地址
199 if(CFSocketSetAddress(_socket, address) != kCFSocketSuccess)
NSLog(@&地址绑定失败!&);
// 如果_socket不为NULL,则释放_socket
if(_socket)
CFRelease(_socket);
217 _socket = NULL;
NSLog(@&---启动循环监听客户端连接-----&);
// 获取当前线程的CFRunLoop
225 CFRunLoopRef
cfRunLoop = CFRunLoopGetCurrent();
227 // 将_socket包装成CFRunLoopSource
229 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(
231 kCFAllocatorDefault , _socket, 0);
233 // 为CFRunLoop对象添加source
235 CFRunLoopAddSource(cfRunLoop,
kCFRunLoopCommonModes);
237 CFRelease(source);
运行当前线程的CFRunLoop
241 CFRunLoopRun();
&& 上面程序中的main()函数作为程序的入口,程序从该函数开始执行.main()函数的第1段红色字代码创建了一个CFSocket对象,并指定了该CFSocket使用TCP协议,基于流经行输入/输出.
sockaddr_in类型的结构体变量,该结构体变量将会作为CFSocket绑定的监听地址,因此程序为socketaddr_in类型的结构体变量指定了IP地址和端口,然后程序中的红色字代码调用了CFSocketSetAddress()函数将指定CFSocket绑定到指定的IP地址和端口.
main()函数的最后一段红色体代码将该CFSocket作为source添加到主线程的CFRunLoop上,并运行主线程的CFRunLoop,从而保证该CFSocket能持续不断地接受来自客户端的连接.
& 该程序的另一个重点是TCPServerAcceptCallBack回调函数---当CFSocket接受来自客户端的连接后,该函数将会被调用.该函数主要做了如下4件事情.
调用getpeername()函数获取远程Socket的相关信息,从而在控制台打印出来连接进来的客户端IP地址和端口.
调用CFStreamCreatePairWithSocket()函数通过CFSocket获取CFReadStreamRef、CFWriterStreamRef,接下来程序即可通过这两个流进行读/写网络数据。
再次使用主线程的CFRunLoop监听来自客户端的数据。
向客户端写入一段字符串。
使用CFSocket实现TCP客户端
创建TCP客户端同样通过CFSocket完成。使用CFSocket创建Socket客户端的步骤如下。
创建一个不监听任何事件或监听Connection的CFSocket。如果要监听Connection,则需要为kCFSocketConnectCallBack事件绑定回调函数.
调用CFSocketConnectToAddress()函数,将客户端的CFSocket连接到指定IP地址和端口的服务器上.
得到客户端CFSocket之后,既可直接使用CFSocket对应的CFSocketNativeHandle进行读/写,也可通过CFSocket获取CFReadStreamRef、CFWriteStreamRef后进行读/写。
ViewController.m
#import &sys/socket.h&
#import &netinet/in.h&
#import &arpa/inet.h&
#import &ViewController.h&
@implementation ViewController
CFSocketRef
- (void)viewDidLoad
[super viewDidLoad];
// 创建Socket,无须回调函数
_socket = CFSocketCreate(kCFAllocatorDefault,
PF_INEF // 指定协议族,如果该参数为0或者负数,则默认为PF_INET
// 指定Socket类型,如果协议族为PF_INEF,且该参数为0或负数
// 则它会默认为SOCK_STREAM,如果要使用UDP协议,则该参数指定为SOCK_DGRAM
, SOCK_STREAM
// 指定通信协议.如果前一个参数为SOCK_STREAM,则默认使用TCP协议
// 如果前一个参数SOCK_DGRAM,则默认使用UDP协议
, IPPROTO_TCP
// 该参数指定下一个回调函数所监听的事件类型
kCFSocketNoCallBack
if(_socket != nil)
// 定义sockaddr_in类型的变量,该变量将作为CFSocket的地址
sockaddr_in
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
// 设置连接远程服务器的地址
addr4.sin_addr.s_addr = inet_addr(&192.168.1.88&);
// 设置连接远程服务器的监听端口
addr4.sin_port = htons(30000);
// 将IPv4的地址转换为CFDataRef
CFDateRef addres = CFDataCreate(kCFAllocatorDefault
(UInt8 *)&addr4, sizeof(addr4));
// 连接远程服务器的Socket,并返回连接结果
CFSocketError
result = CFSocketConnectionToAddress(_socket
address // 指定远程服务器的IP地址和端口
5 // 指定连接超时时长, 如果该参数为负数, 则把连接操作放在后台进行
当_socket消息类型为kCFSocketConnectCallBack时
将会在连接成功或失败的时候在后台触发回调函数
// 如果连接远程服务器成功
if(result == kCFSocketSuccess)
isOnline = YES;
// 启动新线程来读取服务器响应的数据
[NSThread detachNewThreadSelector:@selector(readStream)
toTarget:self withObject:nil];
// 读取接收的数据
- (void)readStream
char buffer[2048];
// 与本机关联的Socket如果已经失效,则返回-1:INVALID_SOCKET
while ((hasRead = recv(CFSocketGetNative(_socket)
, buffer, sizeof(buffer), 0)))
NSLog(@&%@&,[[NSString alloc] initWithBytes:buffer
length:hasRead encoding:NSUTF8StringEncoding]);
(IBAction)clicked:(id)sender
if(isOnline)
NSString* stringTosend = @&来自iOS客户端的问候&;
const char* data = [stringTosend UTF8String];
send(CFSocketGetNative(_socket), data, strlen(data) +1, 1);
NSLog(@&暂未连接服务器&);
&& 上面程序中的第1段红色字代码创建了一个CFSocket, 该CFSocket同样适用了TCP协议,并且是基于SOCK_STREAM流的Socket,然后程序创建了一个struct& sockaddr_in 结构体变量,该结构体变量代表远程服务器的地址.
&& 上面程序中的第2段红色字代码调用了CFSocketConnectToAddress()函数将CFSocket连接到远程服务器地址---如果连城成功,就可得到一个进行网络读/写的CFSocket.剩下的事情是程序以readStream作为新线程的执行体,启动了一个新线程,其中readStream方法中的红色字代码调用了recv()函数从指定CFSocket读取数据.而clicked:方法则用于向服务器发送数据,该方法中的红色字代码调用了send()函数向CFSocket发送数据.
使用CocoaAsyncSocket实现TCP客户端
&& CocoaAsyncSocket封装了CFNetwork底层的CFSocket和CFStream,并提供了异步操作,从而可简化Socket网络编程.CocoaAsyncSocket不仅支持TCP协议的网络编程,也支持UDP协议的网络编程.CocoaAsyncSocket是CFSocket的绝佳替代者.
CocoaAsyncSocket主要有以下特性
非阻塞方式的读和写,而且可设置超时时长.
自动的Socket接收。如果调用它接受连接,它将为每个连接启动新的实例,当然也可以立即关闭这些连接。
委托(delegate)支持。错误、连接、接收、完整的读取、完整的写入、进度以及断开连接,都可通过委托模式调用。
所有操作都封装在一个类中,无须操作Socket或流,该类封装了所有操作。
下载和安装CocoaAsyncSocket的步骤如下
1.CocoaAsyncSocket的官方网站是https://github.com/robbiehanson/CocoaAsyncSocket,登录该站点,单击页面中间的release链接.
2.浏览器将会打开一个新的列表页面,该列表页面中列出了CocoaAsyncSocket的所有发布版本,建议下载最新版的CocoaAsyncSocket.
3.下载完成将可以得到一个CocoaAsyncSocket.zip文件,解压该压缩包,将可以看到如下文件结构.
&&&&&&& RunLoop:该目录下包含了AsyncSocket\AsyncUDPSocket两个类的源文件和Xcode目录.其中AsyncSocket就是基于TCP协议的CocoaAsyncSocket实现,AsyncUDPSocket就是基于UDP协议的AsyncUDPSocket实现。而Xcode目录下则包含了使用CocoaAsyncSocket开发服务器端与客户端的示例项目。
&&&&&&& GCD:该目录下的内容与RunLoop目录下的内容基本相似,只是类名变成了GCDAsyncSocket、GCDAsyncUDPSocket,这是因为该目录下的CocoaAsyncSocket是基于GCD的实现。
&&&&&&& Vendor:其他相关类。
&&&&&&& 其他杂项文件。
&&&&&&& 需要为项目增加CFNetwork.framework框架。
添加CocoaAsyncSocket支持之后,使用AsyncSocket开发TCP客户端的步骤如下
代 码 片 段
1 ViewController.h
3 #import &UIKit/UIKit.h&
5 #import &AsyncSocket.h&
7 @interface ViewController : UIViewController&AsyncSocketDelegate&
9 @property (strong, nonatomic) IBOutlet UITextView *showV
11 @property (strong, nonatomic) IBOutlet UITextFiled *inputField;
13 - (IBAction)finishEdit:(id)
15 - (IBAction)send:(id)
19 ViewController.m
21 @implementation ViewController
23 NSString* myN
25 AsyncSocket*
27 BOOL iSO
29 - (void)viewDidLoad
[super viewDidLoad];
// 创建一个UIAlertView提醒用户输入名字
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@&名字&
39 message:@&请输入您的名字&
delegate:self
41 cancelButtonTitle:@&确定& otherButtonTitles: nil ];
43 // 设置该UIAlertView为UIAlertViewStylePlainTextInput风格
45 alert.alertViewStyle = UIAlertViewStylePlainTextI
47 [alert show];
51 - (IBAction)finishEdit:(id)sender
[sender resignFirstResponder];
59 - (IBAction)send:(id)sender
if(isOnline)
67 // 定义要发送的字符串内容
69 NSString* stringTosend = [NSString stringWithFormat:@&%@说: %@&, myName, self.inputField.text];
71 self.inputField.text =
73 NSData *data = [stringTosend dataUsingEncoding:NSUTF8StringEncoding];
75 // 调用writeData:withTimeout:tag:方法发送数据
77 [socket
writeData:data
withTimeout: - 1
NSLog(@&暂未连接服务器&);
91 // AsyncSocketDelegate中定义的方法,当成功连接到服务器时激发该方法
93 - (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
isOnline = YES;
// 调用readDataWithTimeout: tag: 方法读取数据
[sock readDataWithTimeout:-1 tag:0];
105 // AsyncSocketDelegate中定义的方法,当读取数据完成时激发该方法
107 - (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
// 获取读到的内容
NSData* strData = [data subDataWithRange:NSMakeRange(0, [data length])];
NSString* content = [[NSString alloc] initWithData:strData encoding:NSUTF8StringEncoding];
if(content)
121 // 使用showView控件显示从网络读取的内容
123 self.showView.text = [NSString stringWithFormat:@&%@\n%@&,
125 content , self.showView.text];
129 // 再次调用readDataWithTimeout:tag:方法读取数据
131 [sock readDataWithTimeout:-1 tag:0];
135 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
获取UIAlertView控件上 文本框内的字符串,并将字符串赋值给myName变量
myName = [alertView textFieldAtIndex:0].
socket = [[AsyncSocket alloc] initWithDelegate:self];
NSError* error =
int port = 30000;
NSString* host = @&192.168.1.88&;
// 调用connectToHost:onPort:error:方法连接指定IP地址、指定端口的服务器
[socket connectToHost:host onPort:port withTimeout:2 error:&error];
NSLog(@&连接出现错误:%@&,
& 上面程序中的第1行红色字代码创建了一个AsyncSocket,并指定该视图控制器本身作为它的delegate对象,这意味着该视图控制器本身需要实现AsyncSocketDelegate协议,并实现该协议中特定的方法.程序中的第2行红色字代码调用了AsyncSocket的connectToHost:onPort:error: 方法来连接指定IP地址、指定端口的服务器程序。
&& 处理AsyncSocket网络连接及网络通信过程中的事件,该视图控制器(作为AsyncSocket的delegate)实现了onSocket:didConnectToHost:port:方法------当AsyncSocket成功连接指定服务器时激发该方法,该方法中的程序调用了AsyncSocket的readDataWithTimeout: tag:方法读取网络数据。当AsyncSocket成功读取网络数据之后,系统会自动调用视图控制器(作为AsyncSocket的delegate)的onSocket:didReadData:方法------这就实现了通过AsyncSocket读取网络数据。
暂无相关文章
相关搜索:
相关阅读:
相关频道:
IOS教程最近更新&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
CFSocket相关(未完待续)
摘要:CFSocket相关今天想深入了解下CFNetworking,然后就去看了Apple的API,又结合了网络上前人的经验,发现了这么一段话。Sockets是网络通讯的最基本一层。一个socket起的作用类似与一个电话线接口,它可以使你连接到另一个socket上(不论是本地的还是网络另一端的),并且向那个socket发送数据。最常见的socket抽象概念就是BSDsockets,而CFSocket则是BSDsockets的抽象。CFSocket中包含了少数开销,它几乎可以提供BS
CFSocket相关
今天想深入了解下CFNetworking,然后就去看了Apple的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的使用,具体的可以百度下代码。
第一步:创建
CFSocketRef&&CFSocketCreate(
CFAllocatorRef allocator, //内存分配类型一般为默认KCFAllocatorDefault
SInt32 protocolFamily, //协议族,一般为Ipv4:PF_INET,(Ipv6,PF_INET6)
SInt32 socketType, //套接字类型TCP:SOCK_STREAM
UDP:SOCK_DGRAM
SInt32 protocol, //套接字协议TCP:IPPROTO_TCP
UDP:IPPROTO_UDP;
CFOptionFlags callBackTypes, //回调事件触发类型
Enum CFSocketCallBACKType{
KCFSocketNoCallBack = 0,
KCFSocketReadCallBack =1,
KCFSocketAcceptCallBack&= 2,(常用)
KCFSocketDtatCallBack = 3,
KCFSocketConnectCallBack = 4,
KCFSocketWriteCallBack = 8
CFSocketCallBack callout, // 触发时调用的函数
Const CFSocketContext *context // 用户定义数据指针
假设_socket = CFSocketCreate(….);
第二步:初始化
int yes = 1 ;
setsocketopt(&
CFSocketGetNative(_socket),//返回系统原生套接字,补齐缺省
SOL_SOCKET,
SO_REUSEADDR,
(void*)&;yes,
sizeof(yes)
) //对socket进行定义设置
第三步:地址
uint16_t port = 12345;
struct sockaddr_in addr4; // 定义监听地址以及端口
memset(&;addr4 , 0, sizeof (addr4));
addr4.sin_len = sizeof (addr4);
addr4.sin_family = AF_INET;
addr4.sin_port =htons(port)
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
CFData Ref&&address =CFDataCreate(
kCFAllocatorDefault,
(UInt8 *)&;&addr4,
sizeof (addr4),
int&rst = CFSocketSetAddress(_socket&,&;addr4);&
//将设置数据设入socket
If ( rst !=&KCFSocketSuccess&){…}
第四步:执行&
CFRunLoopRef&cfrl =&CFRunLoopGetCurrent();
//获取当前的运行循环&
CFRunLoopSourceRef&sourceRef =&CFSoceketCreateRunLoopSource(KCFAllocatorDefault,_socket,0);//创建一个运行循环源对象
CFRunLoopSource(cfrl , sourceRef,&KCFRunLoopCommonModes);//以该对象运行到当前运行循环中
CFRelease(sourceRef);
服务端响应
CFSocketCallBack callout, // 触发时调用的函数,该函数会在接收到客户端请求连接时触发:
ServerAcceptCallBack( //名字可以任意取,但参数是固定的
CFSoceketRef socket ,
CFSocketCallBackType callbacktype,
CFDataRef address,
const void * data, //与回调函数有关的特殊数据指针,
对于接受连接请求事件,这个指针指向该socket的句柄,
对于连接事件,则指向Sint32类型的错误代码
void *info) //与套接字关联的自定义的任意数据
{ //实现函数
If(kCFSocketAcceptCallBack&= = type ){
CFSocketNativeHandle&nativeSocketHandle = (CFSocketNativeHandle*)
//////////////////////以下片段用于输出来访者地址
Uint8_t name[SOCK_MAXADDRLEN]
Socklen_t namelen = sizeof(name);
If(0 != getpeername(nativeSocketHandle ,(struct sockaddr_in*)name,&;namelen)) //获取地址
Printf(“%s connected/n”,inet_ntoa((struct sockaddr_in *)name)-&sin_addr);
//////////////////////
CFReadStreamRef&iS
CFWriteStreamRef&oS
CFStreamCreatePairWithSocket( // 创建一个可读写的socket连接
kCFAllocatorDefault,&
nativeSocketHandle,
&;iStream,
&;oStream);
If(iStream &;&; oStream){
CFStreamClinetContext&streamCtxt = {0,NULL, NULL, NULL, NULL};
If(!CFReadStreamSetClient(
kCFStreamEventHasBytesAvailable&//有可用数据则执行
readStream, //设置读取时候的函数
&;steamCtxt))
{exit(1);}
If(!CFWriteStreamSetClient( //为流指定一个在运行循环中接受回调的客户端
kCFStreamEventCanAcceptBytes,&//输出流准备完毕,可输出
writeStream, //设置写入时候的函数
&;steamCtxt))
{exit(1);}
读取流操作(触发式,被动技能)
readStream(CFReadStreamRef&stream,CFStreamEventType&eventType, void *client CallBackInfo)
UInt8 buff[255];
CFReadStreamRead(stream,buff,255); //将输入流中数据存入buff
Printf(“received %s”,buff);
CFWriteStreamRef&outputStream = NULL; //输出流
写入流操作(仍然被动技能,在输出流准备好的时候调用)
writeStream (CFWriteStreamRef&stream,&CFStreamEventType&eventType, void *clientCallBackInfo)
outputStream = //输出流被指定
//主动输出,在输出流准备好之后才能调用
FucForWrite()
UInt8 buff[] = “Hunter21,this is Overlord”;
If(outputStream != NULL)
CFWriteStreamWrite(outputStream,buff,strlen(buff)+1);
------------------------------------------------------------------------------------------ios编程笔记:CFSocket(客户端)CFSocketRef _-(void)Connect{//////////////////////创建套接字//////////////CFSocketContext&CTX = {0,NULL,NULL,NULL,NULL};_socket =&CFSocketCreate(kCFAllocatorDefault,PF_INET,SOCK_STREAM,IPPROTO_TCP,kCFSocketConnectCallBack, // 类型,表示连接时调用ServerConnectCallBack, // 调用的函数)////////////////////////////设置地址///////////////////NSString *serverAddr = @&192.168.0.110&;struct sockaddr_in addrmemset(&;addr , 0,sizeof(addr));addr.sin_len = sizeof(addr);addr.sin_family = AF_INET;addr.sin_port = htons(12345);addr.sin_addr.s_addr = inet_addr([serverAddr UTF8String]);CFDataRef address =&CFDataCreate(kCFAllocatorDefault,(UInt8*)&;addr,sizeof(addr));/////////////////////////////执行连接/////////////////////CFSocketConnectToAddress(_socket,address,-1);CFRunLoopRef&cfrl = CFRunLoopGetCurrent(); // 获取当前运行循环CFRunLoopSourceRef&&source = CFSocketCreateRunLoopSource(kCFAllocatorDefault,_socket,0);//定义循环对象CFRunLoopAddSource(cfrl,source,kCFRunLoopCommonModes); //将循环对象加入当前循环中CFRelease(source);}static void ServerConnectCallBack(CFSocketRef&socket,CFSocketCallBackType&type,CFDataRef&address,const void *data,void * info){if(data != NULL){printf(&connect&).//服务器那边已经提过,连接事件时该指针用于存放报错}else{printf(&connect success&);}}///////////////////监听来自服务器的信息///////////////////-(void)ReadStream{char buffer[255];while(recv(&CFSocektGetNative(_socket),buffer,sizeof(buffer),0)){printf(buffer);}&}/////////////////////////发送信息给服务器////////////////////////- (void) sendMessage{NSString *stringToSend = @&Overlord,this is Hunter21&;const char *data = [stringToSend UTF8String];send(CFSocketGetNative(_socket), data, strlen(data) + 1, 0);}有关CFSocket可以看下面的内容:首先看张图:&这是MAC OS X系统中CFNetwork和其余软件层的结构图。CFNetwork位于底层,但高效地处理协议栈的操作。BSD套接字提供了一些标准对象来方便操作,如与FTP和HTTP服务器通信,解析DNS主机地址。而CFNetwork就是以BSD套接字为基础。类似,一些cocoa类,如NSURL,使用标准网络协议与服务器通信,就是以CFNetwork为基础。除此之外,Web Kit是一些cocoa类,显示窗口中的网络内容。而NSURL和Web Kit都是高层之上,要自行处理网络协议。因此,结构如上图。下图是CFNetwork框架与Core Foundation框架的结构图:&CFSocket API和CFStream API是CFNetwork的基础。套接字是网络通讯的基础,套接字可以连接到网络或是本地的另一个套接字,并允许数据传送。最通常的套接字抽象就是BSD Socket。CFSocket又是BSD Socket的抽象。CFSocket几乎包含BSD Socket的所有功能,而且将Socket融入run-loop中。CFSocket可以处理任何socket,甚至stream-based socket。CFStream API提供了轻松的与设备无关的读写数据的能力。你可以为内存,文件,网络(使用套接字)的数据建立stream,可以使用stream而不必立即把所有数据都写入到内存中。stream,流,是一个在搭建的通讯通道里连续传送的字节序列。steam是单向的,所有有必要建立input(read) stream和output(write) stream。除了基于文件的stream,否则,stream中的数据一经取出消耗,就无法找到。CFStream就是对这些stream的抽象,并提供两种CFType类型:CFReadStream 和 CFWriteStream,他们都符合Core Foundation API的规范。由图可以看出,CFStream是基于CFSocket,而且CFStream是CFFTP和CFHTTP的基础。而CFStream却不是CFNetwork的一部分,而是Core Foundation的一部分。CFNetwork API简介:CFNetwork API可以拆成许多独立的API,可以独立使用,可以联合使用。CFFTP APICFHTTP APICFHTTPAuthentication APICFHost APICFNetServices APICFNetDiagnostics APICFStream API对流的读写操作使我们可以以一种设备无关的方式在各种媒体之间交换数据。你可以为内存、文件或者网络(通过sockets)里面的数据创建流。另外在操作流的时候,所有数据可以分次加载。数据流本质上是在通信通道中串行传输的一个字节序列,它是单向的,所以如果需要双向传输的话必须操作一个输入流(读操作)和一个输出流(写操作)。除了基于文件的流以外,其他流都是不可搜索的,也就是说:在流数据被提供或者接收之后,就不能再从这个流当中获取数据了。CFStream API 用两个新的 CFType 对象提供了对这些流的一个抽象:CFReadStream 和 CFWriteStream。两个类型的流都遵循常见的核心基础 API 惯例。有关核心基础类型的更多信息,请参考设计概念。CFStream 的构建基于 CFSocket,同时也是 CFHTTP 和 CFFTP 的基础。在图 1-2 中你可以看到,尽管CFStream 并不是 CFNetwork的正式成员,它却是几乎所有 CFNetwork 成员的基础。你几乎可以用操作 UNIX 文件描述符的方式对流进行读写操作。首先,实例化流对象的时候需要指定流的类型(内存、文件或者socket)并且设置任何一个可选项。然后,打开流并可以进行任意次的读写操作。当流还存在的时候,你可以通过流的属性获取有关它的信息。流属性包括有关流的任何信息,比如它的数据源或者目标,这些都不属于被读写的实际数据范畴之内。当你不再需要一个流的时候,需要关闭并把它丢弃。CFStream 的函数如果不能进行至少一个字节数据的读写操作的话,它们可能会暂停或者阻塞当前的进程。为了避免在阻塞的时候从一个流读数据或者向一个流写数据,可以使用这些函数的异步操作版本,并且把有关这个流的操作放入一个循环当中。当可以从流中读写数据的时候,你的回调函数就会被调用。另外,CFStream 还内置了对安全 Sockets 层 (SSL) 协议的支持。你可以建立一个包含流的 SSL 信息的字典,其中的信息包括需要的安全级别或者自签署的认证。然后把这些信息当作&kCFStreamPropertySSLSettings&属性传递给流,这样一个流就被转换成了一个 SSL 流。要创建一个客户定制的 CFStream 是不可能的。比如,如果你想要对客户数据库文件当中的对象进行数据流操作,那么仅仅希望通过创建具有自己风格的 CFStream 对象是办不到这一点的,而只有通过定制 NSStream 的子类(利用 Objective-C)才可以做到。由于 NSStream 对象可以很容易的被转换为 CFStream 对象,所以你创建的 NSStream 子类可以被用在任何需要 CFStream 的地方。任何有关 NSStream 所属类的信息,请参考Cocoa 流编程指南。&有关流的操作&一章描述了如何进行读写流操作。既然上面有提到Socket的使用是放在runloop里面,那么就需要了解下runloop和多线程的知识。iPhone中的线程应用并不是无节制的,官方给出的资料显示iPhone OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB。并且该值不能通过编译器开关或线程API函数来更改。只有主线程有直接修改UI的能力。一.线程概述有些程序是一条直线,起点到终点;有些程序是一个圆,不断循环,直到将它切断。直线的如简单的Hello World,运行打印完,它的生命周期便结束了,像昙花一现那样;圆如操作系统,一直运行直到你关机。&一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。Mac和iOS中的程序启动,创建好一个进程的同时, 一个线程便开始运行,这个线程叫主线程。主线程在程序中的地位和其他线程不同,它是其他线程最终的父线程,且所有界面的显示操作即AppKit或 UIKit的操作必须在主线程进行。&系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则共用进程的内存空间。每创建一个新的线程,都需要一些内存(如每个线程有自己的Stack空间)和消耗一定的CPU时间。另外当多个线程对同一个资源出现争夺的时候需要注意线程安全问题。二.创建线程创建一个新的线程就是给进程增加了一个执行流,执行流总得有要执行的代码吧,所以新建一个线程需要提供一个函数或者方法作为线程的入口。1.使用NSThreadNSThread提供了创建线程的途径,还可以提供了检测当前线程是否是主线程的方法。 使用NSThread创建一个新的线程有两种方式:
1.创建一个NSThread的对象,调用其start方法。对于这种方式的NSThread对象的创建,可以使用一个目标对象的方法初始化一个NSThread对象,或者创建一个继承NSThread类的子类,实现其main方法,然后在直接创建这个子类的对象。
2.使用 detachNewThreadSelector:toTarget:withObject:这个类方法创建一个线程,这个比较直接了,直接使用目标对象的方法作为线程启动入口。2.使用NSObject其实NSObject直接就加入了多线程的支持,允许对象的某个方法在后台运行。如:?
&ol&class=&dp-cpp&&&li&class=&alt&&[myObj performSelectorInBackground:@selector(doSomething) withObject:nil]; &/li&&/ol&
3.POSIX Thread由于Mac和iOS都是基于Darwin系统,Darwin系统的XUN内核,是基于Mach和BSD的,继承了BSD的POSIX接口,所以可以直接使用POSIX线程的相关接口来使用线程。创建线程的接口为 pthread_create,当然在创建之前可以通过相关函数设置好线程的属性。以下为POSIX线程使用简单的例子。?
&ol&class=&dp-cpp&&&li&class=&alt&&// //& main.c //& pthread // //& Created by Lu Kejin on 1/27/12. //& Copyright (c) 2012 Taobao.com. Al &/li&&/ol&
三.多线程进阶NSOperation&;NSOperationQueue很多时候我们使用多线程,需要控制线程的并发数,毕竟线程也是消耗系统资源的,当程序中同时运行的线程过多时,系统必然变慢。 所以很多时候我们会控制同时运行线程的数目。NSOperation可以封装我们的操作,然后将创建好的NSOperation对象放到NSOperationQueue中,OperationQueue便开始启动新的线程去执行队列中的操作,OperationQueue的并发度是可以通过如下方式进行设置:?
&ol&class=&dp-cpp&&&li&class=&alt&&- (void)setMaxConcurrentOperationCount:(NSInteger)count &/li&&/ol&
GCDGCD是Grand Central Dispatch的缩写,是一系列的BSD层面的接口,在Mac 10.6 和iOS4.0以后才引入的,且现在NSOperation和NSOperationQueue的多线程的实现就是基于GCD的。目前这个特性也被移植到 FreeBSD上了,可以查看libdispatch这个开源项目。比如一个在UIImageView中显示一个比较大的图片?
&ol&class=&dp-cpp&&&li&class=&alt&&dispatch_queue_t imageDownloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,&0); dispatch_async(imageDownloa &/li&&/ol&
当然,GCD除了处理多线程外还有很多非常好的功能,其建立在强大的kqueue之上,效率也能够得到保障。四.线程间通信线程间通信和进程间通信从本质上讲是相似的。线程间通信就是在进程内的两个执行流之间进行数据的传递,就像两条并行的河流之间挖出了一道单向流动长沟,使得一条河流中的水可以流入另一条河流,物质得到了传递。1.performSelect On The Thread框架为我们提供了强制在某个线程中执行方法的途径,如果两个非主线程的线程需要相互间通信,可以先将自己的当前线程对象注册到某个全局的对象中去,这样相 互之间就可以获取对方的线程对象,然后就可以使用下面的方法进行线程间的通信了,由于主线程比较特殊,所以框架直接提供了在出线程执行的方法。?
&ol&class=&dp-cpp&&&li&class=&alt&&@interface&NSObject (NSThreadPerformAdditions) - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUnti &/li&&/ol&
2.Mach Port&在苹果的Thread Programming Guide的Run Pool一节的Configuring a Port-Based Input Source 这一段中就有使用Mach Port进行线程间通信的例子。 其实质就是父线程创建一个NSMachPort对象,在创建子线程的时候以参数的方式将其传递给子线程,这样子线程中就可以向这个传过来的 NSMachPort对象发送消息,如果想让父线程也可以向子线程发消息的话,那么子线程可以先向父线程发个特殊的消息,传过来的是自己创建的另一个 NSMachPort对象,这样父线程便持有了子线程创建的port对象了,可以向这个子线程的port对象发送消息了。当然各自的port对象需要设置delegate以及schdule到自己所在线程的RunLoop中,这样来了消息之后,处理port消息的delegate方法会被调用,你就可以自己处理消息了。五.RunLoopRunLoop从字面上看是运行循环的意思,这一点也不错,它确实就是一个循环的概念,或者准确的说是线程中的循环。 本文一开始就提到有些程序是一个圈,这个圈本质上就是这里的所谓的RunLoop,就是一个循环,只是这个循环里加入很多特性。&首先循环体的开始需要检测是否有需要处理的事件,如果有则去处理,如果没有则进入睡眠以节省CPU时间。 所以重点便是这个需要处理的事件,在RunLoop中,需要处理的事件分两类,一种是输入源,一种是定时器,定时器好理解就是那些需要定时执行的操作,输 入源分三类:performSelector源,基于端口(Mach port)的源,以及自定义的源。编程的时候可以添加自己的源。RunLoop还有一个观察者Observer的概念,可以往RunLoop中加入自己的 观察者以便监控着RunLoop的运行过程,CFRunLoop.h中定义了所有观察者的类型:?
&ol&class=&dp-cpp&&&li&class=&alt&&enum&CFRunLoopActivity { kCFRunLoopEntry = (1&&&&0), kCFRunLoopBeforeTimers = (1&&&&1), kCFRunLoopBeforeSources = ( &/li&&/ol&
如果你使用过select系统调用写过程序你便可以快速的理解runloop事件源的概念,本质上讲事件源的机制和select一样是一种多路复用IO的 实现,在一个线程中我们需要做的事情并不单一,如需要处理定时钟事件,需要处理用户的触控事件,需要接受网络远端发过来的数据,将这些需要做的事情统统注 册到事件源中,每一次循环的开始便去检查这些事件源是否有需要处理的数据,有的话则去处理。 拿具体的应用举个例子,NSURLConnection网络数据请求,默认是异步的方式,其实现原理就是创建之后将其作为事件源加入到当前的 RunLoop,而等待网络响应以及网络数据接受的过程则在一个新创建的独立的线程中完成,当这个线程处理到某个阶段的时候比如得到对方的响应或者接受完 了网络数据之后便通知之前的线程去执行其相关的delegate方法。所以在Cocoa中经常看到scheduleInRunLoop:forMode: 这样的方法,这个便是将其加入到事件源中,当检测到某个事件发生的时候,相关的delegate方法便被调用。对于CoreFoundation这一层而 言,通常的模式是创建输入源,然后将输入源通过CFRunLoopAddSource函数加入到RunLoop中,相关事件发生后,相关的回调函数会被调 用。如CFSocket的使用。 另外RunLoop中还有一个运行模式的概念,每一个运行循环必然运行在某个模式下,而模式的存在是为了过滤事件源和观察者的,只有那些和当前 RunLoop运行模式一致的事件源和观察者才会被激活。每一个线程都有其对应的RunLoop,但是默认非主线程的RunLoop是没有运行的,需要为RunLoop添加至少一个事件源,然后去run它。一般情况下我们是没有必要去启用线程的RunLoop的,除非你在一个单独的线程中需要长久的检测某个事件。
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
为您提供0门槛上云实践机会
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
CFSocket相关(未完待续)相关信息,包括
的信息,所有CFSocket相关(未完待续)相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
服务与支持
资源和社区
关注阿里云
International

我要回帖

更多关于 ajax中的回调函数 的文章

 

随机推荐