X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fthecheat;a=blobdiff_plain;f=MySocket.m;fp=MySocket.m;h=d040af69b25252edcf8ed228b4ad1bd405f49524;hp=0000000000000000000000000000000000000000;hb=d27548f80fe411fda2ee69c74a24eab4292267e9;hpb=e8d51183acdd2410a38dcf8f0efbf7c30cd6c581 diff --git a/MySocket.m b/MySocket.m new file mode 100644 index 0000000..d040af6 --- /dev/null +++ b/MySocket.m @@ -0,0 +1,1106 @@ +// +// MySocket.m +// The Cheat +// +// Created by Chaz McGarvey on 2/1/05. +// Copyright 2005 Chaz McGarvey. All rights reserved. +// + +#import "MySocket.h" + + +// CONSTANTS + +// this is used to read in unclaimed data +#define MYSOCKET_PACKETLEN (1024) + +enum { + kMySocketAddedToManager = 1, + kMySocketConnected = 2, + kMySocketIsListener = 4 +}; + + +struct _mySocketGlobals { + BOOL isManagerRunning; + NSMutableArray *sockets; + NSRecursiveLock *readLock; + NSRecursiveLock *writeLock; + int readPipe[2]; + int writePipe[2]; + int maxreadfd; + int maxwritefd; + fd_set readfds; + fd_set writefds; +} _mySocketGlobals = { NO, nil, nil, nil, { -1, -1 }, { -1, -1 }, 0, 0 }; + + +// HELPER CLASSES + +@interface _MySocketPacket : NSObject +{ + NSData *_buffer; + unsigned _bytesHandled; + unsigned _bytesRequired; + int _tag; +} + +- (id)initWithData:(NSData *)data tag:(int)tag; + +- (void *)bytes; // pointer to the current bytes; changes are bytes are handled +- (unsigned)bytesRemaining; + +- (void)handledBytes:(unsigned)count; + +- (unsigned)bytesRequired; + +- (NSData *)data; +- (int)tag; +- (BOOL)isComplete; + +@end + + +// PRIVATE STUFF + +@interface MySocket ( PrivateAPI ) + +- (id)_initWithFileDescriptor:(int)sockfd; +- (void)_connect; + +- (void)_addToManager; +- (void)_removeFromManager; + ++ (void)_readWithSockFD:(int)fd; ++ (void)_refreshReadThread; ++ (void)_writeWithSockFD:(int)fd; ++ (void)_refreshWriteThread; + ++ (void)_lockRead; ++ (void)_unlockRead; ++ (void)_lockWrite; ++ (void)_unlockWrite; ++ (void)_lockReadAndWrite; ++ (void)_unlockReadAndWrite; + +/* #### MANAGER METHODS #### */ ++ (void)_startManager; ++ (void)_readThread:(id)object; ++ (void)_writeThread:(id)object; + +- (int)_accept; +- (int)_read; +- (int)_write; + +- (void)_fillReadPacketsWithUnclaimedBytes; + +- (void)_handleReadQueue; +- (void)_handleWriteQueue; + +- (void)_eventDidAcceptSocket:(MySocket *)newSocket; +- (void)_eventDidDisconnect:(id)dummy; +- (void)_eventDidReadData:(_MySocketPacket *)packet; +- (void)_eventDidWriteData:(_MySocketPacket *)packet; + +// utility +- (NSMutableData *)_readBufferWithLength:(unsigned *)len; + +// accessors +- (int)_sockfd; + +@end + + + +@implementation MySocket + + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#pragma mark Initialization +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +- (id)initWithDelegate:(id)delegate +{ + if ( self = [super init] ) { + ChazLog( @"SOCKET CREATED" ); + [self setDelegate:delegate]; + } + return self; +} + +- (void)dealloc +{ + ChazLog( @"SOCKET DESTROYED" ); + [self disconnect]; + [super dealloc]; +} + + +- (void)release +{ + if ( [self retainCount] == 1 ) { + [self _removeFromManager]; + } + + [super release]; +} + + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#pragma mark Connecting/Disconnecting +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +- (BOOL)connectToHost:(NSString *)host port:(int)port +{ + return [self connectToAddressWithData:[MySocket addressWithHost:host port:port]]; +} + +- (BOOL)connectToAddress:(const struct sockaddr *)addr length:(unsigned)addrLen +{ + int err = 0; + + if ( [self isConnected] ) { + return NO; + } + + _sockfd = socket( addr->sa_family, SOCK_STREAM, 0 ); + if ( _sockfd == -1 ) { + // can't get file descriptor + return NO; + } + // make the socket NOT block + /*err = fcntl( _sockfd, F_SETFL, O_NONBLOCK ); + if ( err == -1 ) { + // can't not block + close( _sockfd ); + return NO; + }*/ + err = connect( _sockfd, addr, addrLen ); + if ( err == -1 ) { + // can't connect + close( _sockfd ); + return NO; + } + + [self _connect]; + return YES; +} + +- (BOOL)connectToAddressWithData:(NSData *)addr +{ + return [self connectToAddress:[addr bytes] length:[addr length]]; +} + +- (BOOL)listenOnPort:(int)port +{ + struct sockaddr_in addr; + + int err = 0; + int yes = 1; + + if ( [self isConnected] ) { + return NO; + } + + _sockfd = socket( AF_INET, SOCK_STREAM, 0 ); + if ( _sockfd == -1 ) { + // can't get file descriptor + return NO; + } + err = setsockopt( _sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes) ); + if ( err == -1 ) { + // can't reuse address + close( _sockfd ); + return NO; + } + + // pack the socket address structure + addr.sin_family = AF_INET; + addr.sin_port = htons( (short)port ); + addr.sin_addr.s_addr = INADDR_ANY; + memset( &(addr.sin_zero), NULL, 8 ); + + err = bind( _sockfd, (struct sockaddr *)(&addr), sizeof(addr) ); + if ( err == -1 ) { + // can't bind to this address + close( _sockfd ); + return NO; + } + + err = listen( _sockfd, 10 ); + if ( err == -1 ) { + // can't listen on this address + close( _sockfd ); + return NO; + } + + _flags |= kMySocketIsListener; + [self _connect]; + return YES; +} + + +- (void)disconnect +{ + if ( !(_flags & kMySocketConnected) ) { + // not connected + return; + } + + [self _removeFromManager]; + + close( _sockfd ); + _sockfd = -1; + + [_readQueue release]; + _readQueue = nil; + [_writeQueue release]; + _writeQueue = nil; + [_readLock release]; + _readLock = nil; + [_writeLock release]; + _writeLock = nil; + + _startTime = 0.0; + + _flags = 0; +} + + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#pragma mark Reading/Writing +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +- (void)readDataToLength:(unsigned)len tag:(int)tag +{ + _MySocketPacket *packet; + + if ( ![self isConnected] || [self isListener] || len == 0 ) { + // must be connected and have a valid length + return; + } + + // create a "read" packet + packet = [[_MySocketPacket alloc] initWithData:[NSMutableData dataWithLength:len] tag:tag]; + //[packet handledBytes:0]; + + // add the packet to the queue + [_readLock lock]; + [_readQueue addObject:packet]; + [packet release]; + [_readLock unlock]; + + // make sure the thread picks up the change + [MySocket _refreshReadThread]; +} + + +- (void)writeData:(NSData *)data tag:(int)tag +{ + _MySocketPacket *packet; + BOOL alreadyWriting; + + if ( ![self isConnected] || [self isListener] ) { + // must be connected + return; + } + + // create a "write" packet + packet = [[_MySocketPacket alloc] initWithData:data tag:tag]; + + [_writeLock lock]; + alreadyWriting = [_writeQueue count] > 0; + [_writeQueue addObject:packet]; + [packet release]; + [_writeLock unlock]; + + if ( !alreadyWriting ) { + // make the helper aware the socket has data to write + [MySocket _writeWithSockFD:_sockfd]; + } +} + +- (void)writeBytes:(void const *)bytes length:(unsigned)len tag:(int)tag +{ + [self writeData:[NSData dataWithBytes:bytes length:len] tag:tag]; +} + + +// AsyncSocket compatibility +- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + [self readDataToLength:length tag:tag]; +} + +- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + [self writeData:data tag:tag]; +} + +- (void)writeBytes:(void *)bytes length:(unsigned)len withTimeout:(NSTimeInterval)timeout tag:(long)tag +{ + [self writeBytes:bytes length:len tag:tag]; +} + + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#pragma mark Accesors +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +- (unsigned)bytesRead +{ + return _bytesRead; +} + +- (unsigned)bytesWritten +{ + return _bytesWritten; +} + +- (NSTimeInterval)timeConnected +{ + return CFAbsoluteTimeGetCurrent() - _startTime; +} + +- (double)readSpeed +{ + double currentTime = CFAbsoluteTimeGetCurrent(); + double speed = (double)( _bytesRead - _lastBytesRead ) / ( currentTime - _lastBytesReadTime ); + _lastBytesRead = _bytesRead; + _lastBytesReadTime = currentTime; + return speed; +} + +- (double)writeSpeed +{ + double currentTime = CFAbsoluteTimeGetCurrent(); + double speed = (double)( _bytesWritten - _lastBytesWritten ) / ( currentTime - _lastBytesWrittenTime ); + _lastBytesWritten = _bytesWritten; + _lastBytesWrittenTime = currentTime; + return speed; +} + + +- (NSString *)localHost +{ + char host[128]; + int err; + + err = gethostname( host, sizeof(host) ); + if ( err == -1 ) { + return @""; + } + return [NSString stringWithCString:host]; +} + +- (int)localPort +{ + return 0; +} + +- (NSString *)remoteHost +{ + int err; + struct sockaddr_in addr; + int len = sizeof(addr); + + err = getpeername( _sockfd, (struct sockaddr *)(&addr), &len ); + if ( err == -1 ) { + return @"Unknown"; + } + return [NSString stringWithCString:inet_ntoa(addr.sin_addr)]; +} + +- (int)remotePort +{ + int err; + struct sockaddr_in addr; + int len = sizeof(addr); + + err = getpeername( _sockfd, (struct sockaddr *)(&addr), &len ); + if ( err == -1 ) { + return -1; + } + return addr.sin_port; +} + + +- (BOOL)isConnected +{ + return _flags & kMySocketConnected; +} + +- (BOOL)isListener +{ + return _flags & kMySocketIsListener; +} + + ++ (NSData *)addressWithHost:(NSString *)host port:(int)port +{ + struct hostent *h; + struct sockaddr_in addr; + + // resolve the host + h = gethostbyname( [host lossyCString] ); + if ( h == NULL ) { + // host not found + return nil; + } + + // pack the socket address structure + addr.sin_family = AF_INET; + addr.sin_port = htons( (short)port ); + memcpy( &(addr.sin_addr), h->h_addr, sizeof(struct in_addr) ); + memset( &(addr.sin_zero), NULL, 8 ); + + return [NSData dataWithBytes:&addr length:sizeof(addr)]; +} + + +- (id)delegate +{ + return _delegate; +} + +- (void)setDelegate:(id)delegate +{ + _delegate = delegate; +} + + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#pragma mark PrivateAPI +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +- (id)_initWithFileDescriptor:(int)sockfd +{ + if ( self = [super init] ) { + _sockfd = sockfd; + [self _connect]; + } + return self; +} + +- (void)_connect +{ + if ( ![self isListener] ) { + _readQueue = [[NSMutableArray alloc] init]; + _writeQueue = [[NSMutableArray alloc] init]; + } + + // create the locks + _readLock = [[NSRecursiveLock alloc] init]; + _writeLock = [[NSRecursiveLock alloc] init]; + + _startTime = _lastBytesReadTime = _lastBytesWrittenTime = CFAbsoluteTimeGetCurrent(); + _bytesRead = _lastBytesRead = _bytesWritten = _lastBytesWritten = 0; + + _flags |= kMySocketConnected; + + [self _addToManager]; +} + + +- (void)_addToManager +{ + if ( _flags & kMySocketAddedToManager ) { + // only add this socket once + return; + } + + // start the manager if it is not already started + [MySocket _startManager]; + + [MySocket _lockReadAndWrite]; + // add to global array of sockets + [_mySocketGlobals.sockets addObject:self]; + [self release]; + [MySocket _readWithSockFD:_sockfd]; + [MySocket _unlockReadAndWrite]; + + // mark as added to manager + _flags |= kMySocketAddedToManager; +} + +- (void)_removeFromManager +{ + if ( !(_flags & kMySocketAddedToManager) ) { + // only remove if it is added + return; + } + + [MySocket _lockReadAndWrite]; + // remove from global array + [self retain]; + ChazLog( @"REMOVING SOCKET AT INDEX %i", [_mySocketGlobals.sockets indexOfObject:self] ); + [_mySocketGlobals.sockets removeObject:self]; + FD_CLR( _sockfd, &_mySocketGlobals.readfds ); + FD_CLR( _sockfd, &_mySocketGlobals.writefds ); + [MySocket _unlockReadAndWrite]; + + _flags ^= kMySocketAddedToManager; +} + + ++ (void)_readWithSockFD:(int)fd +{ + [MySocket _lockRead]; + FD_SET( fd, &_mySocketGlobals.readfds ); + _mySocketGlobals.maxreadfd = MAX( _mySocketGlobals.maxreadfd, fd ); + [MySocket _unlockRead]; + // make sure the thread picks up the change + [MySocket _refreshReadThread]; +} + ++ (void)_refreshReadThread +{ + char b = 'R'; + write( _mySocketGlobals.readPipe[1], &b, sizeof(b) ); +} + ++ (void)_writeWithSockFD:(int)fd +{ + [MySocket _lockWrite]; + FD_SET( fd, &_mySocketGlobals.writefds ); + _mySocketGlobals.maxwritefd = MAX( _mySocketGlobals.maxwritefd, fd ); + [MySocket _unlockWrite]; + [MySocket _refreshWriteThread]; +} + ++ (void)_refreshWriteThread +{ + char b = 'R'; + write( _mySocketGlobals.writePipe[1], &b, sizeof(b) ); +} + + ++ (void)_lockRead +{ + [_mySocketGlobals.readLock lock]; +} + ++ (void)_unlockRead +{ + [_mySocketGlobals.readLock unlock]; +} + ++ (void)_lockWrite +{ + [_mySocketGlobals.writeLock lock]; +} + ++ (void)_unlockWrite +{ + [_mySocketGlobals.writeLock unlock]; +} + ++ (void)_lockReadAndWrite +{ + [MySocket _lockRead]; + [MySocket _lockWrite]; +} + ++ (void)_unlockReadAndWrite +{ + [MySocket _unlockRead]; + [MySocket _unlockWrite]; +} + + ++ (void)_startManager +{ + int err; + + if ( _mySocketGlobals.isManagerRunning ) { + return; + } + + ChazLog( @"MySocketHelper STARTING" ); + + // zero the descriptor sets + FD_ZERO( &_mySocketGlobals.readfds ); + FD_ZERO( &_mySocketGlobals.writefds ); + + // create the read pipe + err = pipe( _mySocketGlobals.readPipe ); + if ( err == -1 ) { + return; + } + FD_SET( _mySocketGlobals.readPipe[0], &_mySocketGlobals.readfds ); + _mySocketGlobals.maxreadfd = _mySocketGlobals.readPipe[0]; + // create the write pipe + err = pipe( _mySocketGlobals.writePipe ); + if ( err == -1 ) { + close( _mySocketGlobals.readPipe[0] ); + close( _mySocketGlobals.readPipe[1] ); + return; + } + _mySocketGlobals.maxwritefd = _mySocketGlobals.writePipe[0]; + + // create other global objects + _mySocketGlobals.sockets = [[NSMutableArray alloc] init]; + _mySocketGlobals.readLock = [[NSRecursiveLock alloc] init]; + _mySocketGlobals.writeLock = [[NSRecursiveLock alloc] init]; + + // start the threads + [NSThread detachNewThreadSelector:@selector(_readThread:) toTarget:[MySocket class] withObject:nil]; + [NSThread detachNewThreadSelector:@selector(_writeThread:) toTarget:[MySocket class] withObject:nil]; + _mySocketGlobals.isManagerRunning = YES; +} + ++ (void)_readThread:(id)object +{ + BOOL breakLoop = NO; + fd_set readfds; + + do { + int err; + int i, top; + + // create the ever-so-important pool + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + [MySocket _lockRead]; + //FD_COPY( &_mySocketGlobals.readfds, &readfds ); + readfds = _mySocketGlobals.readfds; + [MySocket _unlockRead]; + + // find the sockets which need processing + err = select( _mySocketGlobals.maxreadfd+1, &readfds, NULL, NULL, NULL ); + if ( err == -1 ) { + // trouble, select() is having problems + ChazLog( @"select() failed!, error: %s", strerror(errno) ); + goto DONE; + } + + // check the pipe + if ( FD_ISSET( _mySocketGlobals.readPipe[0], &readfds ) ) { + char b; + err = read( _mySocketGlobals.readPipe[0], &b, sizeof(b) ); + if ( err <= 0 ) { + // our connection to the main thread was severed... + // SELF DESTRUCT + ChazLog( @"readPipe severed, exiting READ thread..." ); + breakLoop = YES; + goto DONE; + } + } + + // process the sockets + [MySocket _lockRead]; + top = [_mySocketGlobals.sockets count]; + for ( i = 0; i < top; i++ ) { + MySocket *sock = [_mySocketGlobals.sockets objectAtIndex:i]; + int sockfd = [sock _sockfd]; + + [sock _fillReadPacketsWithUnclaimedBytes]; + [sock _handleReadQueue]; + + if ( FD_ISSET( sockfd, &readfds ) ) { + if ( [sock isListener] ) { + // socket ready for accepting + err = [sock _accept]; + } + else { + // socket ready for reading + err = [sock _read]; + } + if ( err == -1 ) { + // action returne error, disconnect socket + [sock disconnect]; + [sock performSelectorOnMainThread:@selector(_eventDidDisconnect:) + withObject:nil waitUntilDone:NO]; + top--; + } + } + } + [MySocket _unlockRead]; + +DONE:; + [pool release]; + } + while ( !breakLoop ); +} + ++ (void)_writeThread:(id)object +{ + BOOL breakLoop = NO; + fd_set pipefds; + fd_set readfds, writefds; + + FD_ZERO( &pipefds ); + FD_SET( _mySocketGlobals.writePipe[0], &pipefds ); + + do { + int err; + int i, top; + + // create the ever-so-important pool + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + readfds = pipefds; + [MySocket _lockWrite]; + //FD_COPY( &_mySocketGlobals.writefds, &writefds ); + writefds = _mySocketGlobals.writefds; + [MySocket _unlockWrite]; + + // find the sockets which need processing + err = select( _mySocketGlobals.maxwritefd+1, &readfds, &writefds, NULL, NULL ); + if ( err == -1 ) { + // trouble, select() is having problems + ChazLog( @"select() failed!, error: %s", strerror(errno) ); + goto DONE; + } + + // check the pipe + if ( FD_ISSET( _mySocketGlobals.writePipe[0], &readfds ) ) { + char b; + err = read( _mySocketGlobals.writePipe[0], &b, sizeof(b) ); + if ( err <= 0 ) { + // our connection to the main thread was severed... + // SELF DESTRUCT + ChazLog( @"writePipe severed" ); + breakLoop = YES; + goto DONE; + } + } + + // process the sockets + [MySocket _lockWrite]; + top = [_mySocketGlobals.sockets count]; + for ( i = 0; i < top; i++ ) { + MySocket *sock = [_mySocketGlobals.sockets objectAtIndex:i]; + int sockfd = [sock _sockfd]; + + if ( FD_ISSET( sockfd, &writefds ) ) { + // socket ready for accepting + err = [sock _write]; + if ( err == -1 ) { + // action returne error, disconnect socket + [sock disconnect]; + [sock performSelectorOnMainThread:@selector(_eventDidDisconnect:) + withObject:nil waitUntilDone:NO]; + top--; + } + } + } + [MySocket _unlockWrite]; + +DONE:; + [pool release]; + } + while ( !breakLoop ); +} + + +- (int)_accept +{ + MySocket *newSocket; + int newsockfd; + struct sockaddr addr; + int addrlen = sizeof(addr); + + newsockfd = accept( _sockfd, &addr, &addrlen ); + if ( newsockfd >= 0 ) { + // create a new MySocket + newSocket = [[MySocket alloc] _initWithFileDescriptor:newsockfd]; + [newSocket setDelegate:_delegate]; + + [self performSelectorOnMainThread:@selector(_eventDidAcceptSocket:) + withObject:newSocket waitUntilDone:NO]; + } + else { + return -1; + } + return 0; +} + +- (int)_read +{ + _MySocketPacket *packet = nil; + int bytesRead; + + [_readLock lock]; + if ( [_readQueue count] == 0 ) { + // no packets claiming anything, so just + // read into the unclaimed bytes buffer. + if ( !_unclaimedData ) { + _unclaimedData = [[NSMutableData alloc] init]; + } + int len = [_unclaimedData length]; + [_unclaimedData increaseLengthBy:MYSOCKET_PACKETLEN]; + bytesRead = recv( _sockfd, [_unclaimedData mutableBytes] + len, MYSOCKET_PACKETLEN, 0 ); + [_unclaimedData setLength:len+bytesRead]; + } + else { + packet = [_readQueue objectAtIndex:0]; + bytesRead = recv( _sockfd, [packet bytes], [packet bytesRemaining], 0 ); + [packet handledBytes:bytesRead]; + } + [_readLock unlock]; + + if ( bytesRead > 0 ) { + // update total bytes read + _bytesRead += bytesRead; + + [self _handleReadQueue]; + } + else { + // remove this socket + ChazLog( @"MySocket disconnecting: %i", bytesRead ); + return -1; + } + return 0; +} + +- (int)_write +{ + _MySocketPacket *packet = nil; + int bytesWritten = 0; + + [_writeLock lock]; + if ( [_writeQueue count] > 0 ) { + int buflen = 0; + int len = sizeof(buflen); + int err; + err = getsockopt( _sockfd, SOL_SOCKET, SO_SNDBUF, &buflen, &len ); + // write data + packet = [_writeQueue objectAtIndex:0]; + bytesWritten = send( _sockfd, [packet bytes], MIN([packet bytesRemaining],buflen/2), 0 ); //MIN(4096,[packet bytesRemaining]), 0 ); + [packet handledBytes:bytesWritten]; + } + [_writeLock unlock]; + + if ( bytesWritten >= 0 ) { + // update total bytes read + _bytesWritten += bytesWritten; + + [self _handleWriteQueue]; + } + else { + return -1; + } + return 0; +} + + +- (void)_fillReadPacketsWithUnclaimedBytes +{ + int i, top; + void *bytes; + int total; + + total = [_unclaimedData length]; + if ( total == 0 ) { + return; + } + bytes = [_unclaimedData mutableBytes]; + + [_readLock lock]; + top = [_readQueue count]; + for ( i = 0; i < top; i++ ) { + _MySocketPacket *packet = [_readQueue objectAtIndex:i]; + int len = MIN( total, [packet bytesRemaining] ); + + if ( len > 0 ) { + memcpy( [packet bytes], bytes, len ); + [packet handledBytes:len]; + bytes += len; + total -= len; + } + if ( total == 0 ) { + break; + } + } + [_unclaimedData replaceBytesInRange:NSMakeRange(0,[_unclaimedData length]-total) withBytes:NULL length:0]; + [_readLock unlock]; +} + + +- (void)_handleReadQueue +{ + int i, top; + + [_readLock lock]; + top = [_readQueue count]; + for ( i = 0; i < top; ) { + _MySocketPacket *packet = [_readQueue objectAtIndex:0]; + if ( [packet isComplete] ) { + [self performSelectorOnMainThread:@selector(_eventDidReadData:) + withObject:[packet retain] waitUntilDone:NO]; + [_readQueue removeObjectAtIndex:i]; + top--; + } + else { + i++; + } + } + [_readLock unlock]; +} + +- (void)_handleWriteQueue +{ + int i, top; + + [_writeLock lock]; + top = [_writeQueue count]; + for ( i = 0; i < top; ) { + _MySocketPacket *packet = [_writeQueue objectAtIndex:0]; + if ( [packet isComplete] ) { + [self performSelectorOnMainThread:@selector(_eventDidWriteData:) + withObject:[packet retain] waitUntilDone:NO]; + [_writeQueue removeObjectAtIndex:i]; + top--; + } + else { + i++; + } + } + if ( [_writeQueue count] == 0 ) { + // no more pending writes + FD_CLR( _sockfd, &_mySocketGlobals.writefds ); + } + [_writeLock unlock]; +} + + +- (void)_eventDidAcceptSocket:(MySocket *)newSocket +{ + // just report the event back to the delegate + if ( [_delegate respondsToSelector:@selector(socket:didAcceptSocket:)] ) { + [_delegate socket:self didAcceptSocket:newSocket]; + } + + // release the parameter(s) + // they were not released by the caller because the caller is in another thread. + [newSocket release]; +} + +- (void)_eventDidDisconnect:(id)dummy +{ + [self disconnect]; + if ( [_delegate respondsToSelector:@selector(socketDidDisconnect:)] ) { + [_delegate socketDidDisconnect:self]; + } +} + +- (void)_eventDidReadData:(_MySocketPacket *)packet +{ + if ( [_delegate respondsToSelector:@selector(socket:didReadData:tag:)] ) { + [_delegate socket:self didReadData:[packet data] tag:[packet tag]]; + } + + [packet release]; +} + +- (void)_eventDidWriteData:(_MySocketPacket *)packet +{ + if ( [_delegate respondsToSelector:@selector(socket:didWriteDataWithTag:)] ) { + [_delegate socket:self didWriteDataWithTag:[packet tag]]; + } + + [packet release]; +} + + +- (NSMutableData *)_readBufferWithLength:(unsigned *)len +{ + NSMutableData *buffer; + unsigned packetLen = *len; + + if ( buffer = _unclaimedData ) { + // claim the bytes + int unclaimedLen = [_unclaimedData length]; + if ( unclaimedLen > packetLen ) { + *len = packetLen; + _unclaimedData = [[NSMutableData alloc] initWithBytes:[buffer bytes]+packetLen length:unclaimedLen-packetLen]; + //[[buffer subdataWithRange:NSMakeRange(packetLen,unclaimedLen-packetLen)] retain]; + } + else { + *len = unclaimedLen; + _unclaimedData = nil; + } + [buffer setLength:packetLen]; + } + else { + buffer = [[NSMutableData alloc] initWithLength:packetLen]; + *len = 0; + } + return [buffer autorelease]; +} + + +- (int)_sockfd +{ + return _sockfd; +} + + +@end + + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#pragma mark _MySocketPacket +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + +@implementation _MySocketPacket + + +- (id)initWithData:(NSData *)data tag:(int)tag +{ + if ( self = [super init] ) { + _buffer = [data retain]; + _bytesRequired = [data length]; + _tag = tag; + } + return self; +} + +- (void)dealloc +{ + [_buffer release]; + [super dealloc]; +} + + +- (void *)bytes +{ + return (void *)[_buffer bytes] + _bytesHandled; +} + +- (unsigned)bytesRemaining +{ + return _bytesRequired - _bytesHandled; +} + + +- (void)handledBytes:(unsigned)count +{ + _bytesHandled += count; +} + +- (unsigned)bytesRequired +{ + return _bytesRequired; +} + + +- (NSData *)data +{ + return _buffer; +} + +- (int)tag +{ + return _tag; +} + +- (BOOL)isComplete +{ + return [self bytesRemaining] == 0; +} + + + +@end