2 Copyright (C) 2000-2003 SKYRIX Software AG
4 This file is part of OGo
6 OGo is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with OGo; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 #if defined(HAVE_UNISTD_H) || defined(__APPLE__)
29 #ifdef HAVE_SYS_SELECT_H
30 # include <sys/select.h>
32 #ifdef HAVE_SYS_FILIO_H
33 # include <sys/filio.h>
35 #if defined(HAVE_SYS_IOCTL_H)
36 # include <sys/ioctl.h>
38 #if defined(HAVE_TIME_H) || defined(__APPLE__)
41 #if defined(HAVE_SYS_TIME_H) || defined(__APPLE__)
42 # include <sys/time.h>
44 #if defined(HAVE_FCNTL_H) || defined(__APPLE__)
48 #if defined(__APPLE__)
49 # include <sys/types.h>
50 # include <sys/socket.h>
51 # include <sys/ioctl.h>
54 #if HAVE_WINDOWS_H && !defined(__CYGWIN32__)
58 #if defined(WIN32) && !defined(__CYGWIN32__)
60 # define ioctl ioctlsocket
65 #include <NGStreams/NGDescriptorFunctions.h>
66 #include "NGActiveSocket.h"
67 #include "NGSocketExceptions.h"
68 #include "NGSocket+private.h"
71 #if !defined(POLLRDNORM)
72 # define POLLRDNORM POLLIN
75 @interface NGActiveSocket(PrivateMethods)
77 - (id)_initWithDescriptor:(int)_fd
78 localAddress:(id<NGSocketAddress>)_local
79 remoteAddress:(id<NGSocketAddress>)_remote;
83 @implementation NGActiveSocket
85 #if !defined(WIN32) || defined(__CYGWIN32__)
87 + (BOOL)socketPair:(id<NGSocket>[2])_pair inDomain:(id<NGSocketDomain>)_domain {
93 if (socketpair([_domain socketDomain], SOCK_STREAM, [_domain protocol],
95 NGActiveSocket *s1 = nil;
96 NGActiveSocket *s2 = nil;
98 s1 = [[self alloc] _initWithDomain:_domain descriptor:fds[0]];
99 s2 = [[self alloc] _initWithDomain:_domain descriptor:fds[1]];
100 s1 = [s1 autorelease];
101 s2 = [s2 autorelease];
103 if ((s1 != nil) && (s2 != nil)) {
104 s1->mode = NGStreamMode_readWrite;
105 s1->receiveTimeout = 0.0;
106 s1->sendTimeout = 0.0;
107 s2->mode = NGStreamMode_readWrite;
108 s2->receiveTimeout = 0.0;
109 s2->sendTimeout = 0.0;
121 NSString *reason = nil;
125 reason = @"Not allowed to create socket of this type";
128 reason = @"Could not create socket: Insufficient user memory available";
130 case EPROTONOSUPPORT:
131 reason = @"The protocol is not supported by the address family or "
135 reason = @"The socket type is not supported by the protocol";
138 reason = @"Could not create socket: descriptor table is full";
141 reason = @"The specified protocol does not permit creation of socket "
147 NSLog(@"WARNING(%s): socketpair() call failed, but errno=0",
148 __PRETTY_FUNCTION__);
151 reason = [NSString stringWithFormat:@"Could not create socketpair: %s",
155 [[[NGCouldNotCreateSocketException alloc]
156 initWithReason:reason domain:_domain] raise];
163 + (id)socketConnectedToAddress:(id<NGSocketAddress>)_address {
164 volatile id sock = [[self alloc] initWithDomain:[_address domain]];
167 if (![sock connectToAddress:_address]) {
170 NSLog(@"WARNING(%s): Couldn't connect to address %@: %@",
171 __PRETTY_FUNCTION__, _address, [sock lastException]);
174 this method needs to raise the exception, since no object is returned
175 in which we could check the -lastException ...
177 e = [[sock lastException] retain];
183 sock = [sock autorelease];
188 - (id)initWithDomain:(id<NGSocketDomain>)_domain {
189 // designated initializer
190 if ((self = [super initWithDomain:_domain])) {
191 self->mode = NGStreamMode_readWrite;
192 self->receiveTimeout = 0.0;
193 self->sendTimeout = 0.0;
198 - (id)_initWithDescriptor:(int)_fd
199 localAddress:(id<NGSocketAddress>)_local
200 remoteAddress:(id<NGSocketAddress>)_remote
202 if ((self = [self _initWithDomain:[_local domain] descriptor:_fd])) {
203 ASSIGN(self->localAddress, _local);
204 ASSIGN(self->remoteAddress, _remote);
205 self->mode = NGStreamMode_readWrite;
207 #if !defined(WIN32) || defined(__CYGWIN32__)
208 NGAddDescriptorFlag(self->fd, O_NONBLOCK);
215 [self->remoteAddress release];
221 - (NSException *)lastException {
222 return [super lastException];
225 - (void)raise:(NSString *)_name reason:(NSString *)_reason {
229 clazz = NSClassFromString(_name);
230 NSAssert1(clazz, @"did not find exception class %@", _name);
234 if ([clazz instancesRespondToSelector:@selector(initWithReason:socket:)])
235 e = [(id)e initWithReason:_reason socket:self];
236 else if ([clazz instancesRespondToSelector:@selector(initWithStream:reason:)])
237 e = [(id)e initWithStream:self reason:_reason];
238 else if ([clazz instancesRespondToSelector:@selector(initWithSocket:)])
239 e = [(id)e initWithSocket:self];
240 else if ([clazz instancesRespondToSelector:@selector(initWithStream:)])
241 e = [(id)e initWithStream:self];
243 e = [e initWithReason:_reason];
246 if ([clazz instancesRespondToSelector:@selector(initWithSocket:)])
247 e = [(id)e initWithSocket:self];
248 else if ([clazz instancesRespondToSelector:@selector(initWithStream:)])
249 e = [(id)e initWithStream:self];
253 [self setLastException:e];
256 - (void)raise:(NSString *)_name {
257 [self raise:_name reason:nil];
260 - (BOOL)markNonblockingAfterConnect {
261 #if !defined(WIN32) || defined(__CYGWIN32__)
262 // mark socket as non-blocking
265 // on Win we only support blocking sockets right now ...
270 - (BOOL)primaryConnectToAddress:(id<NGSocketAddress>)_address {
272 // NGCouldNotConnectException if the the connect() call fails
274 [self resetLastException];
277 (struct sockaddr *)[_address internalAddressRepresentation],
278 [_address addressRepresentationSize]) != 0) {
279 NSString *reason = nil;
280 int errorCode = errno;
285 reason = @"search permission denied for element in path";
287 #if defined(WIN32) && !defined(__CYGWIN32__)
289 reason = @"address already in use";
291 case WSAEADDRNOTAVAIL:
292 reason = @"address is not available on remote machine";
294 case WSAEAFNOSUPPORT:
295 reason = @"addresses in the specified family cannot be used with the socket";
298 reason = @"a previous non-blocking attempt has not yet been completed";
301 reason = @"descriptor is invalid";
303 case WSAECONNREFUSED:
304 reason = @"connection refused";
307 reason = @"connect was interrupted";
310 reason = @"the address length is invalid";
313 reason = @"socket is already connected";
316 reason = @"network is unreachable";
319 reason = @"timeout occured";
323 reason = @"address already in use";
326 reason = @"address is not available on remote machine";
329 reason = @"addresses in the specified family cannot be used with the socket";
332 reason = @"a previous non-blocking attempt has not yet been completed";
335 reason = @"descriptor is invalid";
338 reason = @"connection refused";
341 reason = @"connect was interrupted";
344 reason = @"the address length is invalid";
347 reason = @"an IO error occured";
350 reason = @"socket is already connected";
353 reason = @"network is unreachable";
356 reason = @"timeout occured";
362 NSLog(@"WARNING(%s): connect() call failed, but errno=0",
363 __PRETTY_FUNCTION__);
367 reason = [NSString stringWithCString:strerror(errorCode)];
371 reason = [NSString stringWithFormat:@"Could not connect to address %@: %@",
374 e = [[NGCouldNotConnectException alloc]
375 initWithReason:reason socket:self address:_address];
376 [self setLastException:e];
381 /* connect was successful */
383 ASSIGN(self->remoteAddress, _address);
385 if ([self markNonblockingAfterConnect]) {
386 /* mark socket as non-blocking */
387 NGAddDescriptorFlag(self->fd, O_NONBLOCK);
388 NSAssert((NGGetDescriptorFlags(self->fd) & O_NONBLOCK),
389 @"could not enable non-blocking mode ..");
394 - (BOOL)connectToAddress:(id<NGSocketAddress>)_address {
396 // NGSocketAlreadyConnectedException if the socket is already connected
397 // NGInvalidSocketDomainException if the remote domain != local domain
398 // NGCouldNotCreateSocketException if the socket creation failed
400 if ([self isConnected]) {
401 [[[NGSocketAlreadyConnectedException alloc]
402 initWithReason:@"Could not connected: socket is already connected"
403 socket:self address:self->remoteAddress] raise];
407 // check whether the remote address is in the same domain like the bound one
409 if (![[localAddress domain] isEqual:[_address domain]]) {
410 [[[NGInvalidSocketDomainException alloc]
411 initWithReason:@"local and remote socket domains are different"
412 socket:self domain:[_address domain]] raise];
417 // connect, remote-address is non-nil if this returns
418 if (![self primaryConnectToAddress:_address])
421 // if the socket wasn't bound before (normal case), bind it now
423 if (![self kernelBoundAddress]) return NO;
427 - (void)_shutdownDuringOperation {
432 if (self->fd != NGInvalidSocketDescriptor) {
433 if (self->mode != NGStreamMode_undefined) {
434 if (shutdown(self->fd, SHUT_RDWR) == 0)
435 self->mode = NGStreamMode_undefined;
438 #if defined(WIN32) && !defined(__CYGWIN32__)
439 if (closesocket(self->fd) == 0) {
441 if (close(self->fd) == 0) {
443 self->fd = NGInvalidSocketDescriptor;
446 NSLog(@"ERROR(%s): close of socket %@ (fd=%i) alive=%s failed: %s",
448 self, self->fd, [self isAlive] ? "YES" : "NO", strerror(errno));
451 ASSIGN(self->remoteAddress, (id)nil);
456 - (BOOL)shutdownSendChannel {
457 if (NGCanWriteInStreamMode(self->mode)) {
458 shutdown(self->fd, SHUT_WR);
460 if (self->mode == NGStreamMode_readWrite)
461 self->mode = NGStreamMode_readOnly;
463 self->mode = NGStreamMode_undefined;
464 #if defined(WIN32) && !defined(__CYGWIN32__)
465 closesocket(self->fd);
469 self->fd = NGInvalidSocketDescriptor;
474 - (BOOL)shutdownReceiveChannel {
475 if (NGCanReadInStreamMode(self->mode)) {
476 shutdown(self->fd, SHUT_RD);
478 if (self->mode == NGStreamMode_readWrite)
479 self->mode = NGStreamMode_writeOnly;
481 self->mode = NGStreamMode_undefined;
482 #if defined(WIN32) && !defined(__CYGWIN32__)
483 closesocket(self->fd);
487 self->fd = NGInvalidSocketDescriptor;
493 // ******************** accessors ******************
495 - (id<NGSocketAddress>)remoteAddress {
496 return self->remoteAddress;
499 - (BOOL)isConnected {
500 return (self->remoteAddress != nil);
503 return [self isConnected];
510 - (void)setSendTimeout:(NSTimeInterval)_timeout {
511 self->sendTimeout = _timeout;
513 - (NSTimeInterval)sendTimeout {
514 return self->sendTimeout;
517 - (void)setReceiveTimeout:(NSTimeInterval)_timeout {
518 self->receiveTimeout = _timeout;
520 - (NSTimeInterval)receiveTimeout {
521 return self->receiveTimeout;
524 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
527 if ((![self isConnected]) || (fd == NGInvalidSocketDescriptor))
530 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM;
531 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
533 // timeout of 0 means return immediatly
534 return (NGPollDescriptor([self fileDescriptor], events, 0) == 1 ? NO : YES);
537 - (int)waitForMode:(NGStreamMode)_mode timeout:(NSTimeInterval)_timeout {
540 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM;
541 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
543 // timeout of 0 means return immediatly
544 return NGPollDescriptor([self fileDescriptor], events,
545 (int)(_timeout * 1000.0));
548 - (unsigned)numberOfAvailableBytesForReading {
551 // need to check whether socket is connected
552 if (self->remoteAddress == nil) {
553 [self raise:@"NGSocketNotConnectedException"
554 reason:@"socket is not connected"];
555 return NGStreamError;
558 if (!NGCanReadInStreamMode(self->mode)) {
559 [self raise:@"NGWriteOnlyStreamException"];
560 return NGStreamError;
563 #if !defined(WIN32) && !defined(__CYGWIN32__)
564 while (ioctl(self->fd, FIONREAD, &len) == -1) {
565 if (errno == EINTR) continue;
567 [self raise:@"NGSocketException"
568 reason:@"could not get number of available bytes"];
569 return NGStreamError;
572 // PeekNamedPipe() on Win ...
579 if (self->fd == NGInvalidSocketDescriptor)
582 /* poll socket for input */
589 FD_SET(self->fd, &readMask);
590 to.tv_sec = to.tv_usec = 0;
592 if (select(self->fd + 1, &readMask, NULL, NULL, &to) >= 0)
601 NSLog(@"socket select() failed: %s", strerror(errno));
606 /* no input is pending, connection is alive */
607 if (!FD_ISSET(self->fd, &readMask))
612 input is pending: If select() indicates pending input, but ioctl()
613 indicates zero bytes of pending input, the connection is broken
616 #if defined(WIN32) && !defined(__CYGWIN32__)
621 while (ioctl(self->fd, FIONREAD, &len) == -1) {
622 if (errno == EINTR) continue;
625 if (len > 0) return YES;
629 /* valid descriptor, but not alive .. so we close the socket */
630 #if defined(WIN32) && !defined(__CYGWIN32__)
631 closesocket(self->fd);
635 self->fd = NGInvalidSocketDescriptor;
636 RELEASE(self->remoteAddress); self->remoteAddress = nil;
640 // ******************** NGStream ********************
642 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
644 // NGStreamReadErrorException when the read call failed
645 // NGSocketNotConnectedException when the socket is not connected
646 // NGEndOfStreamException when the end of the stream is reached
647 // NGWriteOnlyStreamException when the receive channel was shutdown
648 NSException *e = nil;
650 if (self->fd == NGInvalidSocketDescriptor) {
651 [self raise:@"NGSocketException" reason:@"NGActiveSocket is not open"];
652 return NGStreamError;
655 // need to check whether socket is connected
656 if (self->remoteAddress == nil) {
657 [self raise:@"NGSocketNotConnectedException"
658 reason:@"socket is not connected"];
659 return NGStreamError;
662 if (!NGCanReadInStreamMode(self->mode)) {
663 [self raise:@"NGWriteOnlyStreamException"];
664 return NGStreamError;
667 if (_len == 0) return 0;
670 #if defined(WIN32) && !defined(__CYGWIN32__)
673 readResult = recv(self->fd, _buf, _len, 0);
674 if (readResult == 0) {
675 [self _shutdownDuringOperation];
676 [self raise:@"NGSocketShutdownDuringReadException"];
677 return NGStreamError;
679 else if (readResult < 0) {
680 int errorCode = WSAGetLastError();
684 e = [[NGSocketConnectionResetException alloc] initWithStream:self];
687 e = [[NGSocketTimedOutException alloc] initWithStream:self];
691 NSLog(@"WARNING: descriptor would block ..");
694 e = [[NGStreamReadErrorException alloc]
695 initWithStream:self errorCode:errorCode];
699 [self setLastException:e];
701 return NGStreamError;
707 NSAssert(_buf, @"invalid buffer");
708 NSAssert1(_len > 0, @"invalid length: %i", _len);
710 readResult = NGDescriptorRecv(self->fd, _buf, _len, 0,
711 (self->receiveTimeout == 0.0)
712 ? -1 // block until data
713 : (int)(self->receiveTimeout * 1000.0));
715 if ((readResult < 0) && (errno == EINVAL)) {
716 NSLog(@"%s: invalid argument in NGDescriptorRecv(%i, 0x%08X, %i, %i)",
718 self->fd, _buf, _len, 0,
719 (self->receiveTimeout == 0.0)
720 ? -1 // block until data
721 : (int)(self->receiveTimeout * 1000.0));
725 if (readResult == 0) {
726 [self _shutdownDuringOperation];
727 [self raise:@"NGSocketShutdownDuringReadException"];
728 return NGStreamError;
730 else if (readResult == -2) {
731 [self raise:@"NGSocketTimedOutException"];
732 return NGStreamError;
734 else if (readResult < 0) {
735 int errorCode = errno;
741 /* this happens with the Oracle7 adaptor !!! */
742 NSLog(@"WARNING(%s): readResult<0 (%i), but errno=0 - retry",
743 __PRETTY_FUNCTION__, readResult);
749 e = [[NGSocketConnectionResetException alloc] initWithStream:self];
752 e = [[NGSocketTimedOutException alloc] initWithStream:self];
756 NSLog(@"WARNING: descriptor would block ..");
759 e = [[NGStreamReadErrorException alloc]
760 initWithStream:self errorCode:errorCode];
764 [self setLastException:e];
766 return NGStreamError;
774 #if defined(WIN32) && !defined(__CYGWIN32__)
775 #warning fix exception handling
777 - (unsigned)_winWriteBytes:(const void *)_buf count:(unsigned)_len {
778 NSException *e = nil;
781 writeResult = send(self->fd, _buf, _len, 0);
783 if (writeResult == 0) {
784 [self _shutdownDuringOperation];
785 [self raise:@"NGSocketShutdownDuringWriteException"];
786 return NGStreamError;
788 else if (writeResult < 0) {
789 int errorCode = WSAGetLastError();
793 e = [[NGSocketConnectionResetException alloc] initWithStream:self];
796 e = [[NGSocketTimedOutException alloc] initWithStream:self];
800 NSLog(@"WARNING: descriptor would block ..");
803 e = [[NGStreamWriteErrorException alloc]
804 initWithStream:self errorCode:errno];
808 [self setLastException:e];
810 return NGStreamError;
818 - (unsigned)_unixWriteBytes:(const void *)_buf count:(unsigned)_len {
824 timeOut = (self->sendTimeout == 0.0)
825 ? -1 // block until data
826 : (int)(self->sendTimeout * 1000.0);
829 writeResult = NGDescriptorSend(self->fd, _buf, _len, 0, timeOut);
831 if (writeResult == 0) {
832 [self _shutdownDuringOperation];
833 [self raise:@"NGSocketShutdownDuringWriteException"];
834 return NGStreamError;
836 else if (writeResult == -2) {
837 [self raise:@"NGSocketTimedOutException"];
838 return NGStreamError;
840 else if (writeResult < 0) {
841 int errorCode = errno;
846 /* this happens with the Oracle7 (on SuSE < 7.1??) adaptor !!! */
847 NSLog(@"WARNING(%s): writeResult<0 (%i), but errno=0 - retry",
848 __PRETTY_FUNCTION__, writeResult);
851 if (retryCount > 200000) {
852 NSLog(@"WARNING(%s): writeResult<0 (%i), but errno=0 - cancel retry "
853 @"(already tried %i times !!!)",
854 __PRETTY_FUNCTION__, writeResult, retryCount);
855 [self _shutdownDuringOperation];
856 [self raise:@"NGSocketShutdownDuringWriteException"];
857 return NGStreamError;
865 [self raise:@"NGSocketConnectionResetException"];
866 return NGStreamError;
868 [self raise:@"NGSocketTimedOutException"];
869 return NGStreamError;
872 [self _shutdownDuringOperation];
873 [self raise:@"NGSocketShutdownDuringWriteException"];
874 return NGStreamError;
877 NSLog(@"WARNING: descriptor would block ..");
881 e = [[NGStreamWriteErrorException alloc]
882 initWithStream:self errorCode:errno];
883 [self setLastException:e];
885 return NGStreamError;
894 - (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len {
896 // NGStreamWriteErrorException when the write call failed
897 // NGSocketNotConnectedException when the socket is not connected
898 // NGReadOnlyStreamException when the send channel was shutdown
900 if (_len == NGStreamError) {
901 NSLog(@"ERROR(%s): got NGStreamError passed in as length ...",
902 __PRETTY_FUNCTION__);
903 return NGStreamError;
906 if (_len > (1024 * 1024 * 100 /* 100MB */)) {
907 NSLog(@"WARNING(%s): got passed in length %uMB (%u bytes, errcode=%u) ...",
908 __PRETTY_FUNCTION__, (_len / 1024 / 1024), _len, NGStreamError);
912 if (self->fd == NGInvalidSocketDescriptor) {
913 [self raise:@"NGSocketException" reason:@"NGActiveSocket is not open"];
914 return NGStreamError;
917 // need to check whether socket is connected
918 if (self->remoteAddress == nil) {
919 [self raise:@"NGSocketNotConnectedException"
920 reason:@"socket is not connected"];
921 return NGStreamError;
924 if (!NGCanWriteInStreamMode(self->mode)) {
925 [self raise:@"NGReadOnlyStreamException"];
926 return NGStreamError;
929 //NSLog(@"writeBytes: count:%u", _len);
931 #if defined(WIN32) && !defined(__CYGWIN32__)
932 return [self _winWriteBytes:_buf count:_len];
934 return [self _unixWriteBytes:_buf count:_len];
943 return [self shutdown];
947 - (NGStreamMode)mode {
951 /* methods method which write exactly _len bytes or fail */
953 - (BOOL)safeReadBytes:(void *)_buf count:(unsigned)_len {
954 volatile int toBeRead;
957 unsigned (*readBytes)(id, SEL, void *, unsigned);
959 *(&readBytes) = (void *)[self methodForSelector:@selector(readBytes:count:)];
966 readBytes(self, @selector(readBytes:count:), pos, toBeRead);
968 if (readResult == NGStreamError) {
969 NSException *localException = [self lastException];
972 data = [NSData dataWithBytes:_buf length:(_len - toBeRead)];
974 localException = [[[localException class] alloc]
976 readCount:(_len - toBeRead)
979 [self setLastException:localException];
980 RELEASE(localException);
983 NSAssert(readResult != 0, @"ERROR: readBytes may not return '0' ..");
985 if (readResult == toBeRead) {
986 // all bytes were read successfully, return
990 if (readResult < 1) {
991 [NSException raise:NSInternalInconsistencyException
992 format:@"readBytes:count: returned a value < 1"];
995 toBeRead -= readResult;
1002 - (BOOL)safeWriteBytes:(const void *)_buf count:(unsigned)_len {
1003 int toBeWritten = _len;
1005 void *pos = (void *)_buf;
1007 /* method cache (THREAD, reentrant) */
1008 static Class lastClass = Nil;
1009 static int (*writeBytes)(id,SEL,const void*,unsigned) = NULL;
1011 if (lastClass == *(Class *)self) {
1012 if (writeBytes == NULL)
1014 (void *)[self methodForSelector:@selector(writeBytes:count:)];
1017 lastClass = *(Class *)self;
1018 writeBytes = (void *)[self methodForSelector:@selector(writeBytes:count:)];
1023 (int)writeBytes(self, @selector(writeBytes:count:), pos, toBeWritten);
1025 if (writeResult == NGStreamError) {
1026 /* remember number of written bytes ??? */
1029 else if (writeResult == toBeWritten) {
1030 // all bytes were written successfully, return
1034 if (writeResult < 1) {
1035 [NSException raise:NSInternalInconsistencyException
1036 format:@"writeBytes:count: returned a value < 1 in stream %@",
1041 toBeWritten -= writeResult;
1051 [self raise:@"NGStreamException" reason:@"stream doesn't support a mark"];
1054 - (BOOL)markSupported {
1058 // convenience methods
1060 - (int)readByte { // java semantics (-1 returned on EOF)
1064 result = [self readBytes:&c count:sizeof(unsigned char)];
1067 static Class EOFExcClass = Nil;
1069 if (EOFExcClass == Nil)
1070 EOFExcClass = [NGEndOfStreamException class];
1072 if ([[self lastException] isKindOfClass:EOFExcClass])
1073 [self resetLastException];
1082 - (NSString *)modeDescription {
1083 NSString *result = @"<unknown>";
1085 switch ([self mode]) {
1086 case NGStreamMode_undefined: result = @"<closed>"; break;
1087 case NGStreamMode_readOnly: result = @"r"; break;
1088 case NGStreamMode_writeOnly: result = @"w"; break;
1089 case NGStreamMode_readWrite: result = @"rw"; break;
1091 [[[NGUnknownStreamModeException alloc] initWithStream:self] raise];
1097 - (NSString *)description {
1098 NSMutableString *d = [NSMutableString stringWithCapacity:64];
1100 [d appendFormat:@"<%@[0x%08X]: mode=%@ address=%@",
1101 NSStringFromClass([self class]),
1103 [self modeDescription], [self localAddress]];
1105 if ([self isConnected])
1106 [d appendFormat:@" connectedTo=%@", [self remoteAddress]];
1108 if ([self sendTimeout] != 0.0)
1109 [d appendFormat:@" send-timeout=%4.3fs", [self sendTimeout]];
1110 if ([self receiveTimeout] != 0.0)
1111 [d appendFormat:@" receive-timeout=%4.3fs", [self receiveTimeout]];
1113 [d appendString:@">"];
1117 @end /* NGActiveSocket */
1119 @implementation NGActiveSocket(DataMethods)
1121 - (NSData *)readDataOfLength:(unsigned int)_length {
1125 if (_length == 0) return [NSData data];
1127 readCount = [self readBytes:buf count:_length];
1128 return [NSData dataWithBytes:buf length:readCount];
1131 - (NSData *)safeReadDataOfLength:(unsigned int)_length {
1134 if (_length == 0) return [NSData data];
1135 [self safeReadBytes:buf count:_length];
1136 return [NSData dataWithBytes:buf length:_length];
1139 - (unsigned int)writeData:(NSData *)_data {
1140 return [self writeBytes:[_data bytes] count:[_data length]];
1142 - (BOOL)safeWriteData:(NSData *)_data {
1143 return [self safeWriteBytes:[_data bytes] count:[_data length]];
1146 @end /* NGActiveSocket(DataMethods) */
1148 #include <NGStreams/NGBufferedStream.h>
1150 @implementation NGBufferedStream(FastSocketForwarders)
1152 - (BOOL)isConnected {
1153 return [(id)self->source isConnected];
1155 - (int)fileDescriptor {
1156 return [(NSFileHandle *)self->source fileDescriptor];
1159 @end /* NGBufferedStream(FastSocketForwarders) */