Fixing server sockets a little bit more.
[ITFoundation.git] / ITInetServerSocket.m
index dcc8805..f7ff228 100755 (executable)
@@ -9,60 +9,82 @@
 #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)registerSocket:(ITInetServerSocket*)sock;
++(void)unregisterSocket:(ITInetServerSocket*)sock;
+-(short)lookupPortForServiceType:(NSString*)name;
+-(void)setupConnection;
+-(void)stopConnection;
+-(void)setupRendezvousAdvertising;
+-(void)stopRendezvousAdvertising;
 -(void)setupTimer;
+-(void)stopTimer;
++(void)globalTimerFunc:(NSTimer*)timer;
 -(void)timerFunc:(NSTimer*)timer;
 @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;
+          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;
+          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;
+    short p;
 
     hints.ai_flags = AI_PASSIVE;
     hints.ai_family = PF_INET;
     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);
-    listen(sockfd, SOMAXCONN);
-    fcntl(sockfd,F_SETFL,O_NONBLOCK);
+    p = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
     freeaddrinfo(res);
+    return p;
 }
 
--(void)setupConnectionWithPortNumber:(NSNumber*)port
+-(void)setupConnection
 {
-    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);
+    sa.sin_port = htons(port);
     bind(sockfd,(struct sockaddr *)&sa,sizeof(sa));
     listen(sockfd, SOMAXCONN);
     fcntl(sockfd,F_SETFL,O_NONBLOCK);
+    [self setupRendezvousAdvertising];
+    [self setupTimer];
+}
+
+- (void)stopConnection
+{
+    [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])];
+    service = [[NSNetService alloc] initWithDomain:@"" type:[NSString stringWithFormat:@"_%@._tcp.",rndType] name:rndName port:htons(port)];
     [service publish];
 }
 
+- (void)stopRendezvousAdvertising
+{
+    [service stop];
+    [service release];
+    service = nil;
+}
+
 - (void)setupTimer
 {
-    NSTimer *timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(timerFunc:) userInfo:nil repeats:YES];
+    if (!timer) timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(timerFunc:) userInfo:nil repeats:YES];
     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
 }
 
+- (void)stopTimer
+{
+    [timer invalidate];
+    [timer release];
+    timer = nil;
+}
+
++ (void)globalTimerFunc:(NSTimer*)timer
+{
+    [servsockets makeObjectsPerformSelector:@selector(timerFunc)];
+}
+
 - (void)timerFunc:(NSTimer*)timer
 {
-    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");}
+    if (sockfd != -1)
+          {
+          struct sockaddr_in csa;
+          int csalen;
+          signed int cfd;
+          cfd = accept(sockfd,(struct sockaddr*)&csa,&csalen);
+          if (cfd == -1) {
+                 if (errno == EWOULDBLOCK) ;
+                 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];
-                  
+                 ITInetSocket *csocket = [[[ITInetSocket alloc] initWithFD:cfd delegate:self] autorelease];
+                 [clients addObject:csocket];
+                 [delegate newClientJoined:csocket];
+          }
           }
 }
 @end
\ No newline at end of file