Re: Implementing a TCP port listener in Cocoa
Re: Implementing a TCP port listener in Cocoa
- Subject: Re: Implementing a TCP port listener in Cocoa
- From: Jakob Olesen <email@hidden>
- Date: Wed, 28 Jun 2006 21:16:32 +0200
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;
}
_______________________________________________
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