Sockets don't quite work, but they will almost work.
[ITFoundation.git] / ITInetServerSocket.m
index dcc8805..08a8fe0 100755 (executable)
@@ -9,60 +9,84 @@
 #import "ITInetServerSocket.h"
 #import "ITInetSocket.h"
 #import <sys/types.h>
+#import <sys/time.h>
 #import <arpa/inet.h>
 #import <netinet/in.h>
 #import <sys/socket.h>
 #import <netdb.h>
 #import <fcntl.h>
+#import <unistd.h>
+
+/* Too bad Objective-C doesn't have class variables... */
+static NSMutableSet *servsockets;
 
 @interface ITInetServerSocket(Private)
--(void)setupConnectionWithServiceName:(NSString*)name;
--(void)setupConnectionWithPortNumber:(NSNumber*)port;
--(void)setupRendezvousAdvertising:(NSString*)name;
--(void)setupTimer;
--(void)timerFunc:(NSTimer*)timer;
++(void)registerSocket:(ITInetServerSocket*)sock;
++(void)unregisterSocket:(ITInetServerSocket*)sock;
+-(short)lookupPortForServiceType:(NSString*)name;
+-(void)setupConnection;
+-(void)stopConnection;
+-(void)setupRendezvousAdvertising;
+-(void)stopRendezvousAdvertising;
+-(void)setupThread;
+-(void)stopThread;
+-(void)newClient:(int)cfd;
+-(void)socketAcceptLoop:(id)data;
 @end
 
 @implementation ITInetServerSocket
-- (id)init
++ (void)initialize
 {
-    if (self = [super init])
-          {
-          sockfd = 0;
-          delegate = clients = nil;
-          }
-    return self;
+    servsockets = [[NSMutableSet alloc] init];
+    //[self setupTimer];
 }
 
-- (id)initWithServiceName:(NSString*)name delegate:(id)d
+- (id)init
 {
     if (self = [super init])
           {
-          delegate = [d retain];
+          sockfd = -1;
+          delegate = nil;
           clients = [[NSMutableSet alloc] init];
-          [self setupConnectionWithServiceName:name];
+          service = nil;
+          port = 0;
+          dieflag = 0;
+          rndType = rndName = nil;
+          timer = nil;
           }
     return self;
 }
 
-- (id)initWithPort:(NSNumber*)port rendezvousName:(NSString*)name delegate:(id)d
+- (id)initWithDelegate:(id)d
 {
     if (self = [super init])
           {
+          sockfd = -1;
           delegate = [d retain];
           clients = [[NSMutableSet alloc] init];
-          [self setupConnectionWithPortNumber:port];
+          service = nil;
+          port = 0;
+          dieflag = 0;
+          rndType = rndName = nil;
+          timer = nil;
           }
     return self;
 }
 
 - (void)dealloc
 {
-    [service stop];
-    [service release];
+    [self stopConnection];
     [clients release];
     [delegate release];
-    shutdown(sockfd,2);
+    [rndName release];
+    [rndType release];
+}
+
+- (BOOL)start
+{
+    if (!rndName || !rndType || !port) return NO;
+    [ITInetServerSocket registerSocket:self];
+    return YES;
 }
 
 - (int)sockfd
 {
     return delegate;
 }
+
+- (short)port
+{
+    return port;
+}
+
+- (void)stop
+{
+    [ITInetServerSocket unregisterSocket:self];
+}
+
+- (void)setServiceType:(NSString*)type useForPort:(BOOL)p
+{
+    rndType = [type retain];
+    if (p) {
+          port = [self lookupPortForServiceType:type];
+    }
+}
+
+- (void)setServiceName:(NSString*)name
+{
+    rndName = [name retain];
+}
+
+- (void)setPort:(short)p
+{
+    port = p;
+}
 @end
 
 @implementation ITInetServerSocket(Private)
--(void)setupConnectionWithServiceName:(NSString*)name
++(void)registerSocket:(ITInetServerSocket*)sock
+{
+    [sock setupConnection];
+    [servsockets addObject:sock];
+}
+
++(void)unregisterSocket:(ITInetServerSocket*)sock
+{
+    [sock stopConnection];
+    [servsockets removeObject:sock];
+}
+
+-(short)lookupPortForServiceType:(NSString*)name
 {
     const char *_name = [name cString];
-    struct addrinfo hints,*res;
+    struct addrinfo *res;
+    short p;
+    getaddrinfo(NULL,_name,NULL,&res);
+    p = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
+    freeaddrinfo(res);
+    return p;
+}
 
+-(void)setupConnection
+{
+    struct addrinfo hints, *ai;
     hints.ai_flags = AI_PASSIVE;
-    hints.ai_family = PF_INET;
+    hints.ai_family = PF_INET6;
     hints.ai_socktype = SOCK_STREAM;
     hints.ai_protocol = IPPROTO_TCP;
     hints.ai_addrlen = 0;
-    hints.ai_canonname = NULL;
-    hints.ai_addr = NULL;
-    hints.ai_next = NULL;
-
-    getaddrinfo(NULL,_name,&hints,&res);
-    sockfd = socket(res->ai_family, res->ai_socktype,res->ai_protocol);
-    bind(sockfd, res->ai_addr, res->ai_addrlen);
+    hints.ai_canonname = hints.ai_addr = hints.ai_next = NULL;
+    getaddrinfo(NULL,[[[NSNumber numberWithShort:port] stringValue] cString],&hints,&ai);
+    bind(sockfd,ai->ai_addr,ai->ai_addrlen);
     listen(sockfd, SOMAXCONN);
-    fcntl(sockfd,F_SETFL,O_NONBLOCK);
-    freeaddrinfo(res);
+    freeaddrinfo(ai);
+    [self setupRendezvousAdvertising];
+    [self setupThread];
 }
 
--(void)setupConnectionWithPortNumber:(NSNumber*)port
+- (void)stopConnection
 {
-    short _port = [port shortValue];
-    struct sockaddr_in sa;
-
-    sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
-    sa.sin_addr.s_addr = INADDR_ANY;
-    sa.sin_family = AF_INET;
-    sa.sin_port = htons(_port);
-    bind(sockfd,(struct sockaddr *)&sa,sizeof(sa));
-    listen(sockfd, SOMAXCONN);
-    fcntl(sockfd,F_SETFL,O_NONBLOCK);
+    [self stopRendezvousAdvertising];
+    [clients makeObjectsPerformSelector:@selector(disconnect)];
+    shutdown(sockfd,2);
+    close(sockfd);
+    sockfd = -1;
 }
 
-- (void)setupRendezvousAdvertising:(NSString*)name port:(NSNumber*)port
+- (void)setupRendezvousAdvertising
 {
-    service = [[NSNetService alloc] initWithDomain:@"" type:[NSString stringWithFormat:@"_%@._tcp.",name] port:htons([port shortValue])];
+    NSString *a = [NSString stringWithFormat:@"_%@._tcp.",rndType];
+    service = [[NSNetService alloc] initWithDomain:@"" type:a name:rndName port:htons(port)];
+    NSLog(@"Advertising a service of type %@ name %@",a,rndName);
     [service publish];
 }
 
-- (void)setupTimer
+- (void)stopRendezvousAdvertising
 {
-    NSTimer *timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(timerFunc:) userInfo:nil repeats:YES];
-    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
+    [service stop];
+    [service release];
+    service = nil;
 }
 
-- (void)timerFunc:(NSTimer*)timer
+- (void)setupThread
 {
-    struct sockaddr_in csa;
-    int csalen;
-    signed int cfd;
-    (void) timer;
-    cfd = accept(sockfd,(struct sockaddr*)&csa,&csalen);
-    if (cfd == -1) {
-          if (errno == EWOULDBLOCK) return;
-          else {perror("Too bad I haven't implemented error checking yet");}
-          }
-          else {
-                  ITInetSocket *csocket = [[[ITInetSocket alloc] initWithFD:cfd delegate:self] autorelease];
-                  [clients addObject:csocket];
-                  [delegate newClientJoined:csocket];
-                  
+    NSPort *p1 = [NSPort port], *p2 = [NSPort port];
+    NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:p1 sendPort:p2];
+    NSArray *par = [NSArray arrayWithObjects:p2,p1,nil];
+    [dcon setRootObject:self];
+    NSLog(@"detached server thread");
+    [NSThread detachNewThreadSelector:@selector(socketAcceptLoop:) toTarget:self withObject:par]; 
+}
+
+- (void)stopThread
+{
+    dieflag = 1;
+}
+
+- (void)newClient:(int)cfd
+{
+    ITInetSocket *csocket = [[ITInetSocket alloc] initWithFD:cfd delegate:delegate];
+    [clients addObject:csocket];
+}
+
+- (void)socketAcceptLoop:(id)data
+{
+    NSAutoreleasePool *ap = [[NSAutoreleasePool alloc] init];
+    NSArray *par = data;
+    NSConnection *dcon = [[NSConnection alloc] initWithReceivePort:[par objectAtIndex:0] sendPort:[par objectAtIndex:1]];
+    NSProxy *dp = [dcon rootProxy];
+    while ((sockfd != -1) && !dieflag)
+          {
+          signed int cfd;
+          cfd = accept(sockfd,NULL,NULL);
+          NSLog(@"Someone connected!");
+          [(id)dp newClient:cfd];
           }
+    dieflag = 0;
+    [dcon release];
+    [ap release];
 }
 @end
\ No newline at end of file