RSS

(root)/iphone/common : /source/SocketMessenger.m (revision 61)

To get this branch, use:
bzr branch /browse/iphone/common
Line Revision Contents
1 52
//
2
//  SocketMessenger.m
3
//  tappity
4
//
5
//  Created by döme on 30.10.2009.
6 54
7 52
//
8
9 54
/*
10
 * Copyright (c) 2009 Doemoetoer Gulyas.
11
 * All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions
15
 * are met:
16
 * 1. Redistributions of source code must retain the above copyright
17
 *    notice, this list of conditions and the following disclaimer.
18
 * 2. Redistributions in binary form must reproduce the above copyright
19
 *    notice, this list of conditions and the following disclaimer in the
20
 *    documentation and/or other materials provided with the distribution.
21
 * 3. The name of the author may not be used to endorse or promote products
22
 *    derived from this software without specific prior written permission.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
 */
35
36 52
#include <unistd.h>
37
#include <stdint.h>
38
#include <sys/socket.h>
39
#include <fcntl.h>
40
#include <termios.h>
41
#include <netinet/in.h>
42
#include <arpa/inet.h>
43
#include <sys/types.h>
44
#include <netdb.h>
45
#include <netinet/in.h>
46
#include <sys/time.h>
47
48
#import "SocketMessenger.h"
49
50
NSString* SocketMessageIdKey	= @"SocketMessageId";
51
NSString* SocketMessageDataKey	= @"SocketMessageData";
52 55
NSString* SocketMessengerKey	= @"SocketMessenger";
53 52
54 55
//const int kSocketMessengerTerminateMsg = -1;
55 53
56
57 52
@interface SocketMessenger (Private)
58
- (void) startCommsWorkThreads;
59 53
- (void) startWatchdogThread;
60
- (void) startListening;
61 52
@end
62
63
@implementation SocketMessenger
64
65
- (id) init
66
{
67
	if (!(self = [super init]))
68
		return nil;
69
		
70
	commsSocket = -1;
71 53
	server.listenSocket = -1;
72
	
73
	automaticallyReconnect = YES;
74 52
	
75
	sendLock = [[NSCondition alloc] init];
76 53
	sendQueue = [[NSMutableArray alloc] init];
77 52
		
78
	return self;
79
}
80
81 55
- (BOOL) isConnected
82
{
83
	return commsSocket != -1;
84
}
85
86 52
- (BOOL) threadActive: (id) key
87
{
88
	return [[activeThreads objectForKey: key] boolValue];
89
}
90
91
- (void) threadWillExit: (id) key
92
{
93
	@synchronized (self)
94
	{
95
		[activeThreads removeObjectForKey: key];
96
		
97
		if (![activeThreads count])
98
		{
99
			close(commsSocket);
100
			commsSocket = -1;
101
			
102
			
103
		}
104
	}
105
}
106
107
- (void) receivingThread: (id) info
108
{
109
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
110
111
	@synchronized(self)
112
	{
113
		[self retain];
114
	}
115
116 53
	while (rxThreadShouldRun)
117 52
	{
118
		struct timeval tv;
119
		fd_set readfds;
120
		fd_set writefds;
121
		fd_set errorfds;
122
		int socket = -1;
123
		int maxSocket = -1;
124
		
125
		@synchronized(self)
126
		{
127
			socket = commsSocket;
128
		}
129
130
		tv.tv_sec = 1;
131
		tv.tv_usec = 0;
132
133
		FD_ZERO(&readfds);
134
		FD_ZERO(&writefds);
135
		FD_ZERO(&errorfds);
136
137
		FD_SET(socket, &readfds);
138
		FD_SET(socket, &errorfds);
139
		maxSocket = MAX(maxSocket, socket);
140
141
		if (select(maxSocket+1, &readfds, &writefds, &errorfds, &tv) < 0)
142
		{
143
			perror("select");
144 53
			goto SELECT_ERR;
145 52
		}
146 53
		
147
		if (FD_ISSET(socket, &errorfds))
148
			goto SELECT_ERR;
149
150 52
151
		if (FD_ISSET(socket, &readfds))
152
		{
153
			BOOL messageFinished = NO;
154
			//NSLog(@"receiving...");
155
			if (!expectedMessageSize)
156
			{
157
				uint32_t header[2] = {0,0};
158
				int actuallyRead = 0;
159
			
160
				actuallyRead = recv(socket, header, 8, MSG_PEEK);
161
				
162
				if (actuallyRead == 8)
163
				{
164
					actuallyRead = recv(socket, header, 8, 0);
165
					expectedMessageSize = ntohl(header[1]);
166
					currentMessageId = ntohl(header[0]);
167
					currentData = [[NSMutableData alloc] initWithLength: expectedMessageSize];
168
				}
169
				else if (actuallyRead == -1)
170
				{
171
					if (errno != ETIMEDOUT)
172
					{
173
						printf("Connection dropped with error.\n");
174 53
						goto RECV_ERR;
175 52
					}
176
				}
177
				else if (actuallyRead == 0)
178
				{
179
					printf("remote socket closed.\n");
180 53
					goto RECV_ERR;
181 52
				}
182
				
183
				if (!expectedMessageSize)
184
					messageFinished = YES;
185
			}
186
			else
187
			{
188
				size_t readAmount = expectedMessageSize - currentlyRead;
189
				int actuallyRead = 0;
190
			
191
				actuallyRead = recv(socket, [currentData mutableBytes] + currentlyRead, readAmount, 0);
192
				if (actuallyRead == -1)
193
				{
194
					printf("Connection dropped with error.\n");
195 53
					goto RECV_ERR;
196 52
				}
197
				else if (actuallyRead == 0)
198
				{
199
					printf("remote socket closed.\n");
200 53
					goto RECV_ERR;
201 52
				}
202
203
				currentlyRead += actuallyRead;
204
				
205
				if (currentlyRead == expectedMessageSize)
206
					messageFinished = YES;
207
			}
208
209
			if (messageFinished)
210
			{
211 53
				if (currentMessageId == kSocketMessengerTerminateMsg)
212
				{
213
					break;
214
				}
215 52
				@synchronized(self)
216
				{
217
					expectedMessageSize = 0;
218
					/*
219
					if (!receivedPackets)
220
						receivedPackets = [[NSMutableArray alloc] init];
221
					[receivedPackets addObject: currentData];
222
					[currentData release];
223
					*/
224
					
225
				}
226 53
				
227
				//NSLog(@"dataReceived (%d) #%d", (int) [currentData length], currentMessageId);
228 52
229
				if (receiveDataOnMainThread)
230 55
					[delegate performSelectorOnMainThread: @selector(dataReceived:) withObject: [NSDictionary dictionaryWithObjectsAndKeys: self, SocketMessengerKey, [NSNumber numberWithInt: currentMessageId], SocketMessageIdKey, currentData, SocketMessageDataKey, nil] waitUntilDone: NO];
231 52
				else
232 55
					[delegate dataReceived: [NSDictionary dictionaryWithObjectsAndKeys: self, SocketMessengerKey, [NSNumber numberWithInt: currentMessageId], SocketMessageIdKey, currentData, SocketMessageDataKey, nil]];
233 52
234
				currentData = nil;
235
				currentlyRead = 0;
236
			}
237
238
		}
239 53
240
		continue;
241
242
RECV_ERR:
243
SELECT_ERR:
244
		close(commsSocket);
245
		self->commsSocket = -1;
246
		break;
247
248 52
	}
249
	
250 53
	rxThreadActive = 0;
251
	
252 52
	[self threadWillExit: info];
253
254
	@synchronized(self)
255
	{
256
		[self release];
257
	}
258
259
	[info release];
260
	[pool drain];
261
}
262
263
264 53
- (void) runThreadWithTarget: (id) target selector: (SEL) selector
265 52
{
266
	@synchronized(self)
267
	{
268
		if (!activeThreads)
269
			activeThreads = [[NSMutableDictionary alloc] init];
270
		id number = [NSNumber numberWithInt: threadIds++];
271
272
		[activeThreads setObject: [NSNumber numberWithBool: YES] forKey: number];
273 53
		[NSThread detachNewThreadSelector: selector toTarget: target withObject: [number retain]];
274 52
	}
275
}
276
277
- (void) sendData: (NSData*) tapData withIdentifier: (NSInteger) messageId
278
{		
279
	[sendLock lock];
280
281
	if (!sendQueue)
282
		sendQueue = [[NSMutableArray alloc] init];
283
284
	[sendQueue addObject: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger: messageId], SocketMessageIdKey, tapData, SocketMessageDataKey, nil]];
285
	
286
	[sendLock signal];
287
	[sendLock unlock];
288
		
289
}
290
291
292
- (void) sendingThread: (id) info
293
{
294 53
//	pthread_setname_np("sendingThread");
295
296 52
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
297
298
	@synchronized(self)
299
	{
300
		[self retain];
301
	}
302
303 53
	while (txThreadShouldRun)
304 52
	{
305
		[sendLock lock];
306 55
		while (![sendQueue count] && txThreadShouldRun)
307
			[sendLock wait];
308 52
				
309 55
		NSDictionary* dict = nil;
310
		if ([sendQueue count])
311
		{
312
			dict = [[sendQueue objectAtIndex: 0] retain];
313
			[sendQueue removeObjectAtIndex: 0];
314
		}
315 52
		
316
		[sendLock unlock];
317 53
		
318 55
		if (dict)
319 52
		{
320 55
			NSData* data = [dict objectForKey: SocketMessageDataKey];
321
			uint32_t messageId = [[dict objectForKey: SocketMessageIdKey] intValue];
322
323
			size_t sizeToSend = 8;
324
			size_t dataSent = 0;
325
			uint32_t header[2] = {htonl(messageId), htonl([data length])};
326
			int err = 0;
327
			int socket = -1;
328
329
			@synchronized(self)
330 52
			{
331 55
				socket = commsSocket;
332 52
			}
333 55
			
334
			//printf("sending %d bytes\n", (int) sizeToSend);
335
336 52
			while (dataSent < sizeToSend)
337
			{
338 55
				if ((err = send(socket, header + dataSent, sizeToSend - dataSent, 0)) == -1)
339 52
				{
340
					perror("send");
341 53
					goto SEND_ERR;
342 52
				}
343
				else
344
					dataSent += err;
345 55
			}
346
			
347
			if (err != -1)
348
			{
349
				sizeToSend = [data length];
350
				dataSent = 0;
351
				while (dataSent < sizeToSend)
352
				{
353
					if ((err = send(socket, [data bytes] + dataSent, sizeToSend - dataSent, 0)) == -1)
354
					{
355
						perror("send");
356
						goto SEND_ERR;
357
					}
358
					else
359
						dataSent += err;
360
						
361
				}
362
			}
363
			
364
			if (err == -1)
365
				goto SEND_ERR;
366
367
			[dict release];
368 52
		}
369 53
		continue;
370
371
SEND_ERR:
372
		close(commsSocket);
373
		commsSocket = -1;
374
		[dict release];
375
		break;
376 52
	}
377
	
378 53
	txThreadActive = 0;
379
	
380 52
	[self threadWillExit: info];
381
382
	@synchronized(self)
383
	{
384
		[self release];
385
	}
386
387
	[info release];
388
	[pool drain];
389
}
390
391 53
- (void) connectToRemoteService
392 52
{
393 53
	NSInteger	port = [remoteService port];
394
	NSString*	hostName = [remoteService hostName];
395 52
	
396
	NSLog(@"SocketMessenger attempting to connect to %@ : %d", hostName, port);
397
398
	int sockfd = 0;
399
400
401
//	_setStandardSocketOpts(socket);
402
403
	struct addrinfo hints, *servinfo = NULL, *p = NULL;
404
	int rv = 0;
405
406
	memset(&hints, 0, sizeof hints);
407
	hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
408
	hints.ai_socktype = SOCK_STREAM;
409
	//hints.ai_protocol = IPPROTO_TCP;
410
	hints.ai_flags    = AI_PASSIVE;
411
	
412
//	NSHost* host = [NSHost hostWithName: hostName];
413
414
	if ((rv = getaddrinfo([hostName UTF8String], [[NSString stringWithFormat: @"%d", port] UTF8String], &hints, &servinfo)) != 0)
415
	{
416
		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
417
		exit(1);
418
	}
419
420
	// loop through all the results and connect to the first we can
421
	for(p = servinfo; p != NULL; p = p->ai_next)
422
	{
423
		if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
424
		{
425
			perror("socket");
426
			continue;
427
		}
428
429
		if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
430
		{
431
			close(sockfd);
432
			perror("connect");
433
			continue;
434
		}
435
436
		break; // if we get here, we must have connected successfully
437
	}
438
439
	if (p == NULL) {
440
		// looped off the end of the list with no connection
441
		fprintf(stderr, "failed to connect\n");
442
		exit(2);
443
	}
444
445
	freeaddrinfo(servinfo); // all done with this structure
446 53
	
447
	assert(sockfd != -1);
448 52
449
	NSLog(@"SocketMessenger connected");
450
451
452
	self->commsSocket = sockfd;
453
	
454
	[self startCommsWorkThreads];
455 53
	[self startWatchdogThread];
456 52
		
457
}
458
459 53
- (void) connectToService: (NSNetService*) service
460
{
461
	[service retain];
462
	[remoteService release];
463
	remoteService = service;
464
	[self connectToRemoteService];
465
}
466
467
- (void) stopCommsWorkThreads
468
{
469
	while (rxThreadActive || txThreadActive)
470
	{
471
		rxThreadShouldRun = 0;
472
		txThreadShouldRun = 0;
473
		[sendLock lock];
474
		[sendLock signal];
475
		[sendLock unlock];
476
		usleep(10000);
477
	}
478
}
479
480
- (void) stopWatchdogThread
481
{
482
	while (wdThreadActive)
483
	{
484
		wdThreadShouldRun = 0;
485
		usleep(10000);
486
	}
487
}
488
489
- (void) startWatchdogThread
490
{
491
	//[self stopWatchdogThread];
492
493
	if (!wdThreadActive)
494
	{
495
		wdThreadShouldRun = 1;
496
		@synchronized(self)
497
		{
498
			wdThreadActive++;
499
		}
500
		[self runThreadWithTarget: self selector: @selector(watchdogThread:)];
501
	}
502
}
503
504
- (void) stopAcceptThread
505
{
506
	while (server.acThreadActive)
507
	{
508
		server.acThreadShouldRun = 0;
509
		usleep(10000);
510
	}
511
}
512
513
- (void) startAcceptThread
514
{
515
	[self stopAcceptThread];
516
517
	server.acThreadShouldRun = 1;
518
	server.acThreadActive = 1;
519
520
	[self runThreadWithTarget: self selector: @selector(acceptThread:)];
521
}
522
523
524 52
- (void) startCommsWorkThreads
525
{
526 53
	[self stopCommsWorkThreads];
527
	
528
	rxThreadShouldRun = 1;
529
	rxThreadActive = 1;
530
	txThreadShouldRun = 1;
531
	txThreadActive = 1;
532
533
	[self runThreadWithTarget: self selector: @selector(sendingThread:)];
534
	[self runThreadWithTarget: self selector: @selector(receivingThread:)];
535
	
536
	if ([delegate respondsToSelector: @selector(connectionWasEstablished:)])
537
		[delegate performSelectorOnMainThread: @selector(connectionWasEstablished:) withObject: self waitUntilDone: NO];
538 52
}
539
540
static void _setStandardSocketOpts(int socket)
541
{
542
    int yes = 1;
543
    setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
544
	int timeout = 2000;
545
	setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
546
	setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
547
}
548
549
550
- (void) acceptThread: (id) info
551
{
552
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
553
554
	@synchronized(self)
555
	{
556
		[self retain];
557
	}
558
559 53
	while (server.acThreadShouldRun)
560 52
	{
561
562
		struct timeval tv;
563
		fd_set readfds;
564
		fd_set writefds;
565
		fd_set errorfds;
566
		int maxSocket = -1;
567
		
568
		int lsock = server.listenSocket;
569
570
		tv.tv_sec = 1;
571
		tv.tv_usec = 0;
572
573
		FD_ZERO(&readfds);
574
		FD_ZERO(&writefds);
575
		FD_ZERO(&errorfds);
576
577
		FD_SET(lsock, &readfds);
578
		FD_SET(lsock, &errorfds);
579
		maxSocket = MAX(maxSocket, lsock);
580 53
				
581 52
		//NSLog(@"listening for connection...");
582
583
		if (select(maxSocket+1, &readfds, NULL, &errorfds, &tv) < 0)
584
		{
585
			perror("select");
586 53
			goto SELECT_ERR;
587 52
			break;
588
		}
589
590
		if (FD_ISSET(lsock, &readfds))
591
		{
592
			NSLog(@"Accepting connection...");
593
			// accept
594
			socklen_t	sinSize = sizeof(struct sockaddr_in);
595
			struct sockaddr_in	peerAddress;
596
			int newSocket = accept(lsock, (struct sockaddr *)&peerAddress, &sinSize);
597
			
598
			_setStandardSocketOpts(newSocket);
599
600
			
601
			if (newSocket == -1)
602
			{
603
				if (errno == EWOULDBLOCK)
604
				{
605
				}
606
				else
607
				{
608
					printf("Error accepting connection.\n");
609 53
					goto ACCEPT_ERR;
610 52
					break;
611
				}
612
			}
613
			
614
			@synchronized(self)
615
			{
616
				if (commsSocket)
617
					close(commsSocket);
618
619
				// add socket to sockets list
620
				commsSocket = newSocket;
621
			}
622
			
623
			[server.netService stop];
624
			[server.netService release];
625
			server.netService = nil;
626
			
627 53
			close(server.listenSocket);
628
			server.listenSocket = -1;
629
			
630 52
			[self startCommsWorkThreads];
631
			
632
			NSLog(@"Accepted connection.");
633 53
			
634
			goto ACCEPT_SUCCESS;
635
		}
636
		
637
		continue;
638
639
ACCEPT_ERR:
640
SELECT_ERR:
641
		close(server.listenSocket);
642
		server.listenSocket = -1;
643
		break;
644
645
ACCEPT_SUCCESS:
646
		break;
647
	}
648
	
649
	server.acThreadActive = 0;
650
651
	@synchronized(self)
652
	{
653
		[self release];
654
	}
655
656
	[info release];
657
	[pool drain];
658
}
659
660
- (void) watchdogThread: (id) info
661
{
662
	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
663
664
	@synchronized(self)
665
	{
666
		[self retain];
667
	}
668
669
	while (wdThreadShouldRun)
670
	{
671
		if (isServing)
672
		{
673
			if ((commsSocket == -1) && (server.listenSocket == -1))
674
			{
675
				NSLog(@"watchdog noticed connection error");
676
				if (automaticallyReconnect)
677
				{
678
					NSLog(@"restarting server");
679
					[self stopCommsWorkThreads];
680
					[self startListening];
681
				}
682
				else 
683
				{
684
					if ([delegate respondsToSelector: @selector(connectionWasTerminated:)])
685
						[delegate performSelectorOnMainThread: @selector(connectionWasTerminated:) withObject: self waitUntilDone: NO];
686
					break;
687
				}
688
			}
689
		}
690
		else
691
		{
692
			if (commsSocket == -1)
693
			{
694
				NSLog(@"watchdog noticed connection error");
695
				if (automaticallyReconnect)
696
				{
697
					NSLog(@"restarting client");
698
					[self stopCommsWorkThreads];
699
					[self connectToRemoteService];
700
				}
701
				else 
702
				{
703
					if ([delegate respondsToSelector: @selector(connectionWasTerminated:)])
704
						[delegate performSelectorOnMainThread: @selector(connectionWasTerminated:) withObject: self waitUntilDone: NO];
705
					break;
706
				}
707
			}
708
		}
709
		usleep(1000000);
710
		continue;
711
712
WATCH_ERR:
713
		assert(0);
714
		break;
715
716
	}
717
718
	@synchronized(self)
719
	{
720
		wdThreadActive--;
721
	}
722
	
723 52
724
	@synchronized(self)
725
	{
726
		[self release];
727
	}
728
729
	[info release];
730
	[pool drain];
731
}
732
733
734
- (BOOL) enableBonjourWithDomain:(NSString*)domain applicationProtocol:(NSString*)protocol name:(NSString*)name 
735
{
736
	if(![domain length])
737
		domain = @""; //Will use default Bonjour registration doamins, typically just ".local"
738
	if(![name length])
739
		name = @""; //Will use default Bonjour name, e.g. the name assigned to the device in iTunes
740
	
741
	assert([protocol length] && server.listenSocket);
742
	
743 53
	NSLog(@"tappity port: %d", server.portnum);
744 52
745
	[server.netService stop];
746
	[server.netService release];
747
748 53
	server.netService = [[NSNetService alloc] initWithDomain: domain type: protocol name: name port: server.portnum];
749 52
	if(server.netService == nil)
750
		return NO;
751 55
752 53
	[server.netService setDelegate: self];
753 52
//	[server.netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
754
	[server.netService publish];
755
	
756
	return YES;
757
}
758
759
760 53
761 52
- (void) startListening
762
{
763 53
	struct sockaddr_in myAddress;
764
	memset(&myAddress, 0, sizeof(myAddress));
765
766
	myAddress.sin_family = AF_INET;					// host byte order
767
	myAddress.sin_port = htons(server.portnum);		// short, network byte order, any port
768
	myAddress.sin_addr.s_addr = htonl(INADDR_ANY);	// auto-fill with my IP
769 52
770
	server.listenSocket = socket(PF_INET, SOCK_STREAM, 0);
771
	assert(server.listenSocket != -1);
772
773
	_setStandardSocketOpts(server.listenSocket);
774
775 53
	int err = bind(server.listenSocket, (struct sockaddr *)&myAddress, sizeof(myAddress));
776 52
	assert(-1 != err);
777
	
778
	err = listen(server.listenSocket, 1);
779
	assert(-1 != err);
780
781 53
	[self startAcceptThread];
782
	[self startWatchdogThread];
783 52
	
784
	if([self enableBonjourWithDomain: @"" applicationProtocol: server.protocolName name: server.serviceName])
785
		NSLog(@"tappity bounjour advertisments up and running");
786
787
}
788
789
- (void) startBonjourServerWithName: (NSString*) name protocol: (NSString*) protocol port: (int) pnum;
790
{
791
	NSLog(@"starting tappity server");
792
	
793
	[server.serviceName release];
794
	server.serviceName = [name retain];
795
	
796
	
797
	[server.protocolName release];
798
	server.protocolName = [protocol retain];
799
	
800
	server.portnum = pnum;
801 53
802
	isServing = YES;
803 52
	
804
	[self startListening];
805
806
}
807
808 53
- (void) terminateConnection
809
{
810
	[self stopWatchdogThread];
811
	[self stopAcceptThread];
812
	[self stopCommsWorkThreads];
813
}
814
815 52
- (void) dealloc
816
{
817 53
818 52
	[server.serviceName release];
819
820
	[server.netService stop];
821
	[server.netService release];
822
823
	[activeThreads release];
824
	[sendQueue release];
825
	[sendLock release];
826
827 53
	[remoteService release];
828
829 52
	[super dealloc];
830
}
831
832 53
@synthesize delegate, receiveDataOnMainThread, automaticallyReconnect;
833 52
834
@end

Loggerhead 1.17 is a web-based interface for Bazaar branches