#import <sys/socket.h>
#import <netdb.h>
#import <fcntl.h>
+#import <unistd.h>
+
+/* Too bad Objective-C doesn't have class variables... */
+static NSMutableDictionary *servsockets;
+static NSTimer *timer;
@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)setupTimer;
++(void)stopTimer;
++(void)globalTimerFunc:(NSTimer*)timer;
+-(BOOL)timerFunc;
@end
@implementation ITInetServerSocket
-- (id)init
++ (void)initialize
{
- if (self = [super init])
- {
- sockfd = 0;
- delegate = clients = nil;
- }
- return self;
+ servsockets = [[NSMutableDictionary 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;
}
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;
}
return self;
}
- (void)dealloc
{
- [service stop];
- [service release];
+ [self stopConnection];
[clients release];
[delegate release];
- shutdown(sockfd,2);
+ [rndName release];
+ [rndType release];
+}
+
+- (BOOL)registerSocket
+{
+ 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 setObject:sock forKey:[NSString stringWithFormat:@"%lu",[sock port]]];
+}
+
++(void)unregisterSocket:(ITInetServerSocket*)sock
+{
+ [sock stopConnection];
+ [servsockets removeObjectForKey:[NSString stringWithFormat:@"%lu",[sock port]]];
+}
+
+-(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];
}
-- (void)setupRendezvousAdvertising:(NSString*)name port:(NSNumber*)port
+- (void)stopConnection
{
- service = [[NSNetService alloc] initWithDomain:@"" type:[NSString stringWithFormat:@"_%@._tcp.",name] port:htons([port shortValue])];
+ [self stopRendezvousAdvertising];
+ [clients makeObjectsPerformSelector:@selector(disconnect)];
+ shutdown(sockfd,2);
+ close(sockfd);
+ sockfd = -1;
+}
+
+- (void)setupRendezvousAdvertising
+{
+ service = [[NSNetService alloc] initWithDomain:@"" type:[NSString stringWithFormat:@"_%@._tcp.",rndType] name:rndName port:htons(port)];
[service publish];
}
-- (void)setupTimer
+- (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(globalTimerFunc:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
-- (void)timerFunc:(NSTimer*)timer
++ (void)stopTimer
+{
+ [timer invalidate];
+ [timer release];
+ timer = nil;
+}
+
++ (void)globalTimerFunc:(NSTimer*)timer
+{
+ NSEnumerator *enume = [servsockets objectEnumerator];
+ id obj;
+
+ while (obj = [enume nextObject])
+ {
+ [obj timerFunc];
+ }
+}
+
+- (BOOL)timerFunc
{
- 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) return NO;
+ 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];
+ return YES;
+ }
}
+ return NO;
}
@end
\ No newline at end of file