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

11 篇文章 0 订阅

搭建服务器

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值