2012-06-13 49 views
5

背景

我一直有種自學約插座和各種示例代碼網絡編程和經典UNIX網絡編程教科書,同時努力把知識工作在我現在正在開發的應用程序中。我目前正在研究需要簡單的客戶端 - 服務器設置的應用程序的一部分。的iOS客戶端/服務器實現與「操作正在進行」錯誤

這是怎麼一回事呢,現在(當然,它應該怎麼走):

  1. Server發佈本身NSNetService並創建使用CFSocketCreateWithNative()
  2. 客戶端的插座中找到與NSNetServiceBrowser
  3. 客戶端解析服務器發現的服務
  4. 服務器從CFSocket獲取一個回調,該CFSocket創建一個類的新實例(MyConnection)來處理連接。讀取和寫入連接的流可通過CFStreamCreatePairWithSocket()獲得。
  5. 的客戶機消息的服務器(@「HI」)
  6. 服務器發送其從客戶端接收返回給客戶端(這是我的問題是)
  7. 客戶端顯示字符串中所涉及的數據UIAlertView中

兩個問題

  1. 我收到「操作正在進行」的錯誤,當我嘗試讓服務器將數據發送回客戶端,如連接代碼註釋下方。我相信這是因爲NSOutputStream沒有可用的空間。處理這個問題的最佳方法是什麼?我知道它應該等待NSStreamEventHasSpaceAvailable事件,但它似乎沒有發生...
    編輯:咄......當我收到此錯誤只與iPhone模擬器我測試的應用程序,並讓它充當服務器和客戶端,因爲我還沒有在我的新公寓中使用互聯網:P使用兩臺真實設備似乎不成問題。

  2. 是否有可能使這種服務器,以便發送和從每個連接對象接收數據的不阻塞發送和從其他連接對象接收數據的?每個新的連接對象是否需要在新的runloop或線程或類似的東西?我圍繞蘋果併發性文檔釣魚,但沒有什麼是跳出來的......目標是儘可能快地向客戶端發送回覆,無論有多少其他客戶端連接到服務器。
    更新:在允許到該服務器的併發連接,我只考慮排隊的連接和處理它們一次一個,因爲數據需要被髮送到每個客戶端的量非常小的代替。這是最好的決定嗎?如果隊列中有數百個客戶端會怎麼樣?第二個想法,這可能是一個壞主意,因爲連接的建立在快速的本地網絡上需要一兩秒鐘,而藍牙需要更長的時間......我希望在這個問題上:)

相關代碼

注意一些專家的意見:APNetService和APNetServiceBrowser類似於NSNetService和NSNetServiceBrowser

服務器代碼

- (void) startServerForGroup:(NSString *)name 
{ 
    self.groupName = name; 

    NSInteger port = [self prepareListeningSocket]; 

    self.service = [[APNetService alloc] initWithDomain:@"local." 
               type:@"_example._tcp." 
               name:self.groupName 
               port:port]; 
    self.service.delegate = self; 
    [self.service publish]; 
} 

- (NSInteger) prepareListeningSocket 
{ 
    int  listenfd, err, junk, port; 
    BOOL success; 

    struct sockaddr_in addr; 

    port = 0; 

    listenfd = socket(AF_INET, SOCK_STREAM, 0); 
    success = (listenfd != -1); 

    if (success) { 
    bzero(&addr, sizeof(addr)); 
    addr.sin_len = sizeof(addr); 
    addr.sin_family = AF_INET; 
    addr.sin_port = 0; 
    addr.sin_addr.s_addr = INADDR_ANY; 
    err = bind(listenfd, (const struct sockaddr *) &addr, sizeof(addr)); 
    success = (err == 0); 
    } 
    if (success) { 
    err = listen(listenfd, 5); 
    success = (err == 0); 
    } 
    if (success) { 
    socklen_t addrLen; 

    addrLen = sizeof(addr); 
    err = getsockname(listenfd, (struct sockaddr *) &addr, &addrLen); 
    success = (err == 0); 

    if (success) { 
     assert(addrLen == sizeof(addr)); 
     port = ntohs(addr.sin_port); 
    } 
    } 
    if (success) { 
    CFSocketContext context = { 0,(__bridge void*) self, NULL, NULL, NULL }; 

    CFSocketRef socket = CFSocketCreateWithNative(
                NULL, 
                listenfd, 
                kCFSocketAcceptCallBack, 
                AcceptCallback, 
                &context 
               ); 
    if (socket) { 
     self.listeningSocket = socket; 
     CFRelease(socket); 
     success = YES; 
    } 

    if (success) { 
     CFRunLoopSourceRef rls; 

     listenfd = -1; 

     rls = CFSocketCreateRunLoopSource(NULL, self.listeningSocket, 0); 
     assert(rls != NULL); 

     CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 

     CFRelease(rls); 
    } 
    } 

    if (success) { 
    return port; 
    } 
    else { 
    NSLog(@"FAILED TO START SERVER"); 

    if (listenfd != -1) { 
     junk = close(listenfd); 
     assert(junk == 0); 
    } 
    return -1; 
    } 
} 

#pragma mark - Callback 

// Called by CFSocket when someone connects to the listening socket 
static void AcceptCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 
{ 
    MyServer * obj; 

    obj = (__bridge MyServer *) info; 

    assert(s == obj->_listeningSocket); 

    MyConnection *newCon = [[MyConnection alloc] initWithFileDescriptor:*(int*)data]; 

    [newCon startReceive]; 

    //add the new connection object to the servers mutable array of connections 
    [obj.connections addObject:newCon]; 

} 

連接代碼

- (void) startReceive 
{ 
    CFReadStreamRef  readStream; 
    CFWriteStreamRef writeStream; 

    CFStreamCreatePairWithSocket(NULL, self.fd, &readStream, &writeStream); 

    self.inputStream = (__bridge_transfer NSInputStream *) readStream; 
    self.outputStream = (__bridge_transfer NSOutputStream*) writeStream; 

    [self.inputStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket]; 
    [self.outputStream setProperty:(id)kCFBooleanTrue forKey:(NSString *)kCFStreamPropertyShouldCloseNativeSocket]; 

    self.inputStream.delegate = self; 
    self.outputStream.delegate = self; 


    [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    [self.inputStream open]; 
    [self.outputStream open]; 
} 


#pragma mark - NSStreamDelegate 

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 
{ 
    switch (eventCode) { 

    case NSStreamEventHasBytesAvailable: { 
     NSInteger  bytesRead; 
     uint8_t   buffer[32768]; 

     bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)]; 
     if (bytesRead == -1)... 
     else if (bytesRead == 0)... 
     else { 
       NSData *data = [NSData dataWithBytes:buffer length:bytesRead]; 
       [self didReceiveData:data]; 
     } 
    } break; 
    case NSStreamEventHasSpaceAvailable: { 
     self.space = YES; 
    } break; 

    . . . 
    } 
} 

- (void) didReceiveData:(NSData *)data 
{ 
    if (self.space) 
    NSLog(@"SPACE"); 
    else 
    NSLog(@"NO SPACE"); //this gets printed 

    NSInteger i = [self.outputStream write:data.bytes maxLength:data.length]; 

    if (i < 0) { 
    printf("%s",strerror(errno)); //"Operation now in progress" error 
    } 
} 

客戶端代碼

#pragma mark - APNetServiceBrowserDelegate 

- (void) browser:(APNetServiceBrowser *)browser didAddService:(APNetService *)service moreComing:(BOOL)moreComing 
{ 
     //omitting checks that determine which server to connect to, if multiple 

    service.delegate = self; 
    [service resolveWithTimeout:20]; 
} 


#pragma mark - APNetServiceDelegate 

- (void) netServiceDidResolveAddress:(APNetService *)service 
{  
    NSInputStream *input; 
    NSOutputStream *output; 

    [service getInputStream:&input outputStream:&output]; 

    self.inputStream = input; 
    self.outputStream = output; 

    self.inputStream.delegate = self; 
    self.outputStream.delegate = self; 

    [self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 
    [self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 

    [self.inputStream open]; 
    [self.outputStream open]; 
} 


#pragma mark - NSStreamDelegate 

- (void) stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode 
{ 
    switch (eventCode) {  

    case NSStreamEventHasBytesAvailable: { 
     NSInteger  bytesRead; 
     uint8_t   buffer[32768]; 

     bytesRead = [self.inputStream read:buffer maxLength:sizeof(buffer)]; 

     if (bytesRead == -1) NSLog(@"Error reading data"); 
     else if (bytesRead == 0) NSLog(@"no bytes read"); 
     else { 
       NSData *data = [NSData dataWithBytes:buffer length:bytesRead]; 
       [self didReceiveData:data]; 
     } 
    } break; 

    case NSStreamEventHasSpaceAvailable: { 
     if (!self.isWaitingForReply) { 
     [self sendHelloMessage]; 
     } 
    } break; 
     //omitted other NSStreamEvents  
    } 
} 

- (void) sendHelloMessage 
{ 
    NSData *d = [NSKeyedArchiver archivedDataWithRootObject:@"hi"]; 

    [self.outputStream write:d.bytes maxLength:d.length]; 
    self.isWaiting = YES; 
} 


- (void) didReceiveData:(NSData *)data 
{ 
    NSString *string = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Message" 
                message:string 
               delegate:self 
             cancelButtonTitle:@"OK" 
             otherButtonTitles:nil]; 
    [alert show]; 
} 
+0

+1,結構良好! – doNotCheckMyBlog

回答

0

正如從我所看到的,你想,可以處理很多連接速度非常快的服務器。一個很好的起點文檔是c10k頁面:http://www.kegel.com/c10k.html。排隊可能是一個好主意,它將消耗比分支更少的資源,並且會更快地響應。但是,這也意味着您的計算保持相對較小,以便您仍然能夠快速回復。一個好的開始也許http://libevent.org/,檢查它是如何做到的,它是這種用途的設計。

祝你好運。

相關問題