5

バックグラウンド

私はソケットとネットワーク プログラミングについて、さまざまなサンプル コードと古典的なUnix ネットワーク プログラミングの教科書から独学で学んできましたが、同時に、現在取り組んでいるアプリでその知識を活用しようとしています。私は現在、単純なクライアント サーバー セットアップを必要とするアプリケーションの一部に取り組んでいます。

これが現在の様子です(まあ、どうあるべきか):

  1. サーバーはそれ自体を公開しNSNetService、使用してソケットを作成しますCFSocketCreateWithNative()
  2. クライアントはサーバーを見つけますNSNetServiceBrowser
  3. クライアントは検出されたサービスを解決します
  4. サーバーは CFSocket からコールバックを取得しMyConnection、接続を処理するクラス ( ) の新しいインスタンスを作成します。接続の読み取りおよび書き込みストリームは、 で取得されCFStreamCreatePairWithSocket()ます。
  5. クライアントがサーバーにメッセージを送る (@"hi")
  6. サーバーは、クライアントから受け取ったデータをクライアントに送り返します(これが私の問題です)
  7. クライアントは文字列を UIAlertView に表示します

2 つの質問

  1. 以下の接続コードで注釈が付けられているように、サーバーからクライアントにデータを送り返そうとすると、「操作が進行中です」というエラーが表示されます。これは、NSOutputStream利用可能なスペースがないためだと思います。これに対処する最善の方法は何ですか?イベントを待つ必要があることはわかっていますが、NSStreamEventHasSpaceAvailableイベントが発生していないようです...<br> 編集: 当然... このエラーが発生したとき、iPhone シミュレーターのみでアプリをテストし、それをサーバーとして機能させていました。私はまだ新しいアパートにインターネットを持っていなかったので、クライアント:P 2 つの実際のデバイスを使用する場合は問題にならないようです。

  2. 各接続オブジェクトからのデータの送受信が、他の接続オブジェクトからのデータの送受信をブロックしないように、このサーバーを作成することはできますか? 新しい接続オブジェクトはそれぞれ、新しい実行ループまたはスレッドなどに配置する必要がありますか? 私はリンゴの同時実行ドキュメントを釣り上げましたが、何も飛び出していません...目標は、サーバーに接続されている他のクライアントの数に関係なく、できるだけ早くクライアントに応答を送信することです。
    更新: このサーバーへの同時接続を許可する代わりに、各クライアントに送信する必要があるデータの量が非常に少ないため、接続をキューに入れ、一度に 1 つずつ処理することを検討しています。これは最善の決定ですか?キューに何百ものクライアントがある場合はどうなりますか? 再考すると、接続の確立には高速なローカル ネットワークでは 1 ~ 2 秒かかり、Bluetooth ではさらに時間がかかるため、これは悪い考えかもしれません... この問題について専門家のアドバイスをいただければ幸いです :)

関連コード

注: 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];
}
4

1 に答える 1

0

私が見たように、多くの接続を非常に高速に処理できるサーバーを作成したいと考えています。ドキュメントの出発点としては、c10k ページ ( http://www.kegel.com/c10k.html ) が適しています。はい、キューイングは良い考えかもしれません。フォーキングよりもはるかに少ないリソースを消費し、より速く応答します。ただし、それはまた、計算が比較的小さいままであるため、高速に応答できることも意味します. 良いスタートかもしれませんhttp://libevent.org/、それがどのように行われたかを確認してください。これは、この種の使用のための設計です。

幸運を。

于 2012-10-04T13:28:46.427 に答える