/* * Grouch.app Copyright (C) 2006 Andy Sveikauskas * ------------------------------------------------------------------------ * This program is free software under the GNU General Public License * -- * This is a socket class. It buffers bytes and flushes them in a run * loop. For the actual socket glue see GrouchSocket{Unix,Mac}.m. */ #import #import #import #import #import #import #import #include #include #define INPUT_BUFFER_SIZE 1024 static void *buffer_alloc( struct grouchsocket_buffer *buf, size_t len ) { size_t alloc = buf->alloc ? buf->alloc : 1; while( alloc-buf->len < len ) alloc *= 2; if( alloc != buf->alloc ) { void *r = realloc(buf->buf, alloc); if( r ) { buf->buf = r; buf->alloc = alloc; } else [GrouchException raiseMemoryException]; } { char *r = buf->buf; r += buf->len; buf->len += len; return r; } } static void remove_from_buffer( struct grouchsocket_buffer *buf, size_t n ) { memmove( buf->buf, buf->buf+n, buf->len -= n ); } @implementation GrouchSocket + (GrouchSocket*)socketForHost:(NSString*)host atPort:(int)port withRunLoop:(NSRunLoop*)loopy { GrouchSocket *r = [self new]; NS_DURING [r initForHost:host atPort:port withRunLoop:loopy]; NS_HANDLER [r release]; [localException raise]; NS_ENDHANDLER return r; } - initForHost:(NSString*)host atPort:(int)port withRunLoop:(NSRunLoop*)loopy { fd = [SOCKET_IMPL socketForHost:host atPort:port withRunLoop:loopy forSocket:self]; return self; } - init { [super init]; memset( &in, 0, sizeof(in) ); memset( &out, 0, sizeof(out) ); _delegate = (id)self; keepAlive = 0; time(&lastKeepAlive); return self; } - close { if( fd ) { SEL msg = @selector(connectionClosed:); [(NSObject*)fd release]; fd = nil; if( [(NSObject*)_delegate respondsToSelector:msg] ) [_delegate connectionClosed:self]; } return self; } - (void)dealloc { [self close]; if( in.buf ) free( in.buf ); if( out.buf ) free( out.buf ); [super dealloc]; } - delegate { return _delegate; } - (void)setDelegate:delegate { _delegate = delegate; } - (void)writeData:(const void*)buf withLength:(size_t)len { memcpy( buffer_alloc(&out,len), buf, len ); if(fd) [fd startWriteThread]; } - (void)getInputBuffer:(void**)buf withLength:(size_t*)len { *buf = in.buf; *len = in.len; } - (void)flush { if(fd) { int r = 1; while( out.len && (r=[fd write:out.buf length:out.len]) > 0 ) remove_from_buffer( &out, r ); if( [fd lastOperationWasError] ) [self close]; } } - (void)forceFlush { if( fd && out.len ) { [fd setBlocking:YES]; [self flush]; [fd setBlocking:NO]; } } - (void)removeBytesFromInputBuffer:(size_t)l { remove_from_buffer( &in, l > in.len ? in.len : l ); } - (void)setKeepAlive:(time_t)intreval { keepAlive = intreval; } - (void)readLoop { BOOL failure = NO; if( fd ) { char *buf[INPUT_BUFFER_SIZE]; int r; while( (r=[fd read:buf length:sizeof(buf)]) > 0 ) memcpy( buffer_alloc(&in, r), buf, r ); if( [fd lastOperationWasError] ) { [self close]; failure = YES; } } } - (void)eventLoop:(GrouchSocketEvent)e { SEL msg = @selector(bytesIn:atBuffer:withLength:); if( (e & GrouchSocketEventIn) ) [self readLoop]; if( (e & GrouchSocketEventOut) ) [self flush]; if( (e & GrouchSocketEventError) ) [self close]; if( in.len && [(NSObject*)_delegate respondsToSelector:msg] ) [_delegate bytesIn:self atBuffer:in.buf withLength:in.len]; if( fd && keepAlive ) { time_t now; time(&now); if( lastKeepAlive-now > keepAlive ) { SEL msg2 = @selector(keepAlive:); lastKeepAlive = now; if( [(NSObject*)_delegate respondsToSelector:msg2] ) [_delegate keepAlive:self]; } } [self flush]; } - (void)eventLoop { [self eventLoop:[fd pollSocketEvents]]; } - (id)fd { return fd; } - (GrouchSocketEvent)pollSocketEvents { return [fd pollSocketEvents]; } - (time_t)keepAlive { return keepAlive; } - (size_t)outBufferSize { return out.len; } @end