2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
6 SOPE 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 SOPE 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 SOPE; see the file COPYING. If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
24 #if defined(HAVE_UNISTD_H) || defined(__APPLE__)
28 #ifdef HAVE_SYS_SELECT_H
29 # include <sys/select.h>
31 #ifdef HAVE_SYS_FILIO_H
32 # include <sys/filio.h>
34 #if defined(HAVE_SYS_IOCTL_H)
35 # include <sys/ioctl.h>
37 #if defined(HAVE_TIME_H) || defined(__APPLE__)
40 #if defined(HAVE_SYS_TIME_H) || defined(__APPLE__)
41 # include <sys/time.h>
43 #if defined(HAVE_FCNTL_H) || defined(__APPLE__)
47 #if defined(__APPLE__)
48 # include <sys/types.h>
49 # include <sys/socket.h>
50 # include <sys/ioctl.h>
53 #if HAVE_WINDOWS_H && !defined(__CYGWIN32__)
57 #if defined(WIN32) && !defined(__CYGWIN32__)
59 # define ioctl ioctlsocket
64 #include <NGStreams/NGDescriptorFunctions.h>
65 #include "NGActiveSocket.h"
66 #include "NGSocketExceptions.h"
67 #include "NGSocket+private.h"
70 #if !defined(POLLRDNORM)
71 # define POLLRDNORM POLLIN
74 @interface NGActiveSocket(PrivateMethods)
76 - (id)_initWithDescriptor:(int)_fd
77 localAddress:(id<NGSocketAddress>)_local
78 remoteAddress:(id<NGSocketAddress>)_remote;
82 @implementation NGActiveSocket
84 #if !defined(WIN32) || defined(__CYGWIN32__)
86 + (BOOL)socketPair:(id<NGSocket>[2])_pair inDomain:(id<NGSocketDomain>)_domain {
92 if (socketpair([_domain socketDomain], SOCK_STREAM, [_domain protocol],
94 NGActiveSocket *s1 = nil;
95 NGActiveSocket *s2 = nil;
97 s1 = [[self alloc] _initWithDomain:_domain descriptor:fds[0]];
98 s2 = [[self alloc] _initWithDomain:_domain descriptor:fds[1]];
99 s1 = [s1 autorelease];
100 s2 = [s2 autorelease];
102 if ((s1 != nil) && (s2 != nil)) {
103 s1->mode = NGStreamMode_readWrite;
104 s1->receiveTimeout = 0.0;
105 s1->sendTimeout = 0.0;
106 s2->mode = NGStreamMode_readWrite;
107 s2->receiveTimeout = 0.0;
108 s2->sendTimeout = 0.0;
120 NSString *reason = nil;
124 reason = @"Not allowed to create socket of this type";
127 reason = @"Could not create socket: Insufficient user memory available";
129 case EPROTONOSUPPORT:
130 reason = @"The protocol is not supported by the address family or "
134 reason = @"The socket type is not supported by the protocol";
137 reason = @"Could not create socket: descriptor table is full";
140 reason = @"The specified protocol does not permit creation of socket "
146 NSLog(@"WARNING(%s): socketpair() call failed, but errno=0",
147 __PRETTY_FUNCTION__);
150 reason = [NSString stringWithFormat:@"Could not create socketpair: %s",
154 [[[NGCouldNotCreateSocketException alloc]
155 initWithReason:reason domain:_domain] raise];
162 + (id)socketConnectedToAddress:(id<NGSocketAddress>)_address {
163 volatile id sock = [[self alloc] initWithDomain:[_address domain]];
166 if (![sock connectToAddress:_address]) {
169 NSLog(@"WARNING(%s): Couldn't connect to address %@: %@",
170 __PRETTY_FUNCTION__, _address, [sock lastException]);
173 this method needs to raise the exception, since no object is returned
174 in which we could check the -lastException ...
176 e = [[sock lastException] retain];
182 sock = [sock autorelease];
187 - (id)initWithDomain:(id<NGSocketDomain>)_domain {
188 // designated initializer
189 if ((self = [super initWithDomain:_domain])) {
190 self->mode = NGStreamMode_readWrite;
191 self->receiveTimeout = 0.0;
192 self->sendTimeout = 0.0;
197 - (id)_initWithDescriptor:(int)_fd
198 localAddress:(id<NGSocketAddress>)_local
199 remoteAddress:(id<NGSocketAddress>)_remote
201 if ((self = [self _initWithDomain:[_local domain] descriptor:_fd])) {
202 ASSIGN(self->localAddress, _local);
203 ASSIGN(self->remoteAddress, _remote);
204 self->mode = NGStreamMode_readWrite;
206 #if !defined(WIN32) || defined(__CYGWIN32__)
207 NGAddDescriptorFlag(self->fd, O_NONBLOCK);
214 [self->remoteAddress release];
220 - (NSException *)lastException {
221 return [super lastException];
224 - (void)raise:(NSString *)_name reason:(NSString *)_reason {
228 clazz = NSClassFromString(_name);
229 NSAssert1(clazz, @"did not find exception class %@", _name);
233 if ([clazz instancesRespondToSelector:@selector(initWithReason:socket:)])
234 e = [(id)e initWithReason:_reason socket:self];
235 else if ([clazz instancesRespondToSelector:@selector(initWithStream:reason:)])
236 e = [(id)e initWithStream:self reason:_reason];
237 else if ([clazz instancesRespondToSelector:@selector(initWithSocket:)])
238 e = [(id)e initWithSocket:self];
239 else if ([clazz instancesRespondToSelector:@selector(initWithStream:)])
240 e = [(id)e initWithStream:self];
242 e = [e initWithReason:_reason];
245 if ([clazz instancesRespondToSelector:@selector(initWithSocket:)])
246 e = [(id)e initWithSocket:self];
247 else if ([clazz instancesRespondToSelector:@selector(initWithStream:)])
248 e = [(id)e initWithStream:self];
252 [self setLastException:e];
255 - (void)raise:(NSString *)_name {
256 [self raise:_name reason:nil];
259 - (BOOL)markNonblockingAfterConnect {
260 #if !defined(WIN32) || defined(__CYGWIN32__)
261 // mark socket as non-blocking
264 // on Win we only support blocking sockets right now ...
269 - (BOOL)primaryConnectToAddress:(id<NGSocketAddress>)_address {
271 // NGCouldNotConnectException if the the connect() call fails
273 [self resetLastException];
276 (struct sockaddr *)[_address internalAddressRepresentation],
277 [_address addressRepresentationSize]) != 0) {
278 NSString *reason = nil;
279 int errorCode = errno;
284 reason = @"search permission denied for element in path";
286 #if defined(WIN32) && !defined(__CYGWIN32__)
288 reason = @"address already in use";
290 case WSAEADDRNOTAVAIL:
291 reason = @"address is not available on remote machine";
293 case WSAEAFNOSUPPORT:
294 reason = @"addresses in the specified family cannot be used with the socket";
297 reason = @"a previous non-blocking attempt has not yet been completed";
300 reason = @"descriptor is invalid";
302 case WSAECONNREFUSED:
303 reason = @"connection refused";
306 reason = @"connect was interrupted";
309 reason = @"the address length is invalid";
312 reason = @"socket is already connected";
315 reason = @"network is unreachable";
318 reason = @"timeout occured";
322 reason = @"address already in use";
325 reason = @"address is not available on remote machine";
328 reason = @"addresses in the specified family cannot be used with the socket";
331 reason = @"a previous non-blocking attempt has not yet been completed";
334 reason = @"descriptor is invalid";
337 reason = @"connection refused";
340 reason = @"connect was interrupted";
343 reason = @"the address length is invalid";
346 reason = @"an IO error occured";
349 reason = @"socket is already connected";
352 reason = @"network is unreachable";
355 reason = @"timeout occured";
361 NSLog(@"WARNING(%s): connect() call failed, but errno=0",
362 __PRETTY_FUNCTION__);
366 reason = [NSString stringWithCString:strerror(errorCode)];
370 reason = [NSString stringWithFormat:@"Could not connect to address %@: %@",
373 e = [[NGCouldNotConnectException alloc]
374 initWithReason:reason socket:self address:_address];
375 [self setLastException:e];
380 /* connect was successful */
382 ASSIGN(self->remoteAddress, _address);
384 if ([self markNonblockingAfterConnect]) {
385 /* mark socket as non-blocking */
386 NGAddDescriptorFlag(self->fd, O_NONBLOCK);
387 NSAssert((NGGetDescriptorFlags(self->fd) & O_NONBLOCK),
388 @"could not enable non-blocking mode ..");
393 - (BOOL)connectToAddress:(id<NGSocketAddress>)_address {
395 // NGSocketAlreadyConnectedException if the socket is already connected
396 // NGInvalidSocketDomainException if the remote domain != local domain
397 // NGCouldNotCreateSocketException if the socket creation failed
399 if ([self isConnected]) {
400 [[[NGSocketAlreadyConnectedException alloc]
401 initWithReason:@"Could not connected: socket is already connected"
402 socket:self address:self->remoteAddress] raise];
406 // check whether the remote address is in the same domain like the bound one
408 if (![[localAddress domain] isEqual:[_address domain]]) {
409 [[[NGInvalidSocketDomainException alloc]
410 initWithReason:@"local and remote socket domains are different"
411 socket:self domain:[_address domain]] raise];
416 // connect, remote-address is non-nil if this returns
417 if (![self primaryConnectToAddress:_address])
420 // if the socket wasn't bound before (normal case), bind it now
422 if (![self kernelBoundAddress]) return NO;
426 - (void)_shutdownDuringOperation {
431 if (self->fd != NGInvalidSocketDescriptor) {
432 if (self->mode != NGStreamMode_undefined) {
433 if (shutdown(self->fd, SHUT_RDWR) == 0)
434 self->mode = NGStreamMode_undefined;
437 #if defined(WIN32) && !defined(__CYGWIN32__)
438 if (closesocket(self->fd) == 0) {
440 if (close(self->fd) == 0) {
442 self->fd = NGInvalidSocketDescriptor;
445 NSLog(@"ERROR(%s): close of socket %@ (fd=%i) alive=%s failed: %s",
447 self, self->fd, [self isAlive] ? "YES" : "NO", strerror(errno));
450 ASSIGN(self->remoteAddress, (id)nil);
455 - (BOOL)shutdownSendChannel {
456 if (NGCanWriteInStreamMode(self->mode)) {
457 shutdown(self->fd, SHUT_WR);
459 if (self->mode == NGStreamMode_readWrite)
460 self->mode = NGStreamMode_readOnly;
462 self->mode = NGStreamMode_undefined;
463 #if defined(WIN32) && !defined(__CYGWIN32__)
464 closesocket(self->fd);
468 self->fd = NGInvalidSocketDescriptor;
473 - (BOOL)shutdownReceiveChannel {
474 if (NGCanReadInStreamMode(self->mode)) {
475 shutdown(self->fd, SHUT_RD);
477 if (self->mode == NGStreamMode_readWrite)
478 self->mode = NGStreamMode_writeOnly;
480 self->mode = NGStreamMode_undefined;
481 #if defined(WIN32) && !defined(__CYGWIN32__)
482 closesocket(self->fd);
486 self->fd = NGInvalidSocketDescriptor;
492 // ******************** accessors ******************
494 - (id<NGSocketAddress>)remoteAddress {
495 return self->remoteAddress;
498 - (BOOL)isConnected {
499 return (self->remoteAddress != nil);
502 return [self isConnected];
509 - (void)setSendTimeout:(NSTimeInterval)_timeout {
510 self->sendTimeout = _timeout;
512 - (NSTimeInterval)sendTimeout {
513 return self->sendTimeout;
516 - (void)setReceiveTimeout:(NSTimeInterval)_timeout {
517 self->receiveTimeout = _timeout;
519 - (NSTimeInterval)receiveTimeout {
520 return self->receiveTimeout;
523 - (BOOL)wouldBlockInMode:(NGStreamMode)_mode {
526 if ((![self isConnected]) || (fd == NGInvalidSocketDescriptor))
529 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM;
530 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
532 // timeout of 0 means return immediatly
533 return (NGPollDescriptor([self fileDescriptor], events, 0) == 1 ? NO : YES);
536 - (int)waitForMode:(NGStreamMode)_mode timeout:(NSTimeInterval)_timeout {
539 if (NGCanReadInStreamMode(_mode)) events |= POLLRDNORM;
540 if (NGCanWriteInStreamMode(_mode)) events |= POLLWRNORM;
542 // timeout of 0 means return immediatly
543 return NGPollDescriptor([self fileDescriptor], events,
544 (int)(_timeout * 1000.0));
547 - (unsigned)numberOfAvailableBytesForReading {
550 // need to check whether socket is connected
551 if (self->remoteAddress == nil) {
552 [self raise:@"NGSocketNotConnectedException"
553 reason:@"socket is not connected"];
554 return NGStreamError;
557 if (!NGCanReadInStreamMode(self->mode)) {
558 [self raise:@"NGWriteOnlyStreamException"];
559 return NGStreamError;
562 #if !defined(WIN32) && !defined(__CYGWIN32__)
563 while (ioctl(self->fd, FIONREAD, &len) == -1) {
564 if (errno == EINTR) continue;
566 [self raise:@"NGSocketException"
567 reason:@"could not get number of available bytes"];
568 return NGStreamError;
571 // PeekNamedPipe() on Win ...
578 if (self->fd == NGInvalidSocketDescriptor)
581 /* poll socket for input */
588 FD_SET(self->fd, &readMask);
589 to.tv_sec = to.tv_usec = 0;
591 if (select(self->fd + 1, &readMask, NULL, NULL, &to) >= 0)
600 NSLog(@"socket select() failed: %s", strerror(errno));
605 /* no input is pending, connection is alive */
606 if (!FD_ISSET(self->fd, &readMask))
611 input is pending: If select() indicates pending input, but ioctl()
612 indicates zero bytes of pending input, the connection is broken
615 #if defined(WIN32) && !defined(__CYGWIN32__)
620 while (ioctl(self->fd, FIONREAD, &len) == -1) {
621 if (errno == EINTR) continue;
624 if (len > 0) return YES;
628 /* valid descriptor, but not alive .. so we close the socket */
629 #if defined(WIN32) && !defined(__CYGWIN32__)
630 closesocket(self->fd);
634 self->fd = NGInvalidSocketDescriptor;
635 RELEASE(self->remoteAddress); self->remoteAddress = nil;
639 // ******************** NGStream ********************
641 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
643 // NGStreamReadErrorException when the read call failed
644 // NGSocketNotConnectedException when the socket is not connected
645 // NGEndOfStreamException when the end of the stream is reached
646 // NGWriteOnlyStreamException when the receive channel was shutdown
647 NSException *e = nil;
649 if (self->fd == NGInvalidSocketDescriptor) {
650 [self raise:@"NGSocketException" reason:@"NGActiveSocket is not open"];
651 return NGStreamError;
654 // need to check whether socket is connected
655 if (self->remoteAddress == nil) {
656 [self raise:@"NGSocketNotConnectedException"
657 reason:@"socket is not connected"];
658 return NGStreamError;
661 if (!NGCanReadInStreamMode(self->mode)) {
662 [self raise:@"NGWriteOnlyStreamException"];
663 return NGStreamError;
666 if (_len == 0) return 0;
669 #if defined(WIN32) && !defined(__CYGWIN32__)
672 readResult = recv(self->fd, _buf, _len, 0);
673 if (readResult == 0) {
674 [self _shutdownDuringOperation];
675 [self raise:@"NGSocketShutdownDuringReadException"];
676 return NGStreamError;
678 else if (readResult < 0) {
679 int errorCode = WSAGetLastError();
683 e = [[NGSocketConnectionResetException alloc] initWithStream:self];
686 e = [[NGSocketTimedOutException alloc] initWithStream:self];
690 NSLog(@"WARNING: descriptor would block ..");
693 e = [[NGStreamReadErrorException alloc]
694 initWithStream:self errorCode:errorCode];
698 [self setLastException:e];
700 return NGStreamError;
706 NSAssert(_buf, @"invalid buffer");
707 NSAssert1(_len > 0, @"invalid length: %i", _len);
709 readResult = NGDescriptorRecv(self->fd, _buf, _len, 0,
710 (self->receiveTimeout == 0.0)
711 ? -1 // block until data
712 : (int)(self->receiveTimeout * 1000.0));
714 if ((readResult < 0) && (errno == EINVAL)) {
715 NSLog(@"%s: invalid argument in NGDescriptorRecv(%i, 0x%p, %i, %i)",
717 self->fd, _buf, _len, 0,
718 (self->receiveTimeout == 0.0)
719 ? -1 // block until data
720 : (int)(self->receiveTimeout * 1000.0));
724 if (readResult == 0) {
725 [self _shutdownDuringOperation];
726 [self raise:@"NGSocketShutdownDuringReadException"];
727 return NGStreamError;
729 else if (readResult == -2) {
730 [self raise:@"NGSocketTimedOutException"];
731 return NGStreamError;
733 else if (readResult < 0) {
734 int errorCode = errno;
740 /* this happens with the Oracle7 adaptor !!! */
741 NSLog(@"WARNING(%s): readResult<0 (%i), but errno=0 - retry",
742 __PRETTY_FUNCTION__, readResult);
748 e = [[NGSocketConnectionResetException alloc] initWithStream:self];
751 e = [[NGSocketTimedOutException alloc] initWithStream:self];
755 NSLog(@"WARNING: descriptor would block ..");
758 e = [[NGStreamReadErrorException alloc]
759 initWithStream:self errorCode:errorCode];
763 [self setLastException:e];
765 return NGStreamError;
773 #if defined(WIN32) && !defined(__CYGWIN32__)
774 #warning fix exception handling
776 - (unsigned)_winWriteBytes:(const void *)_buf count:(unsigned)_len {
777 NSException *e = nil;
780 writeResult = send(self->fd, _buf, _len, 0);
782 if (writeResult == 0) {
783 [self _shutdownDuringOperation];
784 [self raise:@"NGSocketShutdownDuringWriteException"];
785 return NGStreamError;
787 else if (writeResult < 0) {
788 int errorCode = WSAGetLastError();
792 e = [[NGSocketConnectionResetException alloc] initWithStream:self];
795 e = [[NGSocketTimedOutException alloc] initWithStream:self];
799 NSLog(@"WARNING: descriptor would block ..");
802 e = [[NGStreamWriteErrorException alloc]
803 initWithStream:self errorCode:errno];
807 [self setLastException:e];
809 return NGStreamError;
817 - (unsigned)_unixWriteBytes:(const void *)_buf count:(unsigned)_len {
823 timeOut = (self->sendTimeout == 0.0)
824 ? -1 // block until data
825 : (int)(self->sendTimeout * 1000.0);
828 writeResult = NGDescriptorSend(self->fd, _buf, _len, 0, timeOut);
830 if (writeResult == 0) {
831 [self _shutdownDuringOperation];
832 [self raise:@"NGSocketShutdownDuringWriteException"];
833 return NGStreamError;
835 else if (writeResult == -2) {
836 [self raise:@"NGSocketTimedOutException"];
837 return NGStreamError;
839 else if (writeResult < 0) {
840 int errorCode = errno;
845 /* this happens with the Oracle7 (on SuSE < 7.1??) adaptor !!! */
846 NSLog(@"WARNING(%s): writeResult<0 (%i), but errno=0 - retry",
847 __PRETTY_FUNCTION__, writeResult);
850 if (retryCount > 200000) {
851 NSLog(@"WARNING(%s): writeResult<0 (%i), but errno=0 - cancel retry "
852 @"(already tried %i times !!!)",
853 __PRETTY_FUNCTION__, writeResult, retryCount);
854 [self _shutdownDuringOperation];
855 [self raise:@"NGSocketShutdownDuringWriteException"];
856 return NGStreamError;
864 [self raise:@"NGSocketConnectionResetException"];
865 return NGStreamError;
867 [self raise:@"NGSocketTimedOutException"];
868 return NGStreamError;
871 [self _shutdownDuringOperation];
872 [self raise:@"NGSocketShutdownDuringWriteException"];
873 return NGStreamError;
876 NSLog(@"WARNING: descriptor would block ..");
880 e = [[NGStreamWriteErrorException alloc]
881 initWithStream:self errorCode:errno];
882 [self setLastException:e];
884 return NGStreamError;
893 - (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len {
895 // NGStreamWriteErrorException when the write call failed
896 // NGSocketNotConnectedException when the socket is not connected
897 // NGReadOnlyStreamException when the send channel was shutdown
899 if (_len == NGStreamError) {
900 NSLog(@"ERROR(%s): got NGStreamError passed in as length ...",
901 __PRETTY_FUNCTION__);
902 return NGStreamError;
905 if (_len > (1024 * 1024 * 100 /* 100MB */)) {
906 NSLog(@"WARNING(%s): got passed in length %uMB (%u bytes, errcode=%u) ...",
907 __PRETTY_FUNCTION__, (_len / 1024 / 1024), _len, NGStreamError);
911 if (self->fd == NGInvalidSocketDescriptor) {
912 [self raise:@"NGSocketException" reason:@"NGActiveSocket is not open"];
913 return NGStreamError;
916 // need to check whether socket is connected
917 if (self->remoteAddress == nil) {
918 [self raise:@"NGSocketNotConnectedException"
919 reason:@"socket is not connected"];
920 return NGStreamError;
923 if (!NGCanWriteInStreamMode(self->mode)) {
924 [self raise:@"NGReadOnlyStreamException"];
925 return NGStreamError;
928 //NSLog(@"writeBytes: count:%u", _len);
930 #if defined(WIN32) && !defined(__CYGWIN32__)
931 return [self _winWriteBytes:_buf count:_len];
933 return [self _unixWriteBytes:_buf count:_len];
942 return [self shutdown];
946 - (NGStreamMode)mode {
950 /* methods method which write exactly _len bytes or fail */
952 - (BOOL)safeReadBytes:(void *)_buf count:(unsigned)_len {
953 volatile int toBeRead;
956 unsigned (*readBytes)(id, SEL, void *, unsigned);
958 *(&readBytes) = (void *)[self methodForSelector:@selector(readBytes:count:)];
965 readBytes(self, @selector(readBytes:count:), pos, toBeRead);
967 if (readResult == NGStreamError) {
968 NSException *localException = [self lastException];
971 data = [NSData dataWithBytes:_buf length:(_len - toBeRead)];
973 localException = [[[localException class] alloc]
975 readCount:(_len - toBeRead)
978 [self setLastException:localException];
979 RELEASE(localException);
982 NSAssert(readResult != 0, @"ERROR: readBytes may not return '0' ..");
984 if (readResult == toBeRead) {
985 // all bytes were read successfully, return
989 if (readResult < 1) {
990 [NSException raise:NSInternalInconsistencyException
991 format:@"readBytes:count: returned a value < 1"];
994 toBeRead -= readResult;
1001 - (BOOL)safeWriteBytes:(const void *)_buf count:(unsigned)_len {
1002 int toBeWritten = _len;
1004 void *pos = (void *)_buf;
1006 /* method cache (THREAD, reentrant) */
1007 static Class lastClass = Nil;
1008 static int (*writeBytes)(id,SEL,const void*,unsigned) = NULL;
1010 if (lastClass == *(Class *)self) {
1011 if (writeBytes == NULL)
1013 (void *)[self methodForSelector:@selector(writeBytes:count:)];
1016 lastClass = *(Class *)self;
1017 writeBytes = (void *)[self methodForSelector:@selector(writeBytes:count:)];
1022 (int)writeBytes(self, @selector(writeBytes:count:), pos, toBeWritten);
1024 if (writeResult == NGStreamError) {
1025 /* remember number of written bytes ??? */
1028 else if (writeResult == toBeWritten) {
1029 // all bytes were written successfully, return
1033 if (writeResult < 1) {
1034 [NSException raise:NSInternalInconsistencyException
1035 format:@"writeBytes:count: returned a value < 1 in stream %@",
1040 toBeWritten -= writeResult;
1050 [self raise:@"NGStreamException" reason:@"stream doesn't support a mark"];
1053 - (BOOL)markSupported {
1057 // convenience methods
1059 - (int)readByte { // java semantics (-1 returned on EOF)
1063 result = [self readBytes:&c count:sizeof(unsigned char)];
1066 static Class EOFExcClass = Nil;
1068 if (EOFExcClass == Nil)
1069 EOFExcClass = [NGEndOfStreamException class];
1071 if ([[self lastException] isKindOfClass:EOFExcClass])
1072 [self resetLastException];
1081 - (NSString *)modeDescription {
1082 NSString *result = @"<unknown>";
1084 switch ([self mode]) {
1085 case NGStreamMode_undefined: result = @"<closed>"; break;
1086 case NGStreamMode_readOnly: result = @"r"; break;
1087 case NGStreamMode_writeOnly: result = @"w"; break;
1088 case NGStreamMode_readWrite: result = @"rw"; break;
1090 [[[NGUnknownStreamModeException alloc] initWithStream:self] raise];
1096 - (NSString *)description {
1097 NSMutableString *d = [NSMutableString stringWithCapacity:64];
1099 [d appendFormat:@"<%@[0x%p]: mode=%@ address=%@",
1100 NSStringFromClass([self class]),
1102 [self modeDescription], [self localAddress]];
1104 if ([self isConnected])
1105 [d appendFormat:@" connectedTo=%@", [self remoteAddress]];
1107 if ([self sendTimeout] != 0.0)
1108 [d appendFormat:@" send-timeout=%4.3fs", [self sendTimeout]];
1109 if ([self receiveTimeout] != 0.0)
1110 [d appendFormat:@" receive-timeout=%4.3fs", [self receiveTimeout]];
1112 [d appendString:@">"];
1116 @end /* NGActiveSocket */
1118 @implementation NGActiveSocket(DataMethods)
1120 - (NSData *)readDataOfLength:(unsigned int)_length {
1124 if (_length == 0) return [NSData data];
1126 readCount = [self readBytes:buf count:_length];
1127 return [NSData dataWithBytes:buf length:readCount];
1130 - (NSData *)safeReadDataOfLength:(unsigned int)_length {
1133 if (_length == 0) return [NSData data];
1134 [self safeReadBytes:buf count:_length];
1135 return [NSData dataWithBytes:buf length:_length];
1138 - (unsigned int)writeData:(NSData *)_data {
1139 return [self writeBytes:[_data bytes] count:[_data length]];
1141 - (BOOL)safeWriteData:(NSData *)_data {
1142 return [self safeWriteBytes:[_data bytes] count:[_data length]];
1145 @end /* NGActiveSocket(DataMethods) */
1147 #include <NGStreams/NGBufferedStream.h>
1149 @implementation NGBufferedStream(FastSocketForwarders)
1151 - (BOOL)isConnected {
1152 return [(id)self->source isConnected];
1154 - (int)fileDescriptor {
1155 return [(NSFileHandle *)self->source fileDescriptor];
1158 @end /* NGBufferedStream(FastSocketForwarders) */