5
// Created by döme on 30.10.2009.
10
* Copyright (c) 2009 Doemoetoer Gulyas.
11
* All rights reserved.
13
* Redistribution and use in source and binary forms, with or without
14
* modification, are permitted provided that the following conditions
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.
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.
38
#include <sys/socket.h>
41
#include <netinet/in.h>
42
#include <arpa/inet.h>
43
#include <sys/types.h>
45
#include <netinet/in.h>
48
#import "SocketMessenger.h"
50
NSString* SocketMessageIdKey = @"SocketMessageId";
51
NSString* SocketMessageDataKey = @"SocketMessageData";
52
NSString* SocketMessengerKey = @"SocketMessenger";
54
//const int kSocketMessengerTerminateMsg = -1;
57
@interface SocketMessenger (Private)
58
- (void) startCommsWorkThreads;
59
- (void) startWatchdogThread;
60
- (void) startListening;
63
@implementation SocketMessenger
67
if (!(self = [super init]))
71
server.listenSocket = -1;
73
automaticallyReconnect = YES;
75
sendLock = [[NSCondition alloc] init];
76
sendQueue = [[NSMutableArray alloc] init];
83
return commsSocket != -1;
86
- (BOOL) threadActive: (id) key
88
return [[activeThreads objectForKey: key] boolValue];
91
- (void) threadWillExit: (id) key
95
[activeThreads removeObjectForKey: key];
97
if (![activeThreads count])
107
- (void) receivingThread: (id) info
109
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
116
while (rxThreadShouldRun)
127
socket = commsSocket;
137
FD_SET(socket, &readfds);
138
FD_SET(socket, &errorfds);
139
maxSocket = MAX(maxSocket, socket);
141
if (select(maxSocket+1, &readfds, &writefds, &errorfds, &tv) < 0)
147
if (FD_ISSET(socket, &errorfds))
151
if (FD_ISSET(socket, &readfds))
153
BOOL messageFinished = NO;
154
//NSLog(@"receiving...");
155
if (!expectedMessageSize)
157
uint32_t header[2] = {0,0};
158
int actuallyRead = 0;
160
actuallyRead = recv(socket, header, 8, MSG_PEEK);
162
if (actuallyRead == 8)
164
actuallyRead = recv(socket, header, 8, 0);
165
expectedMessageSize = ntohl(header[1]);
166
currentMessageId = ntohl(header[0]);
167
currentData = [[NSMutableData alloc] initWithLength: expectedMessageSize];
169
else if (actuallyRead == -1)
171
if (errno != ETIMEDOUT)
173
printf("Connection dropped with error.\n");
177
else if (actuallyRead == 0)
179
printf("remote socket closed.\n");
183
if (!expectedMessageSize)
184
messageFinished = YES;
188
size_t readAmount = expectedMessageSize - currentlyRead;
189
int actuallyRead = 0;
191
actuallyRead = recv(socket, [currentData mutableBytes] + currentlyRead, readAmount, 0);
192
if (actuallyRead == -1)
194
printf("Connection dropped with error.\n");
197
else if (actuallyRead == 0)
199
printf("remote socket closed.\n");
203
currentlyRead += actuallyRead;
205
if (currentlyRead == expectedMessageSize)
206
messageFinished = YES;
211
if (currentMessageId == kSocketMessengerTerminateMsg)
217
expectedMessageSize = 0;
219
if (!receivedPackets)
220
receivedPackets = [[NSMutableArray alloc] init];
221
[receivedPackets addObject: currentData];
222
[currentData release];
227
//NSLog(@"dataReceived (%d) #%d", (int) [currentData length], currentMessageId);
229
if (receiveDataOnMainThread)
230
[delegate performSelectorOnMainThread: @selector(dataReceived:) withObject: [NSDictionary dictionaryWithObjectsAndKeys: self, SocketMessengerKey, [NSNumber numberWithInt: currentMessageId], SocketMessageIdKey, currentData, SocketMessageDataKey, nil] waitUntilDone: NO];
232
[delegate dataReceived: [NSDictionary dictionaryWithObjectsAndKeys: self, SocketMessengerKey, [NSNumber numberWithInt: currentMessageId], SocketMessageIdKey, currentData, SocketMessageDataKey, nil]];
245
self->commsSocket = -1;
252
[self threadWillExit: info];
264
- (void) runThreadWithTarget: (id) target selector: (SEL) selector
269
activeThreads = [[NSMutableDictionary alloc] init];
270
id number = [NSNumber numberWithInt: threadIds++];
272
[activeThreads setObject: [NSNumber numberWithBool: YES] forKey: number];
273
[NSThread detachNewThreadSelector: selector toTarget: target withObject: [number retain]];
277
- (void) sendData: (NSData*) tapData withIdentifier: (NSInteger) messageId
282
sendQueue = [[NSMutableArray alloc] init];
284
[sendQueue addObject: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger: messageId], SocketMessageIdKey, tapData, SocketMessageDataKey, nil]];
292
- (void) sendingThread: (id) info
294
// pthread_setname_np("sendingThread");
296
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
303
while (txThreadShouldRun)
306
while (![sendQueue count] && txThreadShouldRun)
309
NSDictionary* dict = nil;
310
if ([sendQueue count])
312
dict = [[sendQueue objectAtIndex: 0] retain];
313
[sendQueue removeObjectAtIndex: 0];
320
NSData* data = [dict objectForKey: SocketMessageDataKey];
321
uint32_t messageId = [[dict objectForKey: SocketMessageIdKey] intValue];
323
size_t sizeToSend = 8;
325
uint32_t header[2] = {htonl(messageId), htonl([data length])};
331
socket = commsSocket;
334
//printf("sending %d bytes\n", (int) sizeToSend);
336
while (dataSent < sizeToSend)
338
if ((err = send(socket, header + dataSent, sizeToSend - dataSent, 0)) == -1)
349
sizeToSend = [data length];
351
while (dataSent < sizeToSend)
353
if ((err = send(socket, [data bytes] + dataSent, sizeToSend - dataSent, 0)) == -1)
380
[self threadWillExit: info];
391
- (void) connectToRemoteService
393
NSInteger port = [remoteService port];
394
NSString* hostName = [remoteService hostName];
396
NSLog(@"SocketMessenger attempting to connect to %@ : %d", hostName, port);
401
// _setStandardSocketOpts(socket);
403
struct addrinfo hints, *servinfo = NULL, *p = NULL;
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;
412
// NSHost* host = [NSHost hostWithName: hostName];
414
if ((rv = getaddrinfo([hostName UTF8String], [[NSString stringWithFormat: @"%d", port] UTF8String], &hints, &servinfo)) != 0)
416
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
420
// loop through all the results and connect to the first we can
421
for(p = servinfo; p != NULL; p = p->ai_next)
423
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
429
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
436
break; // if we get here, we must have connected successfully
440
// looped off the end of the list with no connection
441
fprintf(stderr, "failed to connect\n");
445
freeaddrinfo(servinfo); // all done with this structure
447
assert(sockfd != -1);
449
NSLog(@"SocketMessenger connected");
452
self->commsSocket = sockfd;
454
[self startCommsWorkThreads];
455
[self startWatchdogThread];
459
- (void) connectToService: (NSNetService*) service
462
[remoteService release];
463
remoteService = service;
464
[self connectToRemoteService];
467
- (void) stopCommsWorkThreads
469
while (rxThreadActive || txThreadActive)
471
rxThreadShouldRun = 0;
472
txThreadShouldRun = 0;
480
- (void) stopWatchdogThread
482
while (wdThreadActive)
484
wdThreadShouldRun = 0;
489
- (void) startWatchdogThread
491
//[self stopWatchdogThread];
495
wdThreadShouldRun = 1;
500
[self runThreadWithTarget: self selector: @selector(watchdogThread:)];
504
- (void) stopAcceptThread
506
while (server.acThreadActive)
508
server.acThreadShouldRun = 0;
513
- (void) startAcceptThread
515
[self stopAcceptThread];
517
server.acThreadShouldRun = 1;
518
server.acThreadActive = 1;
520
[self runThreadWithTarget: self selector: @selector(acceptThread:)];
524
- (void) startCommsWorkThreads
526
[self stopCommsWorkThreads];
528
rxThreadShouldRun = 1;
530
txThreadShouldRun = 1;
533
[self runThreadWithTarget: self selector: @selector(sendingThread:)];
534
[self runThreadWithTarget: self selector: @selector(receivingThread:)];
536
if ([delegate respondsToSelector: @selector(connectionWasEstablished:)])
537
[delegate performSelectorOnMainThread: @selector(connectionWasEstablished:) withObject: self waitUntilDone: NO];
540
static void _setStandardSocketOpts(int socket)
543
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
545
setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
546
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
550
- (void) acceptThread: (id) info
552
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
559
while (server.acThreadShouldRun)
568
int lsock = server.listenSocket;
577
FD_SET(lsock, &readfds);
578
FD_SET(lsock, &errorfds);
579
maxSocket = MAX(maxSocket, lsock);
581
//NSLog(@"listening for connection...");
583
if (select(maxSocket+1, &readfds, NULL, &errorfds, &tv) < 0)
590
if (FD_ISSET(lsock, &readfds))
592
NSLog(@"Accepting connection...");
594
socklen_t sinSize = sizeof(struct sockaddr_in);
595
struct sockaddr_in peerAddress;
596
int newSocket = accept(lsock, (struct sockaddr *)&peerAddress, &sinSize);
598
_setStandardSocketOpts(newSocket);
603
if (errno == EWOULDBLOCK)
608
printf("Error accepting connection.\n");
619
// add socket to sockets list
620
commsSocket = newSocket;
623
[server.netService stop];
624
[server.netService release];
625
server.netService = nil;
627
close(server.listenSocket);
628
server.listenSocket = -1;
630
[self startCommsWorkThreads];
632
NSLog(@"Accepted connection.");
641
close(server.listenSocket);
642
server.listenSocket = -1;
649
server.acThreadActive = 0;
660
- (void) watchdogThread: (id) info
662
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
669
while (wdThreadShouldRun)
673
if ((commsSocket == -1) && (server.listenSocket == -1))
675
NSLog(@"watchdog noticed connection error");
676
if (automaticallyReconnect)
678
NSLog(@"restarting server");
679
[self stopCommsWorkThreads];
680
[self startListening];
684
if ([delegate respondsToSelector: @selector(connectionWasTerminated:)])
685
[delegate performSelectorOnMainThread: @selector(connectionWasTerminated:) withObject: self waitUntilDone: NO];
692
if (commsSocket == -1)
694
NSLog(@"watchdog noticed connection error");
695
if (automaticallyReconnect)
697
NSLog(@"restarting client");
698
[self stopCommsWorkThreads];
699
[self connectToRemoteService];
703
if ([delegate respondsToSelector: @selector(connectionWasTerminated:)])
704
[delegate performSelectorOnMainThread: @selector(connectionWasTerminated:) withObject: self waitUntilDone: NO];
734
- (BOOL) enableBonjourWithDomain:(NSString*)domain applicationProtocol:(NSString*)protocol name:(NSString*)name
737
domain = @""; //Will use default Bonjour registration doamins, typically just ".local"
739
name = @""; //Will use default Bonjour name, e.g. the name assigned to the device in iTunes
741
assert([protocol length] && server.listenSocket);
743
NSLog(@"tappity port: %d", server.portnum);
745
[server.netService stop];
746
[server.netService release];
748
server.netService = [[NSNetService alloc] initWithDomain: domain type: protocol name: name port: server.portnum];
749
if(server.netService == nil)
752
[server.netService setDelegate: self];
753
// [server.netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
754
[server.netService publish];
761
- (void) startListening
763
struct sockaddr_in myAddress;
764
memset(&myAddress, 0, sizeof(myAddress));
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
770
server.listenSocket = socket(PF_INET, SOCK_STREAM, 0);
771
assert(server.listenSocket != -1);
773
_setStandardSocketOpts(server.listenSocket);
775
int err = bind(server.listenSocket, (struct sockaddr *)&myAddress, sizeof(myAddress));
778
err = listen(server.listenSocket, 1);
781
[self startAcceptThread];
782
[self startWatchdogThread];
784
if([self enableBonjourWithDomain: @"" applicationProtocol: server.protocolName name: server.serviceName])
785
NSLog(@"tappity bounjour advertisments up and running");
789
- (void) startBonjourServerWithName: (NSString*) name protocol: (NSString*) protocol port: (int) pnum;
791
NSLog(@"starting tappity server");
793
[server.serviceName release];
794
server.serviceName = [name retain];
797
[server.protocolName release];
798
server.protocolName = [protocol retain];
800
server.portnum = pnum;
804
[self startListening];
808
- (void) terminateConnection
810
[self stopWatchdogThread];
811
[self stopAcceptThread];
812
[self stopCommsWorkThreads];
818
[server.serviceName release];
820
[server.netService stop];
821
[server.netService release];
823
[activeThreads release];
827
[remoteService release];
832
@synthesize delegate, receiveDataOnMainThread, automaticallyReconnect;