IOS client / server and Operation in progress error

Background

I kind of taught myself about sockets and network programming from various code examples and the classic Unix Network Programming programming tutorial, while trying to put knowledge into the application I'm working on. I am currently working on a part of an application that requires a simple client-server setup.

Here's how it happens right now (well, how it should turn out):

  • The server is published with NSNetServiceand creates a socket usingCFSocketCreateWithNative()
  • The client finds the server with NSNetServiceBrowser
  • The client resolves the discovered service
  • The server receives a callback from CFSocket, which creates a new instance of the class ( MyConnection) to handle the connection. Reading and writing streams for the connection is achieved using CFStreamCreatePairWithSocket().
  • The client tells the server (@ "hello")
  • The server sends the data that it receives from the client back to the client (this is my problem)
  • Client displays string in UIAlertView

Two questions

  • I get the error "Operation now in progress" when I try to send the server back to this client, as indicated in the connection code below. I believe that this is because it NSOutputStreamdoes not have free space. What is the best way to handle this? I know that he should wait for the event NSStreamEventHasSpaceAvailable, but it seems that this is not happening ...
    EDIT: duh ... When I received this error, I only tested the application with the iPhone simulator and allowed it to act as a server and client, because I didn’t have Internet in my new apartment: P It seems that this is no problem when using two real devices.

  • , ? runloop - ? Apple concurrency, ... , , , .
    : , , , . ? , ? , , 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];
}
+5
1

, , . c10k: http://www.kegel.com/c10k.html. , , , forking, . , , . , , http://libevent.org/, , , .

.

0

All Articles