title: Threading and Sockets in Objective-C
body: <p><strong>NOTE</strong>: I've edited my question. I've got it to connect and perform the <em>first callback</em>, but subsequent callbacks don't go through at all.</p>
  
  <p>This is my first time writing Objective-C (with GNUstep; it's for a homework assignment). I've got the solution working, but I am trying to add something more to it. The app is a GUI client that connects to a server and gets data from it. Multiple clients can connect to the same server. If any one of the clients changes data that is residing on the server, the server sends a callback to all registered clients. This solution was originally implemented in Java (both client and server) and for the latest assignment, the professor wanted us to write an Objective-C client for it. He said that we don't need to handle callbacks, but I wanted to try anyway.</p>
  
  <p>I am using <code>NSThread</code> and I wrote something that looks like this:</p>
  
  <p><strong>CallbackInterceptorThread.h</strong></p>
  
  <pre><code>#import &lt;Foundation/Foundation.h&gt;
  #import "AppDelegate.h"
  
  @interface CallbackInterceptorThread : NSThread {
     @private
     NSString* clientPort;
     AppDelegate* appDelegate;
  }
  
  - (id) initWithClientPort: (NSString*) aClientPort
                appDelegate: (AppDelegate*) anAppDelegate;
  - (void) main;
  @end
  </code></pre>
  
  <p><strong>CallbackInterceptorThread.m</strong></p>
  
  <pre><code>#import &lt;Foundation/Foundation.h&gt;
  #import "CallbackInterceptorThread.h"
  
  #define MAXDATASIZE 4096
  
  @implementation CallbackInterceptorThread
  
  - (id) initWithClientPort: (NSString*) aClientPort
                  appDelegate: (AppDelegate*) anAppDelegate {
  
     if((self = [super init])) {
        [clientPort autorelease];
        clientPort = [aClientPort retain];
        [appDelegate autorelease];
        appDelegate = [anAppDelegate retain];
     }
  
     return self;
  }
  
  - (void) main {
  
     GSRegisterCurrentThread();
  
     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  
     char* buffer = malloc(MAXDATASIZE);
     Cst420ServerSocket* socket = [[Cst420ServerSocket alloc] initWithPort: clientPort];
     [socket retain];
  
     NSString* returnString;
  
     while(YES) {
        printf("Client waiting for callbacks on port %s\n", [clientPort cString]);
  
        if([socket accept]) {
           printf("Connection accepted!\n");
           while(YES) {
              printf("Inner loop\n");
              sleep(1);
  
              returnString = [socket receiveBytes: buffer maxBytes: MAXDATASIZE beginAt: 0];
              printf("Received from Server |%s|\n", [returnString cString]);
              if([returnString length] &gt; 0) {
                 printf("Got a callback from server\n");
  
                 [appDelegate populateGui];
              }
  
              printf("Going to sleep now\n");
              sleep(1);
           }
  
           [socket close];
        }
     }
  }
  
  @end
  </code></pre>
  
  <p><code>Cst420ServerSocket</code> has been provided to us by the instructor. It looks like this:</p>
  
  <pre><code>#import "Cst420Socket.h"
  #define PORT "4444"
  
  /**
   * Cst420Socket.m - objective-c class for manipulating stream sockets.
   * Purpose: demonstrate stream sockets in Objective-C.
   * These examples are buildable on MacOSX and GNUstep on top of Windows7
   */
  
  // get sockaddr, IPv4 or IPv6:
  void *get_in_addr(struct sockaddr *sa){
      if (sa-&gt;sa_family == AF_INET) {
          return &amp;(((struct sockaddr_in*)sa)-&gt;sin_addr);
      }
      return &amp;(((struct sockaddr_in6*)sa)-&gt;sin6_addr);
  }
  
  @implementation Cst420ServerSocket
  
  - (id) initWithPort: (NSString*) port{
     self = [super init];
     int ret = 0;
     memset(&amp;hints, 0, sizeof hints);
     hints.ai_family = AF_INET;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_flags = AI_PASSIVE; // use my IP
     const char* portStr = [port UTF8String];
     if ((rv = getaddrinfo(NULL, portStr, &amp;hints, &amp;servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        ret = 1;
     }else{
        for(p = servinfo; p != NULL; p = p-&gt;ai_next) {
           if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1){
              perror("server: socket create error");
              continue;
           }
           if (bind(sockfd, p-&gt;ai_addr, p-&gt;ai_addrlen) == -1) {
  #if defined(WINGS)
           closesocket(sockfd);
  #else
           close(sockfd);
  #endif
              perror("server: bind error");
              continue;
           }
           break;
        }
        if (p == NULL)  {
           fprintf(stderr, "server: failed to bind\n");
           ret = 2;
        }else{
           freeaddrinfo(servinfo); // all done with this structure
           if (listen(sockfd, BACKLOG) == -1) {
              perror("server: listen error");
              ret = 3;
           }
        }
        if (ret == 0){
           return self;
        } else {
           return nil;
        }
     }
  }
  
  - (BOOL) accept {
     BOOL ret = YES;
  #if defined(WINGS)
     new_fd = accept(sockfd, NULL, NULL);
  #else
     new_fd = accept(sockfd, (struct sockaddr *)&amp;their_addr, &amp;sin_size);
  #endif
     if (new_fd == -1) {
        perror("server: accept error");
        ret = NO;
     }
     connected = ret;
     return ret;
  }
  
  - (int) sendBytes: (char*) byteMsg OfLength: (int) msgLength Index: (int) at{
     int ret = send(new_fd, byteMsg, msgLength, 0);
     if(ret == -1){
        NSLog(@"error sending bytes");
     }
     return ret;
  }
  
  - (NSString* ) receiveBytes: (char*) byteMsg
                     maxBytes: (int) max
                      beginAt: (int) at {
     int ret = recv(new_fd, byteMsg, max-1, at);
     if(ret == -1){
        NSLog(@"server error receiving bytes");
     }
     byteMsg[ret+at] = '\0';
     NSString * retStr = [NSString stringWithUTF8String: byteMsg];
     return retStr;
  }
  
  - (BOOL) close{
  #if defined(WINGS)
     closesocket(new_fd);
  #else
     close(new_fd);
  #endif
     connected = NO;
     return YES;
  }
  
  - (void) dealloc {
  #if defined(WINGS)
     closesocket(sockfd);
  #else
     close(sockfd);
  #endif
     [super dealloc];
  }
  
  @end
  </code></pre>
  
  <p>Our professor also provided us an example of a simple echo server and client (the server just spits back whatever the client sent it) and I've used the same pattern in the thread.</p>
  
  <p>My initial problem was that my callback interceptor thread didn't accept any (callback) connections from the server. The server said that it could not connect back to the client (<code>ConnectException</code> from Java; it said "Connection refused"). I was able to fix this by changing my instructor's code. In the <code>connect</code> function (not shown), he had set the hints to use <code>AF_UNSPEC</code> instead of <code>AF_INET</code>. So Java was seeing my localhost IP come through as <code>0:0:0:0:0:0:0:1</code> (in IPv6 format). When Java tried to connect back to send a callback, it received an exception (not sure why it cannot connect to an IPv6 address).</p>
  
  <p>After fixing this problem, I tried out my app again and this time the callback from the server was received by my client. However, <em>subsequent</em> callbacks fail to work. After receiving the first callback, the busy-loop keeps running (as it should). But when the server sends a second callback, it looks like the client cannot read it in. On the server side I can see that it sent the callback to the client successfully. It's just that the client is having trouble reading in the data. I added some print statements (see above) for debugging and this is what I get:</p>
  
  <pre><code>Client waiting for callbacks on port 2020
  Connection accepted!
  Inner loop
  Received from Server |A callback from server to 127.0.0.1:2020|
  Got a callback from server
  Going to sleep now
  Inner loop
  Received from Server ||
  Going to sleep now
  Inner loop
  Received from Server ||
  Going to sleep now
  Inner loop
  ... (and it keeps going regardless of the second callback being sent)
  </code></pre>
  
  <p>Here is how I am starting the thread (from the GUI):</p>
  
  <pre><code>CallbackInterceptorThread* callbackInterceptorThread = [[CallbackInterceptorThread alloc] initWithClientPort: clientPort appDelegate: self];
  [callbackInterceptorThread start];
  </code></pre>