]> Dogcows Code - chaz/thecheat/blobdiff - MySocket.m
The Cheat 1.2
[chaz/thecheat] / MySocket.m
diff --git a/MySocket.m b/MySocket.m
new file mode 100644 (file)
index 0000000..d040af6
--- /dev/null
@@ -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
This page took 0.040733 seconds and 4 git commands to generate.