5
// Created by döme on 30.10.2009.
6
// Copyright 2009 __MyCompanyName__. All rights reserved.
11
#include <sys/socket.h>
14
#include <netinet/in.h>
15
#include <arpa/inet.h>
16
#include <sys/types.h>
18
#include <netinet/in.h>
21
#import "SocketMessenger.h"
23
NSString* SocketMessageIdKey = @"SocketMessageId";
24
NSString* SocketMessageDataKey = @"SocketMessageData";
26
@interface SocketMessenger (Private)
27
- (void) startCommsWorkThreads;
30
@implementation SocketMessenger
34
if (!(self = [super init]))
39
sendLock = [[NSCondition alloc] init];
44
- (BOOL) threadActive: (id) key
46
return [[activeThreads objectForKey: key] boolValue];
49
- (void) threadWillExit: (id) key
53
[activeThreads removeObjectForKey: key];
55
if (![activeThreads count])
65
- (void) receivingThread: (id) info
67
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
74
while ([self threadActive: info])
95
FD_SET(socket, &readfds);
96
FD_SET(socket, &errorfds);
97
maxSocket = MAX(maxSocket, socket);
101
//printf("No sockets, sleeping for a bit...\n");
106
if (select(maxSocket+1, &readfds, &writefds, &errorfds, &tv) < 0)
112
if (FD_ISSET(socket, &readfds))
114
BOOL messageFinished = NO;
115
//NSLog(@"receiving...");
116
if (!expectedMessageSize)
118
uint32_t header[2] = {0,0};
119
int actuallyRead = 0;
121
actuallyRead = recv(socket, header, 8, MSG_PEEK);
123
if (actuallyRead == 8)
125
actuallyRead = recv(socket, header, 8, 0);
126
expectedMessageSize = ntohl(header[1]);
127
currentMessageId = ntohl(header[0]);
128
currentData = [[NSMutableData alloc] initWithLength: expectedMessageSize];
130
else if (actuallyRead == -1)
132
//close(commsSocket);
133
//self->commsSocket = 0;
134
if (errno != ETIMEDOUT)
136
printf("Connection dropped with error.\n");
140
else if (actuallyRead == 0)
142
printf("remote socket closed.\n");
146
if (!expectedMessageSize)
147
messageFinished = YES;
151
size_t readAmount = expectedMessageSize - currentlyRead;
152
int actuallyRead = 0;
154
actuallyRead = recv(socket, [currentData mutableBytes] + currentlyRead, readAmount, 0);
155
if (actuallyRead == -1)
157
printf("Connection dropped with error.\n");
160
else if (actuallyRead == 0)
162
printf("remote socket closed.\n");
166
currentlyRead += actuallyRead;
168
if (currentlyRead == expectedMessageSize)
169
messageFinished = YES;
176
expectedMessageSize = 0;
178
if (!receivedPackets)
179
receivedPackets = [[NSMutableArray alloc] init];
180
[receivedPackets addObject: currentData];
181
[currentData release];
186
if (receiveDataOnMainThread)
187
[self performSelectorOnMainThread: @selector(dataReceived:) withObject: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: currentMessageId], SocketMessageIdKey, currentData, SocketMessageDataKey, nil] waitUntilDone: NO];
189
[self dataReceived: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: currentMessageId], SocketMessageIdKey, currentData, SocketMessageDataKey, nil]];
198
[self threadWillExit: info];
210
- (void) runThreadWithSelector: (SEL) selector
215
activeThreads = [[NSMutableDictionary alloc] init];
216
id number = [NSNumber numberWithInt: threadIds++];
218
[activeThreads setObject: [NSNumber numberWithBool: YES] forKey: number];
219
[NSThread detachNewThreadSelector: selector toTarget: self withObject: [number retain]];
223
- (void) sendData: (NSData*) tapData withIdentifier: (NSInteger) messageId
228
sendQueue = [[NSMutableArray alloc] init];
230
[sendQueue addObject: [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInteger: messageId], SocketMessageIdKey, tapData, SocketMessageDataKey, nil]];
238
- (void) sendingThread: (id) info
240
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
247
while ([self threadActive: info])
250
while (![sendQueue count])
255
NSDictionary* dict = [[sendQueue objectAtIndex: 0] retain];
256
NSData* data = [dict objectForKey: SocketMessageDataKey];
257
uint32_t messageId = [[dict objectForKey: SocketMessageIdKey] intValue];
258
[sendQueue removeObjectAtIndex: 0];
262
size_t sizeToSend = 8;
264
uint32_t header[2] = {htonl(messageId), htonl([data length])};
270
socket = commsSocket;
273
while (dataSent < sizeToSend)
275
if ((err = send(socket, header + dataSent, sizeToSend - dataSent, 0)) == -1)
286
sizeToSend = [data length];
288
while (dataSent < sizeToSend)
290
if ((err = send(socket, [data bytes] + dataSent, sizeToSend - dataSent, 0)) == -1)
308
[self threadWillExit: info];
319
- (void) connectToService: (NSNetService*) service
321
NSInteger port = [service port];
322
NSString* hostName = [service hostName];
324
NSLog(@"SocketMessenger attempting to connect to %@ : %d", hostName, port);
329
// _setStandardSocketOpts(socket);
331
struct addrinfo hints, *servinfo = NULL, *p = NULL;
334
memset(&hints, 0, sizeof hints);
335
hints.ai_family = AF_UNSPEC; // use AF_INET6 to force IPv6
336
hints.ai_socktype = SOCK_STREAM;
337
//hints.ai_protocol = IPPROTO_TCP;
338
hints.ai_flags = AI_PASSIVE;
340
// NSHost* host = [NSHost hostWithName: hostName];
342
if ((rv = getaddrinfo([hostName UTF8String], [[NSString stringWithFormat: @"%d", port] UTF8String], &hints, &servinfo)) != 0)
344
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
348
// loop through all the results and connect to the first we can
349
for(p = servinfo; p != NULL; p = p->ai_next)
351
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
357
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
364
break; // if we get here, we must have connected successfully
368
// looped off the end of the list with no connection
369
fprintf(stderr, "failed to connect\n");
373
freeaddrinfo(servinfo); // all done with this structure
375
NSLog(@"SocketMessenger connected");
378
self->commsSocket = sockfd;
380
[self startCommsWorkThreads];
384
- (void) startCommsWorkThreads
386
[self runThreadWithSelector: @selector(sendingThread:)];
387
[self runThreadWithSelector: @selector(receivingThread:)];
389
if ([self respondsToSelector: @selector(connectionWasEstablished)])
390
[self connectionWasEstablished];
393
static void _setStandardSocketOpts(int socket)
396
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
398
setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
399
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
403
- (void) acceptThread: (id) info
405
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
412
while ([self threadActive: info])
421
int lsock = server.listenSocket;
430
FD_SET(lsock, &readfds);
431
FD_SET(lsock, &errorfds);
432
maxSocket = MAX(maxSocket, lsock);
436
printf("No sockets, sleeping for a bit...\n");
441
//NSLog(@"listening for connection...");
443
if (select(maxSocket+1, &readfds, NULL, &errorfds, &tv) < 0)
449
if (FD_ISSET(lsock, &readfds))
451
NSLog(@"Accepting connection...");
453
socklen_t sinSize = sizeof(struct sockaddr_in);
454
struct sockaddr_in peerAddress;
455
int newSocket = accept(lsock, (struct sockaddr *)&peerAddress, &sinSize);
457
_setStandardSocketOpts(newSocket);
462
if (errno == EWOULDBLOCK)
467
printf("Error accepting connection.\n");
477
// add socket to sockets list
478
commsSocket = newSocket;
481
[server.netService stop];
482
[server.netService release];
483
server.netService = nil;
485
[self startCommsWorkThreads];
487
NSLog(@"Accepted connection.");
502
- (BOOL) enableBonjourWithDomain:(NSString*)domain applicationProtocol:(NSString*)protocol name:(NSString*)name
505
domain = @""; //Will use default Bonjour registration doamins, typically just ".local"
507
name = @""; //Will use default Bonjour name, e.g. the name assigned to the device in iTunes
509
assert([protocol length] && server.listenSocket);
511
NSLog(@"tappity port: %d", ntohs(server.myAddress.sin_port));
513
[server.netService stop];
514
[server.netService release];
516
server.netService = [[NSNetService alloc] initWithDomain: domain type: protocol name: name port: ntohs(server.myAddress.sin_port)];
517
if(server.netService == nil)
520
[server.netService setDelegate:self];
521
// [server.netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
522
[server.netService publish];
528
- (void) startListening
530
server.myAddress.sin_family = AF_INET; // host byte order
531
server.myAddress.sin_port = htons(server.portnum); // short, network byte order, any port
532
server.myAddress.sin_addr.s_addr = htonl(INADDR_ANY); // auto-fill with my IP
534
server.listenSocket = socket(PF_INET, SOCK_STREAM, 0);
535
assert(server.listenSocket != -1);
537
_setStandardSocketOpts(server.listenSocket);
539
int err = bind(server.listenSocket, (struct sockaddr *)&server.myAddress, sizeof(server.myAddress));
542
err = listen(server.listenSocket, 1);
545
[self runThreadWithSelector: @selector(acceptThread:)];
547
if([self enableBonjourWithDomain: @"" applicationProtocol: server.protocolName name: server.serviceName])
548
NSLog(@"tappity bounjour advertisments up and running");
552
- (void) startBonjourServerWithName: (NSString*) name protocol: (NSString*) protocol port: (int) pnum;
554
NSLog(@"starting tappity server");
556
[server.serviceName release];
557
server.serviceName = [name retain];
560
[server.protocolName release];
561
server.protocolName = [protocol retain];
563
server.portnum = pnum;
565
[self startListening];
571
[server.serviceName release];
573
[server.netService stop];
574
[server.netService release];
576
[activeThreads release];