• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Implementing a TCP port listener in Cocoa
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Implementing a TCP port listener in Cocoa


  • Subject: Re: Implementing a TCP port listener in Cocoa
  • From: Andrew Bowman <email@hidden>
  • Date: Wed, 28 Jun 2006 14:35:14 -0700


On Jun 28, 2006, at 12:16 PM, Jakob Olesen wrote:


On 28/06/2006, at 16.06, Rick Hoge wrote:


I would like to implement an internet service from within a Cocoa application such that when a remote client requests a connection on a certain port the connection and subsequent service will be handled by the Cocoa application.


I want to do this using Cocoa Foundation classes if possible, but it's not really clear from the documentation how to set this up. Most of the documentation is geared towards Distributed Objects (DO) and not provision of a TCP port service in the classic Unix sense.

I think you need to use Core Foundation for a listening socket. CFSocket lets you create a socket that listens for incoming connections in a run loop. You get a callback for each connection. Check out http://developer.apple.com/samplecode/CFLocalServer/ index.html It is for UNIX domain sockets, but you can do the same for INET sockets.


Once you get a connection, you are passed a bsd-style file descriptor in the accept callback. Pass that to CFStreamCreatePairWithSocket() to get a CFReadStreamRef +CFWriteStreamRef pair. These are toll-free bridged to NSInputStream and NSOutputStream.


The code below is a bit messy - it is part of a Ruby wrapper for CFSocket. Apple's sample code is probably better.


static void
accept_callback(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info)
{
if (callbackType!=kCFSocketAcceptCallBack)
return;
CFSocketNativeHandle fd = *(CFSocketNativeHandle*)data;
// pass fd to CFStreamCreatePairWithSocket...
}


// ServerSocket.new(:host, port)
static VALUE
socket_initialize(int argc, VALUE *argv, VALUE slf)
{
    VALUE ahost, aport;
    rb_scan_args(argc, argv, "02", &ahost, &aport);

Socket *s = DATA_PTR(slf);
struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP;
hint.ai_flags = AI_PASSIVE | AI_CANONNAME;
const char *hostname = NIL_P(ahost) ? 0 : StringValuePtr(ahost);
const char *servname = NIL_P(aport) ? 0 : RSTRING (rb_obj_as_string(aport))->ptr;
struct addrinfo *res = 0;
if (getaddrinfo(hostname, servname, &hint, &res))
rb_sys_fail("getaddrinfo");
if (!res)
rb_raise(rb_eIOError, "getaddrinfo(%s,%s) failed", hostname, servname);


CFSocketContext ctx = { 0, (void*)slf, cf_ruby_retain, cf_ruby_release, cf_ruby_copy_description };
s->sock = CFSocketCreate(kCFAllocatorDefault, res->ai_family, res->ai_socktype, res->ai_protocol, kCFSocketAcceptCallBack, accept_callback, &ctx);
if (!s->sock) {
freeaddrinfo(res);
rb_sys_fail("couldn't create socket");
}


CFDataRef addr = CFDataCreate(kCFAllocatorDefault, (UInt8*)res- >ai_addr, res->ai_addrlen);
//NSLog(@"getaddrinfo(%s,%s) -> %s %@", hostname, servname, res- >ai_canonname, addr);
freeaddrinfo(res);


int yes = 1;
setsockopt(CFSocketGetNative(s->sock), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));


    // now bind
    CFSocketError err = CFSocketSetAddress(s->sock, addr);
    CFRelease(addr);
    if (err!=kCFSocketSuccess) {
        rb_sys_fail("couldn't bind socket");
    }
    return slf;
}

static VALUE
m_accept(VALUE slf)
{
// NSLog(@"accept slf=%p", slf);
Socket *s = DATA_PTR(slf);
s->block = rb_block_proc();
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource (kCFAllocatorDefault, s->sock, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes);
CFRelease(rls);
return Qnil;
}





Is there something wrong with using NSSocketPort and asynchronous callbacks with NSFileHandle? Assuming serverSocket and serverHandle are instance variables, something like:

serverSocket = [[NSSocketPort alloc] initWithTCPPort:myPortNumber];

serverHandle = [[NSFileHandle alloc] initWithFileDescriptor: [serverSocket socket] closeOnDealloc:YES];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptConnection:) name:NSFileHandleConnectionAcceptedNotification object:serverHandle];

[serverHandle acceptConnectionInBackgroundAndNotify];


Your acceptConnection method (or whatever you choose to name it) would look like:



- (void) acceptConnection: (NSNotification *)note {
NSFileHandle *serviceHandle = [[note userInfo] objectForKey:NSFileHandleNotificationFileHandleItem];


// any extra code here for dealing with new connections and retaining serviceHandle, probably in some collection

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readData:) name:NSFileHandleReadCompletionNotification object:serviceHandle];


	[serviceHandle readInBackgroundAndNotify];


[serverHandle acceptConnectionInBackgroundAndNotify]; // in order to keep listening
}



Your readData method (or whatever you choose to name it) would look something like:



- (void) readData: (NSNotification *)note {
NSData *dataBytes = [[note userInfo] objectForKey:NSFileHandleNotificationDataItem];


	if ([dataBytes length] != 0) {
		// your code here

// if you need to read more, obtain the appropriate file handle and send another readInBackgroundAndNotify: message.
}
else {
// data length of 0 means connection is closed
// cleanup here, if necessary
}
}



Seems like this is an easier way to go.


- Andrew Bowman _______________________________________________ Do not post admin requests to the list. They will be ignored. Cocoa-dev mailing list (email@hidden) Help/Unsubscribe/Update your Subscription: This email sent to email@hidden
  • Follow-Ups:
    • Re: Implementing a TCP port listener in Cocoa
      • From: Jakob Olesen <email@hidden>
References: 
 >Implementing a TCP port listener in Cocoa (From: Rick Hoge <email@hidden>)
 >Re: Implementing a TCP port listener in Cocoa (From: Jakob Olesen <email@hidden>)

  • Prev by Date: RE: How to debug during Unit Test (otest)
  • Next by Date: Re: How to debug during Unit Test (otest)
  • Previous by thread: Re: Implementing a TCP port listener in Cocoa
  • Next by thread: Re: Implementing a TCP port listener in Cocoa
  • Index(es):
    • Date
    • Thread