建立socket的方法与BSD socket相同(见上一篇),主要的不同在监听上,这里使用的是run loop对socket进行监听,这是CFNetwork的一个很大的优势
在通过BSD socket获取到listening descriptor后,通过CFSocketCreateWithNative()创建CFSocket,(这个步骤的逆向是CFSocketGetNative())然后将其加入run loop中
其实大家都知道BSD基本没有实用性,CFSocket还能用用
-(instancetype)initOnPort:(int)port {
struct sockaddr_in servaddr;
CFRunLoopSourceRef source;
const CFSocketContext context = {0, NULL, NULL, NULL, NULL};
self.errorCode = NOERROR;
if ((self.listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0) {
self.errorCode = SOCKETERROR;
} else {
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (bind(self.listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) <0) {
self.errorCode = BINDERROR;
} else {
if (listen(self.listenfd, LISTENQ) <0) {
self.errorCode = LISTENERROR;
} else {
self.sRef = CFSocketCreateWithNative(NULL, self.listenfd, kCFSocketAcceptCallBack,
acceptConnection, &context);
if (self.sRef == NULL) {
self.errorCode = CFSOCKETCREATEERROR;
}else {
source = CFSocketCreateRunLoopSource(NULL, self.sRef, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
CFRunLoopRun();
}
}
}
}
return self;
}
依次介绍下CFSocketCreateWithNative的几个参数:
CFAllocatorRef:用来定位新对象的内存位置,一般用NULL或者kCFAllocatorDefault表示当前默认
CFSocketNativeHandle:之前创建的 native BSD socket
CFOptionFlags:关于回调的选项依次是kCFSocketNoCallBack/...ReadCallBack/...AcceptCallBack/...DataCallBack/...ConnectCallBack/...WriteCallBack
CFSocketCallBack:回调调用的函数,这是个c函数(acceptConnection方法)
CFSocketContext:并不知道有什么卵用
CFSocketCreateRunLoopSource的几个参数:
CFAllocatorRef:同上
CFSocketRef:之前通过BSD创建的那个
CFIndex:run loop中的优先级
CFRunLoopAddSource的参数:
CFRunLoopRef:所要添加进的run loop
CFRunLoopSourceRef:要加入的source
CFStringRed:run loop mode
将CFRunLoopSourceRef加入run loop后,清除掉不需要的内容
CFRelease(source);
然后运行runloop
void acceptConnection(CFSocketRef sRef, CFSocketCallBackType
cType, CFDataRef address, const void *data, void *info)
{
CFSocketNativeHandle csock = *(CFSocketNativeHandle *)data;
CFSocketRef sn;
CFRunLoopSourceRef source;
const CFSocketContext context = {0, NULL, NULL, NULL, NULL};
sn = CFSocketCreateWithNative(NULL, csock,
kCFSocketDataCallBack, receiveData, &context);
source = CFSocketCreateRunLoopSource(NULL, sn, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source,
kCFRunLoopDefaultMode);
CFRelease(source);
CFRelease(sn);
}
这里创建了一个新的CFSocketRef然后再加入另一个回调方法
void receiveData(CFSocketRef sRef, CFSocketCallBackType
cType,CFDataRef address, const void *data, void *info)
{
CFDataRef df = (CFDataRef) data;
long len = CFDataGetLength(df);
if(len <= 0) return;
UInt8 buf[len];
CFRange range = CFRangeMake(0,len);
CFDataGetBytes(df, range, buf);
buf[len]='\0';
NSString *str = [[NSString alloc] initWithData:(NSData*)data
encoding:NSASCIIStringEncoding];
NSLog(@"Received: %@",str);
[[NSNotificationCenter defaultCenter]
postNotificationName:@"posttext" object:str];
CFSocketSendData(sRef, address, df, 0); // Echo back
}
将data先转成CFDataRef再转成UInt8再转成NSString
最后CFSocketSendData将CFDataRef格式的data发送出去