// A simple (TCP) socket connection implementation // (C)2007,8 Simon Urbanek // Dual-license: GPL v2 and AT&T Proprietary // // this implementation is based on regular unix sockets #import "TCPConnection.h" #include #include #include #include #include #include @implementation TCPConnection NSError *errorFromSystem() { int ec = errno; if (ec == 0) return nil; #ifdef TALK_DEBUG NSError *err = [NSError errorWithDomain:NSPOSIXErrorDomain code:ec userInfo:nil]; NSLog(@"TCPConnection::errorFromSystem(): %@", err); return err; #else return [NSError errorWithDomain:NSPOSIXErrorDomain code:ec userInfo:nil]; #endif } - (NSError*) connectToHost: (NSString*) host port: (int) port { #ifdef TALK_DEBUG NSLog(@"TCPConnection connectToHost: %@ port: %d", host, port); #endif struct hostent *haddr; if(!(haddr = gethostbyname([host UTF8String]))) return errorFromSystem(); #ifdef TALK_DEBUG NSLog(@" - host resolved"); #endif if (sock == -1) sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) return errorFromSystem(); struct sockaddr_in sa; memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = *((unsigned long *) haddr->h_addr); if(connect(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1) { [self close]; return errorFromSystem(); } #ifdef TALK_DEBUG NSLog(@" - connected"); #endif return nil; } + (id) connectToHost: (NSString*) host port: (int) port error: (NSError**) err { #ifdef TALK_DEBUG NSLog(@"TCPConnection + connectToHost: %@ port: %d error:%p", host, port, err); #endif TCPConnection *conn = [[TCPConnection alloc] init]; NSError *localError = [conn connectToHost:host port:port]; if (localError) { [localError retain]; [conn release]; if (err) err[0] = [localError autorelease]; else [localError release]; return nil; } return [conn autorelease]; } - (id) initWithSocket: (int) socketHandle connected: (BOOL) isConnected { self = [super init]; if (self) { sock = socketHandle; lastError = nil; inClosed = outClosed = NO; connected = isConnected; } return self; } - (id) init { self = [super init]; if (self) { sock = socket(AF_INET, SOCK_STREAM, 0); lastError = nil; inClosed = outClosed = NO; connected = NO; } return self; } - (NSError*) lastError { NSError *error = lastError; return error ? [[error retain] autorelease] : nil; } - (BOOL) connected { return connected && (sock != -1); } - (BOOL) close { #ifdef TALK_DEBUG NSLog(@"TCPConnection: close (sock=%d)", sock); #endif if (sock != -1) close(sock); sock = -1; inClosed = outClosed = YES; connected = NO; return YES; } - (void) dealloc { #ifdef TALK_DEBUG NSLog(@"TCPConnection: dealloc"); #endif if (sock != -1) [self close]; if (lastError) [lastError release]; [super dealloc]; } - (BOOL) waitForBytesAvailableWithTimeout: (double) timeout { fd_set fds; struct timeval timv; FD_ZERO(&fds); FD_SET(sock, &fds); timv.tv_sec=(unsigned int)(timeout/1000000.0); timv.tv_usec=(unsigned int)(timeout*1000000.0); return (select(sock + 1, &fds, 0, 0, &timv) > 0 && FD_ISSET(sock, &fds)); } - (BOOL) hasBytesAvailable { fd_set fds; struct timeval timv; FD_ZERO(&fds); FD_SET(sock, &fds); timv.tv_sec=0; timv.tv_usec=0; return (select(sock + 1, &fds, 0, 0, &timv) > 0 && FD_ISSET(sock, &fds)); } - (NSInteger) read: (unsigned char *) buf maxLength: (NSUInteger) maxLength { NSError *error = lastError; lastError = nil; if (error) [error release]; int n = recv(sock, buf, maxLength, 0); #ifdef TALK_DEBUG NSLog(@"TCPConnection: recv=%d (maxLength=%d)", n, (int) maxLength); #endif if (n < 0) lastError = [errorFromSystem() retain]; return n; } - (NSInteger) write: (const unsigned char*) buf maxLength: (NSUInteger) maxLength { #ifdef TALK_DEBUG NSLog(@"TCPConnection: write (maxLength=%d)", (int) maxLength); #endif NSError *error = lastError; lastError = nil; if (error) [error release]; if (maxLength < 1) return 0; int n = send(sock, buf, maxLength, 0); #ifdef TALK_DEBUG NSLog(@" - send=%d (maxLength=%d)", n, (int) maxLength); #endif if (n < 0) lastError = [errorFromSystem() retain]; return n; } - (BOOL) closeOut { #ifdef TALK_DEBUG NSLog(@"TCPConnection: closeOut (sock=%d)", sock); #endif NSError *error = lastError; lastError = nil; if (error) [error release]; if (outClosed) return YES; if (inClosed) return [self close]; if (sock == -1) return NO; BOOL res = (shutdown(sock, SHUT_WR) == 0); if (res) outClosed = YES; return res; } - (BOOL) closeIn { #ifdef TALK_DEBUG NSLog(@"TCPConnection: closeIn (sock=%d)", sock); #endif if (lastError) { [lastError release]; lastError = nil; } if (inClosed) return YES; if (outClosed) return [self close]; if (sock == -1) return NO; BOOL res = (shutdown(sock, SHUT_RD) == 0); if (res) inClosed = YES; return res; } #pragma -- convenience methods -- - (BOOL) writeString: (NSString*) string { NSError *error = lastError; lastError = nil; if (error) [error release]; const char *u8 = [string UTF8String]; int len = strlen(u8); #ifdef TALK_DEBUG NSLog(@"TCPConnection.writeString: %@", string); #endif return [self write:(const unsigned char*) u8 maxLength:len] == len; } - (BOOL) writeData: (NSData*) data { NSError *error = lastError; lastError = nil; if (error) [error release]; #ifdef TALK_DEBUG NSLog(@"TCPConnection.writeData (%d bytes)", [data length]); #endif // FIXME: we should be really sending pieces if it cannot be sent at once return [self write:[data bytes] maxLength:[data length]]; } @end