Bonjour写的聊天室的思路(一)——搭建服务器

来源:互联网 发布:te网络配线架打法图解 编辑:程序博客网 时间:2024/06/08 05:21

搭建服务器

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中已经写入的字节



0 0
原创粉丝点击