搭建服务器:
1.创建socket--CFSocketCreate设置accept后callback + 确保相同的socket在每次连接后得到重用(setsockopt())
2.bind——创建sockaddr_in struct记录服务器信息,port设为0(主机自动分配)再用[NSData dataWithBytes: length: ]转换为NSData,用CFSocketSetAddressData进行连接,其他的连接方式还有直接bind(CFSocketNativeHandle, struct sockaddr * ,sizeof(struct sockeaddr ))
3.找出主机为你分配的port,就是反过来从与sockadd_in struct连接的socket得到address,得到的是NSData类型,通过memcpy转化为struct sockaddr_in,然后就得到port了。
NSData *socketAddressActualData = [(NSData *)CFSocketCopyAddress(listeningSocket) autorelease];
// Convert socket data into a usable structure
struct sockaddr_in socketAddressActual;
memcpy(&socketAddressActual, [socketAddressActualData bytes], [socketAddressActualData length]);
self.port = ntohs(socketAddressActual.sin_port);
4.socket加入runloop进行监听
CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket, 0);
CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopCommonModes);
CFRelease(runLoopSource);
Publish Bonjour Service
1.初始化NSNetservice
NSString* chatRoomName = [NSString stringWithFormat:@"%@'s chat room", [[AppConfig sharedInstance] name]];
// create new instance of netService
self.netService = [[NSNetService alloc] initWithDomain:@"" type:@"_chatty._tcp." name:chatRoomName port:self.port];
2.加入主线程
[self.netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
3.设置代理,连接失败关闭server
[self.netService setDelegate:self];
- (void) netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict
{
if ( sender != self.netService ) {
return;
}
// Stop socket server
[self terminateServer];
// Stop Bonjour
[self unpublishService];
// Let delegate know about failure
[delegate serverFailed:self reason:@"Failed to publish service via Bonjour (duplicate server name?)"];
}
- (void) terminateServer
{
if ( listeningSocket != nil ) {
CFSocketInvalidate(listeningSocket);
CFRelease(listeningSocket);
listeningSocket = nil;
}
}
- (void) unpublishService
{
if ( self.netService ) {
[self.netService stop];
[self.netService removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
self.netService = nil;
}
}
4.publish
[self.netService publish];
CFSocket的callback:
1.使用socket进行connect
2.设置代理,handleNewConnection:
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
// We can only process "connection accepted" calls here
if ( type != kCFSocketAcceptCallBack ) {
return;
}
// for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
Server *server = (Server *)info;
[server handleNewNativeSocket:nativeSocketHandle];
}
- (void) handleNewNativeSocket:(CFSocketNativeHandle)nativeSocketHandle
{
Connection* connection = [[[Connection alloc] initWithNativeSocketHandle:nativeSocketHandle] autorelease];
// In case of errors, close native socket handle
if ( connection == nil ) {
close(nativeSocketHandle);
return;
}
// finish connecting
BOOL succeed = [connection connect];
if ( !succeed ) {
[connection close];
return;
}
// Pass this on to our delegate
[delegate handleNewConnection:connection];
}
Connect的过程
1.通过之前的socket创建readStream和writeStream
CFStreamCreatePairWithSocket(kCFAllocatorDefault, self.connectedSocketHandle,
&readStream, &writeStream);
2.然后对incomingDataBuffer和outgoingDataBuffer进行初始化,
incomingDataBuffer = [[NSMutableData alloc] init];
outgoingDataBuffer = [[NSMutableData alloc] init];
3.设置readstream和writestream随着socket的关闭而关闭,
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
4.再设置readstream和writestream的callback,
CFOptionFlags registeredEvents = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable
| kCFStreamEventCanAcceptBytes | kCFStreamEventEndEncountered
| kCFStreamEventErrorOccurred;
// Setup stream context - reference to 'self' will be passed to stream event handling callbacks
CFStreamClientContext ctx = {0, self, NULL, NULL, NULL};
// Specify callbacks that will be handling stream events
CFReadStreamSetClient(readStream, registeredEvents, readStreamEventHandler, &ctx);
CFWriteStreamSetClient(writeStream, registeredEvents, writeStreamEventHandler, &ctx);
5.将readstream和write stream加入runloop
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
CFWriteStreamScheduleWithRunLoop(writeStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
6.测试下readStream和writeStream能不能打开
if ( !CFReadStreamOpen(readStream) || !CFWriteStreamOpen(writeStream)) {
[self close];
return NO;
}
7.close就是将readStream、writeStream、incomingBuffer、outgoingBuffer清空。readStream和writeStream关闭的方式:踢出runloop,再read/writeStream close然后release,然后设置位NULL
ReadStream的回调
1.incomingbuffer的长度设置位readstream的长度
2.去除incomingDataBuffer中头部长度sizeof(int)
3.将下一段sizeof(int)包装成NSDictionary用delegate传出。
WriteStream的回调
1.outgoingDataBuffer的长度不为0且CFWriteStreamCanAcceptBytes
2.向writeStream中写入:CFWriteStreamWrite(writeStream, [outgoingDataBuffer bytes], [outgoingDataBuffer length])
3.移除outgoingBuffer中已经写入的字节