Fixing server sockets a little bit more.
[ITFoundation.git] / ITInetServerSocket.m
1 //
2 //  ITInetServerSocket.m
3 //  ITFoundation
4 //
5 //  Created by Alexander Strange on Thu Feb 13 2003.
6 //  Copyright (c) 2003 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "ITInetServerSocket.h"
10 #import "ITInetSocket.h"
11 #import <sys/types.h>
12 #import <sys/time.h>
13 #import <arpa/inet.h>
14 #import <netinet/in.h>
15 #import <sys/socket.h>
16 #import <netdb.h>
17 #import <fcntl.h>
18 #import <unistd.h>
19
20 /* Too bad Objective-C doesn't have class variables... */
21 static NSMutableSet *servsockets;
22
23 @interface ITInetServerSocket(Private)
24 +(void)registerSocket:(ITInetServerSocket*)sock;
25 +(void)unregisterSocket:(ITInetServerSocket*)sock;
26 -(short)lookupPortForServiceType:(NSString*)name;
27 -(void)setupConnection;
28 -(void)stopConnection;
29 -(void)setupRendezvousAdvertising;
30 -(void)stopRendezvousAdvertising;
31 -(void)setupTimer;
32 -(void)stopTimer;
33 +(void)globalTimerFunc:(NSTimer*)timer;
34 -(void)timerFunc:(NSTimer*)timer;
35 @end
36
37 @implementation ITInetServerSocket
38 + (void)initialize
39 {
40     servsockets = [[NSMutableSet alloc] init];
41     //[self setupTimer];
42 }
43
44 - (id)init
45 {
46     if (self = [super init])
47            {
48            sockfd = -1;
49            delegate = nil;
50            clients = [[NSMutableSet alloc] init];
51            service = nil;
52            port = 0;
53            rndType = rndName = nil;
54            timer = nil;
55            }
56     return self;
57 }
58
59 - (id)initWithDelegate:(id)d
60 {
61     if (self = [super init])
62            {
63            sockfd = -1;
64            delegate = [d retain];
65            clients = [[NSMutableSet alloc] init];
66            service = nil;
67            port = 0;
68            rndType = rndName = nil;
69            timer = nil;
70            }
71     return self;
72 }
73
74 - (void)dealloc
75 {
76     [self stopConnection];
77     [clients release];
78     [delegate release];
79     [rndName release];
80     [rndType release];
81 }
82
83 - (BOOL)start
84 {
85     if (!rndName || !rndType || !port) return NO;
86     [ITInetServerSocket registerSocket:self];
87     return YES;
88 }
89
90 - (int)sockfd
91 {
92     return sockfd;
93 }
94
95 - (NSSet*)clients
96 {
97     return clients;
98 }
99
100 - (id)delegate
101 {
102     return delegate;
103 }
104
105 - (short)port
106 {
107     return port;
108 }
109
110 - (void)stop
111 {
112     [ITInetServerSocket unregisterSocket:self];
113 }
114
115 - (void)setServiceType:(NSString*)type useForPort:(BOOL)p
116 {
117     rndType = [type retain];
118     if (p) {
119            port = [self lookupPortForServiceType:type];
120     }
121 }
122
123 - (void)setServiceName:(NSString*)name
124 {
125     rndName = [name retain];
126 }
127
128 - (void)setPort:(short)p
129 {
130     port = p;
131 }
132 @end
133
134 @implementation ITInetServerSocket(Private)
135 +(void)registerSocket:(ITInetServerSocket*)sock
136 {
137     [sock setupConnection];
138     [servsockets addObject:sock];
139 }
140
141 +(void)unregisterSocket:(ITInetServerSocket*)sock
142 {
143     [sock stopConnection];
144     [servsockets removeObject:sock];
145 }
146
147 -(short)lookupPortForServiceType:(NSString*)name
148 {
149     const char *_name = [name cString];
150     struct addrinfo hints,*res;
151     short p;
152
153     hints.ai_flags = AI_PASSIVE;
154     hints.ai_family = PF_INET;
155     hints.ai_socktype = SOCK_STREAM;
156     hints.ai_protocol = IPPROTO_TCP;
157     hints.ai_addrlen = 0;
158     hints.ai_canonname = NULL;
159     hints.ai_addr = NULL;
160     hints.ai_next = NULL;
161
162     getaddrinfo(NULL,_name,&hints,&res);
163     p = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port);
164     freeaddrinfo(res);
165     return p;
166 }
167
168 -(void)setupConnection
169 {
170     struct sockaddr_in sa;
171
172     sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
173     sa.sin_addr.s_addr = INADDR_ANY;
174     sa.sin_family = AF_INET;
175     sa.sin_port = htons(port);
176     bind(sockfd,(struct sockaddr *)&sa,sizeof(sa));
177     listen(sockfd, SOMAXCONN);
178     fcntl(sockfd,F_SETFL,O_NONBLOCK);
179     [self setupRendezvousAdvertising];
180     [self setupTimer];
181 }
182
183 - (void)stopConnection
184 {
185     [self stopRendezvousAdvertising];
186     [clients makeObjectsPerformSelector:@selector(disconnect)];
187     shutdown(sockfd,2);
188     close(sockfd);
189     sockfd = -1;
190 }
191
192 - (void)setupRendezvousAdvertising
193 {
194     service = [[NSNetService alloc] initWithDomain:@"" type:[NSString stringWithFormat:@"_%@._tcp.",rndType] name:rndName port:htons(port)];
195     [service publish];
196 }
197
198 - (void)stopRendezvousAdvertising
199 {
200     [service stop];
201     [service release];
202     service = nil;
203 }
204
205 - (void)setupTimer
206 {
207     if (!timer) timer = [NSTimer timerWithTimeInterval:0 target:self selector:@selector(timerFunc:) userInfo:nil repeats:YES];
208     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
209 }
210
211 - (void)stopTimer
212 {
213     [timer invalidate];
214     [timer release];
215     timer = nil;
216 }
217
218 + (void)globalTimerFunc:(NSTimer*)timer
219 {
220     [servsockets makeObjectsPerformSelector:@selector(timerFunc)];
221 }
222
223 - (void)timerFunc:(NSTimer*)timer
224 {
225     if (sockfd != -1)
226            {
227            struct sockaddr_in csa;
228            int csalen;
229            signed int cfd;
230            cfd = accept(sockfd,(struct sockaddr*)&csa,&csalen);
231            if (cfd == -1) {
232                   if (errno == EWOULDBLOCK) ;
233                   else {perror("Too bad I haven't implemented error checking yet");}
234            }
235            else {
236                   ITInetSocket *csocket = [[[ITInetSocket alloc] initWithFD:cfd delegate:self] autorelease];
237                   [clients addObject:csocket];
238                   [delegate newClientJoined:csocket];
239            }
240            }
241 }
242 @end