1

I'm writing a Cocoa app. There is a socket in the application, and whenever the socket becomes readable I want to read data from the socket, process the data, and update the user interface accordingly. I want to integrate the read event check in the main loop, i.e. I want to attach the socket to the main loop and have the main loop call a callback whenever that socket becomes readable.

I've written a test application, but for some reason it doesn't work:

#include <stdio.h>
#include <Foundation/NSAutoReleasePool.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSPort.h>

@interface MyDelegate : NSObject <NSPortDelegate> {
}
- (void)handlePortMessage:(NSPortMessage *)portMessage;
@end

@implementation MyDelegate
- (void)handlePortMessage:(NSPortMessage *)portMessage {
    printf("Haiz\n");
}
@end

int
main() {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSSocketPort *server = [NSSocketPort alloc];
    MyDelegate *foo = [MyDelegate alloc];
    [server initWithTCPPort: 1234];
    [server setDelegate: foo];
    [[NSRunLoop mainRunLoop] addPort: server forMode: NSDefaultRunLoopMode];
    [[NSRunLoop mainRunLoop] run];
    [pool release];
    return 0;
}

The app is supposed to listen on localhost port 1234, and whenever someone connects to the server or sends data to the server, the app is supposed to print "Haiz" on the console. However the app does nothing at all. The socket is created and I can telnet to port 1234, but the app doesn't print anything to the console.

What am I doing wrong?

Hongli
  • 18,682
  • 15
  • 79
  • 107
  • Among other things, sending `alloc` to a class but not sending `init` to the instance. You forget this completely with your MyDelegate object, which demonstrates well why you should always keep `alloc` and `init` together in the same message expression: `MyDelegate *foo = [[[MyDelegate alloc] init] autorelease];` Note also that you must release objects you allocate. Review the memory management rules: http://developer.apple.com/mac/library/documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html – Peter Hosey Feb 16 '10 at 16:06

2 Answers2

1

From the documentation:

An NSSocketPort object can be used as an endpoint for distributed object connections.

That's not what you're doing here.

You want either NSFileHandle around a socket file descriptor from the BSD sockets API, or a CFSocket. This will let you put the socket on the run loop.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • NSFileHandle is not an NSPort and there's no CFRunLoopSource for it so I cannot attach it to the main loop. – Hongli Feb 16 '10 at 17:19
  • I know NSFileHandle isn't an NSPort. NSPorts are for IPC. Your delegate method doesn't get called because you never receive a port message on that socket. As for attaching to the run loop, either send the file handle a `acceptConnectionInBackgroundAndNotify` message or use CFSocket instead. – Peter Hosey Feb 16 '10 at 17:33
  • I was under the impression that FoundationKit is just an objective C wrapper around CoreFoundation, but now it seems they're totally different and that there are some things that I can do with CoreFoundation but not with FoundationKit? The reason why I'm trying to cling to FoundationKit because I'm actually writing the app in MacRuby, not Objective C, and it has some problems with function pointers which the CoreFoundation APIs expect. – Hongli Feb 16 '10 at 18:56
  • Foundation is older than Core Foundation. Some Foundation classes are now wrappers for or toll-free-bridged with CF classes, but not all, and there are some things you can do with each that you can't do with the other. – Peter Hosey Feb 16 '10 at 20:24
0

You want to use NSSocketPort in the way you're doing, but then create a NSFileHandle to accept connections on the socket. You can get callbacks on the main thread just like you're expecting, first for new connections, and then for data on those connections. Use this O'Reilly article and just ignore the HTTP stuff.

easeout
  • 8,665
  • 5
  • 43
  • 51