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