FROM : Jakob Olesen
DATE : Wed Jun 28 21:16:32 2006
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;
}
DATE : Wed Jun 28 21:16:32 2006
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;
}






Cocoa mail archive

