--- /dev/null
+//
+// 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