--- a/Socket.st Sat Mar 08 11:57:34 2008 +0100
+++ b/Socket.st Fri Mar 28 13:47:59 2008 +0100
@@ -1517,7 +1517,7 @@
listenOn:aPortNr backlogSize:aNumber
self bindTo:aPortNr address:nil.
- self listenWithBacklog:aNumber
+ self listenFor:aNumber
"Created: / 31-05-2007 / 17:59:47 / cg"
!
@@ -1530,7 +1530,7 @@
!
primSocketLocalPort:aSocket
- ^ port
+ ^ self port
!
sendData: aStringOrByteArray
@@ -1588,6 +1588,305 @@
"Created: / 04-06-2007 / 21:28:40 / cg"
! !
+!Socket methodsFor:'accepting connections'!
+
+accept
+ "create a new TCP socket from accepting on the receiver.
+ This method will suspend the current process if no connection is waiting.
+ For ST-80 compatibility"
+
+ |newSock|
+
+ self readWait.
+ newSock := self class new.
+ (newSock primAcceptOn:self blocking:false) ifFalse:[
+ "should raise an error here"
+ ^ nil
+ ].
+ ^ newSock
+
+ "
+ |sock newSock|
+
+ sock := Socket provide:8004.
+ sock listenFor:5.
+ newSock := sock accept.
+ "
+!
+
+blockingAccept
+ "create a new TCP socket from accepting on the receiver.
+ This method will suspend the smalltalk image with all smalltalk processes if no connection is waiting.
+ For ST-80 compatibility"
+
+ |newSock|
+
+ newSock := self class new.
+ (newSock primAcceptOn:self blocking:true) ifFalse:[
+ "should raise an error here"
+ ^ nil
+ ].
+ ^ newSock
+! !
+
+!Socket methodsFor:'binding'!
+
+bindAnonymously
+ "bind to any address. A free port will be allocated.
+ Our own socket address will be determined after conection set up.
+ This is the default after the socket has been created"
+
+ ^ self
+ bindTo:nil
+ address:nil
+ reuseAddress:false
+!
+
+bindAnonymouslyToAddress:addressString
+ "bind to address addressString.
+ A free port will be allocated"
+
+ ^ self
+ bindTo:nil
+ address:addressString
+ reuseAddress:false
+!
+
+bindTo:aSocketAddress
+ "ST80 compatible bind, expecting a socketAddress argument.
+ The socketAddress object (an instance of SocketAddress)
+ is supposed to respond to #portOrName and #address requests."
+
+ ^ self bindTo:(aSocketAddress portOrName)
+ address:(aSocketAddress address)
+ reuseAddress:true
+!
+
+bindTo:portNrOrNameString address:addressString
+ "low level bind - returns true if ok, false otherwise.
+ Currently only non-address binding is supported;
+ i.e. the address must always be nil.
+
+ The interpretation of portNrOrName depends on the domain:
+ inet domain uses (4byte) byteArray like internet numbers,
+ unix domain uses pathname strings,
+ others use whatever will come up in the future
+ "
+
+ ^ self
+ bindTo:portNrOrNameString
+ address:addressString
+ reuseAddress:true
+!
+
+bindTo:portNrOrNameOrNil address:hostOrPathNameOrSocketAddrOrNil reuseAddress:reuse
+ "low level bind - returns true if ok, false otherwise.
+ Currently only non-address binding is supported;
+ i.e. address must always be nil.
+
+ The interpretation of portNrOrName depends on the domain:
+ inet domain uses (4byte) byteArray like internet numbers,
+ unix domain uses pathname strings,
+ others use whatever will come up in the future
+
+ The reuse boolean argument controls if the SO_REUSEADDR socket option
+ is to be set (to avoid the 'bind: address in use' error).
+ "
+
+ |ok addr addrName domainClass error|
+
+ filePointer isNil ifTrue:[
+ ^ self errorNotOpen
+ ].
+
+ domainClass := self class socketAddressClassForDomain:domain.
+ domainClass isNil ifTrue:[
+ ^ self error:'invalid (unsupported) domain'.
+ ].
+
+ hostOrPathNameOrSocketAddrOrNil isNil ifTrue:[
+ addr := domainClass anyHost.
+ ] ifFalse:[
+ (hostOrPathNameOrSocketAddrOrNil isKindOf:SocketAddress) ifTrue:[
+ addr := hostOrPathNameOrSocketAddrOrNil.
+ ] ifFalse:[
+ "backward compatibility: support for byteArray and string arg"
+ hostOrPathNameOrSocketAddrOrNil isString ifTrue:[
+ addr := domainClass hostName:hostOrPathNameOrSocketAddrOrNil.
+ addrName := hostOrPathNameOrSocketAddrOrNil.
+ ] ifFalse:[
+ hostOrPathNameOrSocketAddrOrNil isByteArray ifFalse:[
+ ^ self error:'bad host (socketAddress) argument'
+ ].
+ addr := domainClass hostAddress:hostOrPathNameOrSocketAddrOrNil.
+ ].
+ ].
+ ].
+ portNrOrNameOrNil notNil ifTrue:[
+ addr port:portNrOrNameOrNil.
+ ].
+ (portNrOrNameOrNil isNil or:[portNrOrNameOrNil == 0]) ifTrue:[
+ addr := addr copy.
+ ].
+
+%{ /* STACK: 100000 */
+#ifndef NO_SOCKET
+ OBJ fp = __INST(filePointer);
+
+ if (! __isBytes(addr)) {
+ error=__mkSmallInteger(-1);
+ addr = nil;
+ goto getOutOfHere;
+ }
+ if (fp != nil) {
+ SOCKET sock;
+ union sockaddr_u sa;
+ int sockaddr_size;
+ int ret;
+ int sockAddrOffs;
+
+ {
+ int nIndex;
+ OBJ cls;
+
+ sockAddrOffs = 0;
+ if ((cls = __qClass(addr)) != @global(ByteArray))
+ sockAddrOffs += __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
+ nIndex = __qSize(addr) - OHDR_SIZE;
+ sockaddr_size = nIndex - sockAddrOffs;
+ if (sockaddr_size > sizeof(sa)) {
+ error=__mkSmallInteger(-2);
+ goto getOutOfHere;
+ }
+ memcpy(&sa, __byteArrayVal(addr) + sockAddrOffs, sockaddr_size);
+ }
+
+ sock = SOCKET_FROM_FILE_OBJECT(fp);
+
+# ifdef SO_REUSEADDR
+ if (reuse == true) {
+ int on = 1;
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) {
+ DBGPRINTF(("SOCKET: setsockopt - SO_REUSEADDR failed\n"));
+ }
+ }
+# endif /* SO_REUSEADDR */
+
+# ifdef BIND_BLOCKS
+# ifdef DO_WRAP_CALLS
+ do {
+ __threadErrno = 0;
+ ret = STX_WSA_CALL3("bind", bind, sock, &sa, sockaddr_size);
+ } while ((ret < 0) && (__threadErrno == EINTR));
+# else
+ __BEGIN_INTERRUPTABLE__
+ do {
+ ret = bind(sock, (struct sockaddr *)&sa, sockaddr_size);
+ } while ((ret < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+# endif
+# else
+ ret = bind(sock, (struct sockaddr *)&sa, sockaddr_size);
+# endif
+ if (ret < 0) {
+ DBGPRINTF(("SOCKET: bind failed errno=%d\n", errno));
+ error = __INST(lastErrorNumber) = __MKSMALLINT(errno);
+ goto getOutOfHere;
+ } else {
+ ok = true;
+
+ if (! __isSmallInteger(portNrOrNameOrNil)
+ || (portNrOrNameOrNil == __MKSMALLINT(0))) {
+ unsigned int alen = sockaddr_size;
+
+ /*
+ * anonymous port - get the actual portNr
+ */
+ if (getsockname(sock, (struct sockaddr *)&sa, &alen) < 0) {
+# ifdef WIN32
+ errno = WSAGetLastError();
+# endif
+ console_fprintf(stderr, "SOCKET: cannot get socketname: %d\n", errno);
+ }
+ memcpy(__byteArrayVal(addr) + sockAddrOffs, &sa, alen);
+ }
+ }
+ }
+#endif /* NO_SOCKET */
+
+getOutOfHere: ;
+%}.
+ ok ~~ true ifTrue:[
+ "maybe someone catches the error and binds to some other port..."
+ OpenError raiseRequestWith:self errorString:('cannot bind socket to port: <1p> address: <2p> (error=<3p>)'
+ expandMacrosWith:portNrOrNameOrNil
+ with:hostOrPathNameOrSocketAddrOrNil
+ with:error).
+ ^ true.
+ ].
+
+ port := addr port.
+
+ ^ true
+
+ "
+ (Socket domain:#inet type:#stream)
+ bindTo:21
+ address:nil
+ "
+!
+
+listenFor:aNumber
+ "start listening; return true if ok, false on error
+ aNumber is the number of connect requests, that may be queued on the socket"
+
+ filePointer isNil ifTrue:[
+ ^ self errorNotOpen
+ ].
+%{
+#ifndef NO_SOCKET
+ OBJ fp = __INST(filePointer);
+ SOCKET sock;
+ int ret;
+
+ if (! __isSmallInteger(aNumber)) {
+ DBGPRINTF(("SOCKET: invalid arg\n"));
+ RETURN (false);
+ }
+
+ sock = SOCKET_FROM_FILE_OBJECT(fp);
+
+#ifdef LISTEN_BLOCKS
+# ifdef DO_WRAP_CALLS
+ do {
+ __threadErrno = 0;
+ ret = STX_WSA_CALL2("listen", listen, sock, __intVal(aNumber));
+ } while ((ret < 0) && (__threadErrno == EINTR));
+# else
+ __BEGIN_INTERRUPTABLE__
+ do {
+ ret = listen(sock, __intVal(aNumber));
+ } while ((ret < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+# endif
+#else
+ ret = listen(sock, __intVal(aNumber));
+#endif
+
+ if (ret < 0) {
+ DBGPRINTF(("SOCKET: listen call failed errno=%d\n", errno));
+ __INST(lastErrorNumber) = __MKSMALLINT(errno);
+ RETURN (false);
+ }
+#else
+ RETURN (false);
+#endif
+%}.
+ listening := true.
+ ^ true
+! !
+
!Socket methodsFor:'closing'!
shutDown
@@ -1615,6 +1914,208 @@
self shutdown:1.
! !
+!Socket methodsFor:'connecting'!
+
+connectTo:hostOrPathName port:portNrOrName
+ "low level connect; connect to port, portNrOrNameOrNil on host, hostName.
+ For backward compatibility, host may be also a string or a byteArray,
+ but it is recommended to pass socketAddress instances.
+
+ Return true if ok, false otherwise.
+ The current process will block (but not the whole Smalltalk) until the connection is established.
+ See also: #connectTo:port:withTimeout: for a somewhat nicer interface."
+
+ ^ self connectTo:hostOrPathName port:portNrOrName withTimeout:nil
+!
+
+connectTo:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil withTimeout:timeout
+ "low level connect; connect to port, portNrOrNameOrNil on host, hostName.
+ For backward compatibility, host may be also a string or a byteArray,
+ but it is recommended to pass socketAddress instances.
+
+ Return true if ok, false otherwise.
+ The current process will block (but not the whole Smalltalk) until the connection is established,
+ or timeout millliseconds have passed."
+
+ |isAsync err domainClass addr addrName|
+
+ filePointer isNil ifTrue:[
+ ^ self errorNotOpen
+ ].
+
+ (hostOrPathNameOrSocketAddr isKindOf:SocketAddress) ifTrue:[
+ addr := hostOrPathNameOrSocketAddr.
+ portNrOrNameOrNil notNil ifTrue:[
+ addr port:portNrOrNameOrNil.
+ ].
+ ] ifFalse:[
+ "backward compatibility: support for byteArray and string arg"
+ domainClass := self class socketAddressClassForDomain:domain.
+ domainClass isNil ifTrue:[
+ ^ self error:'invalid (unsupported) domain'.
+ ].
+
+ hostOrPathNameOrSocketAddr isString ifTrue:[
+ addr := domainClass hostName:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil.
+ addrName := hostOrPathNameOrSocketAddr.
+ ] ifFalse:[
+ hostOrPathNameOrSocketAddr isByteCollection ifFalse:[
+ ^ self error:'bad host (socketAddress) argument'
+ ].
+ addr := domainClass hostAddress:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil.
+ ].
+ ].
+
+%{ /* STACK: 100000 */
+
+#ifndef NO_SOCKET
+ OBJ fp = __INST(filePointer);
+ union sockaddr_u sa;
+ SOCKET sock;
+ int a;
+ int ret, oldFlags;
+ int on = 1;
+ int sockaddr_size;
+
+ if (!__isNonNilObject(addr) || !__isBytes(addr)) {
+ DBGPRINTF(("SOCKET: invalid addrBytes\n"));
+ RETURN (false);
+ }
+
+ {
+ int sockAddrOffs, nIndex;
+ OBJ cls;
+
+ sockAddrOffs = 0;
+ if ((cls = __qClass(addr)) != @global(ByteArray))
+ sockAddrOffs += __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
+ nIndex = __qSize(addr) - OHDR_SIZE;
+ sockaddr_size = nIndex - sockAddrOffs;
+ if (sockaddr_size > sizeof(sa)) {
+ console_fprintf(stderr, "Socket: bad socketAddr\n");
+ RETURN (false);
+ }
+ bcopy((__byteArrayVal(addr) + sockAddrOffs), &sa, sockaddr_size);
+ }
+
+ sock = SOCKET_FROM_FILE_OBJECT(fp);
+
+# if defined(O_NONBLOCK)
+# if !defined(WIN32)
+ /*
+ * set to non-blocking and wait later
+ */
+ oldFlags = fcntl(sock, F_GETFL, 0);
+ /* on SUNOS4.x, would use fcntl(osfd, F_SETFL, flags | FNDELAY); */
+ fcntl(sock, F_SETFL, oldFlags | O_NONBLOCK);
+# endif
+# endif
+
+ /*
+ * connect
+ */
+# ifdef DO_WRAP_CALLS
+ do {
+ DBGFPRINTF((stderr, "SOCKET: connect...\n"));
+ __threadErrno = 0;
+ ret = STX_WSA_NOINT_CALL3("connect", connect, sock, &sa, sockaddr_size);
+ DBGFPRINTF((stderr, "SOCKET: connect(%d) -> %d (%d)\n", sock, ret, __threadErrno));
+ } while ((ret < 0) && (__threadErrno == EINTR));
+# else
+ __BEGIN_INTERRUPTABLE__
+ do {
+ ret = connect(sock, (struct sockaddr *)&sa, sockaddr_size);
+ } while ((ret < 0)
+ && ((errno == EINTR)
+# ifdef EAGAIN
+ || (errno == EAGAIN)
+# endif
+ ));
+ __END_INTERRUPTABLE__
+#endif
+
+ if (ret < 0) {
+# if defined(EINPROGRESS) || defined(EALREADY)
+ if (0
+# ifdef EINPROGRESS
+ || (errno == EINPROGRESS)
+# endif
+# ifdef EALREADY
+ || (errno == EALREADY)
+# endif
+ ) {
+ /*
+ * This was a nonblocking operation that will take some time.
+ * Do a select on read to get informed when the operation is ready.
+ */
+ DBGFPRINTF((stderr, "SOCKET: isAsync is true\n"));
+ isAsync = true;
+ } else
+# endif /* EINPROGRESS or EALREADY */
+ {
+ DBGFPRINTF((stderr, "SOCKET: connect failed ret=%d errno=%d __threadErrno=%d\n",
+ ret, errno, __threadErrno ));
+# ifdef DUMP_ADDRESS
+ {
+ char *cp = (char *)(&sa);
+ int i;
+
+ console_printf("address data:\n");
+ for (i=0; i<sockaddr_size; i++) {
+ console_printf(" %02x\n", *cp++);
+ }
+ }
+# endif
+ __INST(lastErrorNumber) = __MKSMALLINT(errno);
+ RETURN (false);
+ }
+ }
+
+# if defined(O_NONBLOCK)
+# if !defined(WIN32)
+ fcntl(sock, F_SETFL, oldFlags);
+# endif
+# endif
+
+# else /* NO_SOCKET */
+ RETURN (false);
+# endif /* NO_SOCKET */
+%}.
+ isAsync == true ifTrue:[
+ (self writeWaitWithTimeoutMs:timeout) ifTrue:[
+ "/ a timeout occured
+ "/ should cancel the connect?
+ ^ false.
+ ].
+ err := self getSocketError.
+ err ~~ 0 ifTrue:[
+ lastErrorNumber := err.
+ ^ false.
+ ].
+ ].
+
+ peer := addr.
+ peerName := addrName.
+ port isNil ifTrue:[
+ "socket has not been explicitly bound,
+ after connect it has been bound implicitly - fetch the port"
+ port := self getFullSocketAddress port.
+ ].
+ ^ true
+
+ "
+ |sock|
+ sock := Socket newTCP.
+ sock connectTo:'localhost' port:21 withTimeout:1000.
+ sock
+
+ |sock|
+ sock := Socket newTCP.
+ sock connectTo:'localhost' port:9876 withTimeout:2000.
+ sock
+ "
+! !
+
!Socket methodsFor:'datagram transmission'!
receiveBuffer:aDataBuffer start:startIndex for:nBytes
@@ -2082,216 +2583,6 @@
self primitiveFailed
! !
-!Socket methodsFor:'low level'!
-
-bindAnonymously
- "bind to any address. A free port will be allocated.
- Our own socket address will be determined after conection set up.
- This is the default after the socket has been created"
-
- ^ self
- bindTo:nil
- address:nil
- reuseAddress:false
-!
-
-bindAnonymouslyToAddress:addressString
- "bind to address addressString.
- A free port will be allocated"
-
- ^ self
- bindTo:nil
- address:addressString
- reuseAddress:false
-!
-
-bindTo:aSocketAddress
- "ST80 compatible bind, expecting a socketAddress argument.
- The socketAddress object (an instance of SocketAddress)
- is supposed to respond to #portOrName and #address requests."
-
- ^ self bindTo:(aSocketAddress portOrName)
- address:(aSocketAddress address)
- reuseAddress:true
-!
-
-bindTo:portNrOrNameString address:addressString
- "low level bind - returns true if ok, false otherwise.
- Currently only non-address binding is supported;
- i.e. the address must always be nil.
-
- The interpretation of portNrOrName depends on the domain:
- inet domain uses (4byte) byteArray like internet numbers,
- unix domain uses pathname strings,
- others use whatever will come up in the future
- "
-
- ^ self
- bindTo:portNrOrNameString
- address:addressString
- reuseAddress:true
-!
-
-bindTo:portNrOrNameOrNil address:hostOrPathNameOrSocketAddrOrNil reuseAddress:reuse
- "low level bind - returns true if ok, false otherwise.
- Currently only non-address binding is supported;
- i.e. address must always be nil.
-
- The interpretation of portNrOrName depends on the domain:
- inet domain uses (4byte) byteArray like internet numbers,
- unix domain uses pathname strings,
- others use whatever will come up in the future
-
- The reuse boolean argument controls if the SO_REUSEADDR socket option
- is to be set (to avoid the 'bind: address in use' error).
- "
-
- |ok addr addrName domainClass error|
-
- filePointer isNil ifTrue:[
- ^ self errorNotOpen
- ].
-
- domainClass := self class socketAddressClassForDomain:domain.
- domainClass isNil ifTrue:[
- ^ self error:'invalid (unsupported) domain'.
- ].
-
- hostOrPathNameOrSocketAddrOrNil isNil ifTrue:[
- addr := domainClass anyHost.
- ] ifFalse:[
- (hostOrPathNameOrSocketAddrOrNil isKindOf:SocketAddress) ifTrue:[
- addr := hostOrPathNameOrSocketAddrOrNil.
- ] ifFalse:[
- "backward compatibility: support for byteArray and string arg"
- hostOrPathNameOrSocketAddrOrNil isString ifTrue:[
- addr := domainClass hostName:hostOrPathNameOrSocketAddrOrNil.
- addrName := hostOrPathNameOrSocketAddrOrNil.
- ] ifFalse:[
- hostOrPathNameOrSocketAddrOrNil isByteArray ifFalse:[
- ^ self error:'bad host (socketAddress) argument'
- ].
- addr := domainClass hostAddress:hostOrPathNameOrSocketAddrOrNil.
- ].
- ].
- ].
- portNrOrNameOrNil notNil ifTrue:[
- addr port:portNrOrNameOrNil.
- ].
- (portNrOrNameOrNil isNil or:[portNrOrNameOrNil == 0]) ifTrue:[
- addr := addr copy.
- ].
-
-%{ /* STACK: 100000 */
-#ifndef NO_SOCKET
- OBJ fp = __INST(filePointer);
-
- if (! __isBytes(addr)) {
- error=__mkSmallInteger(-1);
- addr = nil;
- goto getOutOfHere;
- }
- if (fp != nil) {
- SOCKET sock;
- union sockaddr_u sa;
- int sockaddr_size;
- int ret;
- int sockAddrOffs;
-
- {
- int nIndex;
- OBJ cls;
-
- sockAddrOffs = 0;
- if ((cls = __qClass(addr)) != @global(ByteArray))
- sockAddrOffs += __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
- nIndex = __qSize(addr) - OHDR_SIZE;
- sockaddr_size = nIndex - sockAddrOffs;
- if (sockaddr_size > sizeof(sa)) {
- error=__mkSmallInteger(-2);
- goto getOutOfHere;
- }
- memcpy(&sa, __byteArrayVal(addr) + sockAddrOffs, sockaddr_size);
- }
-
- sock = SOCKET_FROM_FILE_OBJECT(fp);
-
-# ifdef SO_REUSEADDR
- if (reuse == true) {
- int on = 1;
-
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) {
- DBGPRINTF(("SOCKET: setsockopt - SO_REUSEADDR failed\n"));
- }
- }
-# endif /* SO_REUSEADDR */
-
-# ifdef BIND_BLOCKS
-# ifdef DO_WRAP_CALLS
- do {
- __threadErrno = 0;
- ret = STX_WSA_CALL3("bind", bind, sock, &sa, sockaddr_size);
- } while ((ret < 0) && (__threadErrno == EINTR));
-# else
- __BEGIN_INTERRUPTABLE__
- do {
- ret = bind(sock, (struct sockaddr *)&sa, sockaddr_size);
- } while ((ret < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-# endif
-# else
- ret = bind(sock, (struct sockaddr *)&sa, sockaddr_size);
-# endif
- if (ret < 0) {
- DBGPRINTF(("SOCKET: bind failed errno=%d\n", errno));
- error = __INST(lastErrorNumber) = __MKSMALLINT(errno);
- goto getOutOfHere;
- } else {
- ok = true;
-
- if (! __isSmallInteger(portNrOrNameOrNil)
- || (portNrOrNameOrNil == __MKSMALLINT(0))) {
- unsigned int alen = sockaddr_size;
-
- /*
- * anonymous port - get the actual portNr
- */
- if (getsockname(sock, (struct sockaddr *)&sa, &alen) < 0) {
-# ifdef WIN32
- errno = WSAGetLastError();
-# endif
- console_fprintf(stderr, "SOCKET: cannot get socketname: %d\n", errno);
- }
- memcpy(__byteArrayVal(addr) + sockAddrOffs, &sa, alen);
- }
- }
- }
-#endif /* NO_SOCKET */
-
-getOutOfHere: ;
-%}.
- ok ~~ true ifTrue:[
- "maybe someone catches the error and binds to some other port..."
- OpenError raiseRequestWith:self errorString:('cannot bind socket to port: <1p> address: <2p> (error=<3p>)'
- expandMacrosWith:portNrOrNameOrNil
- with:hostOrPathNameOrSocketAddrOrNil
- with:error).
- ^ true.
- ].
-
- peer := addr.
- port := addr port.
- peerName := addrName.
-
- ^ true
-
- "
- (Socket domain:#inet type:#stream)
- bindTo:21
- address:nil
- "
-! !
-
!Socket protectedMethodsFor:'low level'!
closeFile
@@ -2354,6 +2645,14 @@
!Socket methodsFor:'low level'!
+getSocketAdress
+ "BAD SPELLING, of #getSocketAddress, kept for compatibility with swazoo"
+
+ <resource: #obsolete>
+
+ ^ self getSocketAddress
+!
+
getSocketError
"get the SO_ERROR form the socket, which indicates the
result of an asynchronous operation"
@@ -2385,58 +2684,245 @@
%}
!
-listenFor:aNumber
- "same as listenWithBacklog: - for ST-80 compatibility"
-
- ^ self listenWithBacklog:aNumber
+listenWithBacklog:aNumber
+ <resource: #obsolete>
+ "same as listenFor: - backward compatibility with old ST/X"
+
+ ^ self listenFor:aNumber
!
-listenWithBacklog:aNumber
- "start listening; return true if ok, false on error"
-
- filePointer isNil ifTrue:[
- ^ self errorNotOpen
+pollingWaitForNewConnectionOrDataOnAny:otherConnections timeout:timeoutSeconds
+ <resource: #obsolete>
+ "stupid MSDOS does not support select on sockets (sigh).
+ Must poll here."
+
+ |millis newConnection|
+
+ millis := timeoutSeconds * 1000.
+ [millis > 0] whileTrue:[
+ otherConnections size > 0 ifTrue:[
+ otherConnections do:[:aConnection |
+ aConnection canReadWithoutBlocking ifTrue:[
+ ^ aConnection
+ ]
+ ].
+ ].
+ newConnection := self blockingAccept.
+ newConnection notNil ifTrue:[^ newConnection].
+ Delay waitForMilliseconds:20.
+ millis := millis - 20.
+ ].
+ ^ nil.
+!
+
+pollingWaitForNewConnectionWithTimeout:timeoutSeconds
+ <resource: #obsolete>
+ "stupid MSDOS does not support select on sockets (sigh).
+ Must poll here."
+
+ |millis newConnection|
+
+ timeoutSeconds notNil ifTrue:[
+ millis := timeoutSeconds * 1000.
+ ].
+ [millis isNil or:[millis > 0]] whileTrue:[
+ newConnection := self blockingAccept.
+ newConnection notNil ifTrue:[^ newConnection].
+ Delay waitForMilliseconds:20.
+ millis notNil ifTrue:[
+ millis := millis - 20.
+ ]
].
-%{
+ ^ nil.
+!
+
+primAcceptOn:aSocket blocking:blocking
+ "accept a connection on a server port (created with:'Socket>>onIPPort:')
+ usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
+ Return the true if ok; false if not.
+ If blocking is true, the accept() syscall (and the whole smalltalk image)
+ will block, until a connection is accepted.
+ If blocking is false, this call will return immediately, if there is no connection pending."
+
+ |serverSocketFd addr addrLen domainClass|
+
+ filePointer notNil ifTrue:[
+ ^ self errorAlreadyOpen
+ ].
+
+ domain := aSocket domain.
+ socketType := aSocket type.
+ serverSocketFd := aSocket fileDescriptor.
+ serverSocketFd isNil ifTrue:[
+ ^ self error:'invalid server socket'
+ ].
+ (serverSocketFd isMemberOf:SmallInteger) ifFalse:[
+ ^ self error:'invalid server socket'
+ ].
+
+ domainClass := self class socketAddressClassForDomain:domain.
+ domainClass isNil ifTrue:[
+ ^ self error:'invalid (unsupported) domain'.
+ ].
+ addrLen := domainClass socketAddressSize.
+ addr := domainClass new.
+
+%{ /* STACK: 100000 */
#ifndef NO_SOCKET
- OBJ fp = __INST(filePointer);
- SOCKET sock;
- int ret;
-
- if (! __isSmallInteger(aNumber)) {
- DBGPRINTF(("SOCKET: invalid arg\n"));
- RETURN (false);
+ FILE *fp;
+ int flags;
+ SOCKET sock, newSock;
+ union sockaddr_u sa;
+ unsigned int alen, alen0;
+ struct hostent *he ;
+ char dotted[20] ;
+
+ if (!__isSmallInteger(addrLen)) {
+ DBGPRINTF(("SOCKET: bad addrLen\n"));
+ RETURN (false);
}
-
- sock = SOCKET_FROM_FILE_OBJECT(fp);
-
-#ifdef LISTEN_BLOCKS
+ alen0 = __intVal(addrLen);
+ sock = SOCKET_FROM_FD(__intVal(serverSocketFd));
+
+ if (blocking == false) {
+# if defined(O_NONBLOCK) && defined(SET_NDELAY)
+ flags = fcntl(sock, F_GETFL);
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+# endif
+ }
+
# ifdef DO_WRAP_CALLS
do {
- __threadErrno = 0;
- ret = STX_WSA_CALL2("listen", listen, sock, __intVal(aNumber));
- } while ((ret < 0) && (__threadErrno == EINTR));
+ __threadErrno = 0;
+ alen = alen0;
+ newSock = STX_WSA_CALL3("accept", accept, sock, &sa, &alen);
+ } while ((newSock < 0) && (__threadErrno == EINTR));
# else
__BEGIN_INTERRUPTABLE__
do {
- ret = listen(sock, __intVal(aNumber));
- } while ((ret < 0) && (errno == EINTR));
+ alen = alen0;
+ newSock = accept(sock, (struct sockaddr *) &sa, &alen);
+ } while ((newSock < 0) && (errno == EINTR));
__END_INTERRUPTABLE__
# endif
-#else
- ret = listen(sock, __intVal(aNumber));
-#endif
-
- if (ret < 0) {
- DBGPRINTF(("SOCKET: listen call failed errno=%d\n", errno));
- __INST(lastErrorNumber) = __MKSMALLINT(errno);
- RETURN (false);
+ DBGFPRINTF((stderr, "SOCKET: accept newSock=%d\n", newSock));
+
+ if (blocking == false) {
+# if defined(O_NDELAY) && defined(SET_NDELAY)
+ fcntl(sock, F_SETFL, flags);
+# endif
+ }
+
+ if (newSock < 0) {
+ DBGPRINTF(("SOCKET: accept call failed errno=%d\n", errno));
+ __INST(lastErrorNumber) = __MKSMALLINT(errno);
+ RETURN (false);
+ }
+
+ if (__isNonNilObject(addr)) {
+ OBJ oClass;
+ int nInstVars, nInstBytes, objSize;
+ char *cp;
+
+ oClass = __qClass(addr);
+ if (! __isBytes(addr) ) {
+ DBGPRINTF(("SOCKET: bad addr\n"));
+ closesocket(newSock);
+ RETURN (false);
+ }
+
+ nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + (nInstVars * sizeof(OBJ));
+ objSize = __qSize(addr) - nInstBytes;
+ cp = (char *)__InstPtr(addr) + nInstBytes;
+ if (objSize < alen) {
+ DBGPRINTF(("SOCKET: bad addr\n"));
+ closesocket(newSock);
+ RETURN (false);
+ }
+
+ /*
+ * extract the partners address
+ */
+ bcopy((char *)&sa, cp, alen);
+ addrLen = __MKSMALLINT(alen);
+ }
+
+ /*
+ * make it a FILE *
+ */
+# ifdef WIN32
+ {
+ int _fd = _open_osfhandle(newSock, 0);
+ fp = fdopen(_fd, "r+");
+ DBGPRINTF(("SOCKET: sock=%d fd=%d fp=%x\n",newSock,_fd, fp));
}
-#else
- RETURN (false);
-#endif
+# else
+ fp = fdopen(newSock, "r+");
+# endif
+
+ if (! fp) {
+ DBGPRINTF(("SOCKET: fdopen call failed\n"));
+ __INST(lastErrorNumber) = __MKSMALLINT(errno);
+# ifdef DO_WRAP_CALLS
+ {
+ int ret;
+ do {
+ __threadErrno = 0;
+ ret = STX_WSA_CALL1("closesocket", closesocket, newSock);
+ } while ((ret < 0) && (__threadErrno == EINTR));
+ }
+# else
+ closesocket(newSock);
+# endif
+ DBGFPRINTF((stderr, "SOCKET: close (fdopen failed) (%d)\n", newSock));
+ RETURN (false);
+ }
+
+ if ((@global(FileOpenTrace) == true) || __debugging__) {
+# ifdef WIN32
+ {
+ HANDLE h;
+ int _fd = fileno(fp);
+ h = (HANDLE)_get_osfhandle(_fd);
+ console_fprintf(stderr, "fdopen [Socket] -> %x (fd: %d) (H: %x)\n", fp, _fd, h);
+ }
+# else
+ console_fprintf(stderr, "fdopen [Socket] -> %x (fd: %d)\n", fp, newSock);
+# endif
+ }
+
+# ifdef BUGGY_STDIO_LIB
+ setbuf(fp, NULL);
+ __INST(buffered) = false;
+# endif
+
+# if 0
+ // The original code was:
+ __INST(filePointer) = __MKOBJ((INT)fp); __STORESELF(filePointer);
+ // but for that, gcc generates wrong code, which loads self (volatile) into
+ // a register (bp), then calls __MKOBJ, then stores indirect bp.
+ // That is wrong if a scavenge occurs in MKOBJ, as bp is now still pointing to the old
+ // object.
+# endif
+ {
+ OBJ t;
+
+ t = __MKOBJ(fp);
+ __INST(filePointer) = t;
+ __STORE(self, t);
+ }
+#endif /* not NO_SOCKET */
%}.
- listening := true.
+ mode := #readwrite.
+ Lobby register:self.
+ binary := false.
+ port := aSocket port.
+
+ addr notNil ifTrue:[
+ peer := addr.
+ ].
+
^ true
! !
@@ -2671,579 +3157,13 @@
%}.
! !
-!Socket methodsFor:'low level-accepting'!
-
-accept
- "create a new TCP socket from accepting on the receiver.
- This method will suspend the current process if no connection is waiting.
- For ST-80 compatibility"
-
- |newSock|
-
- newSock := self class new.
- (newSock acceptOn:self) ifFalse:[^ nil].
- ^ newSock
-
- "
- |sock newSock|
-
- sock := Socket provide:8004.
- sock listenFor:5.
- newSock := sock accept.
- "
-!
-
-acceptOn:aSocket
- "accept a connection on a server port (created with:'Socket>>onIPPort:')
- usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
- This method will suspend the current process if no connection is waiting.
- Return the true if ok; false if not."
-
- aSocket readWait.
- ^ self primAcceptOn:aSocket blocking:false.
-
- "Modified: / 11.3.1996 / 14:21:31 / stefan"
- "Modified: / 1.8.1998 / 23:39:10 / cg"
-!
-
-blockingAccept
- "create a new TCP socket from accepting on the receiver.
- This method will suspend the current process if no connection is waiting.
- For ST-80 compatibility"
-
- |newSock|
-
- newSock := self class new.
- (newSock blockingAcceptOn:self) ifFalse:[^ nil].
- ^ newSock
-!
-
-blockingAcceptOn:aSocket
- "accept a connection on a server port (created with:'Socket>>onIPPort:')
- usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
- This method will suspend the current process if no connection is waiting.
- Return the true if ok; false if not."
-
- ^ self primAcceptOn:aSocket blocking:true.
-!
-
-pollingWaitForNewConnectionOrDataOnAny:otherConnections timeout:timeoutSeconds
- <resource: #obsolete>
- "stupid MSDOS does not support select on sockets (sigh).
- Must poll here."
-
- |millis newConnection|
-
- millis := timeoutSeconds * 1000.
- [millis > 0] whileTrue:[
- otherConnections size > 0 ifTrue:[
- otherConnections do:[:aConnection |
- aConnection canReadWithoutBlocking ifTrue:[
- ^ aConnection
- ]
- ].
- ].
- newConnection := self blockingAccept.
- newConnection notNil ifTrue:[^ newConnection].
- Delay waitForMilliseconds:20.
- millis := millis - 20.
- ].
- ^ nil.
-!
-
-pollingWaitForNewConnectionWithTimeout:timeoutSeconds
- <resource: #obsolete>
- "stupid MSDOS does not support select on sockets (sigh).
- Must poll here."
-
- |millis newConnection|
-
- timeoutSeconds notNil ifTrue:[
- millis := timeoutSeconds * 1000.
- ].
- [millis isNil or:[millis > 0]] whileTrue:[
- newConnection := self blockingAccept.
- newConnection notNil ifTrue:[^ newConnection].
- Delay waitForMilliseconds:20.
- millis notNil ifTrue:[
- millis := millis - 20.
- ]
- ].
- ^ nil.
-!
-
-primAcceptOn:aSocket blocking:blocking
- "accept a connection on a server port (created with:'Socket>>onIPPort:')
- usage is: (Socket basicNew acceptOn:(Socket onIPPort:9999)).
- Return the true if ok; false if not."
-
- |serverSocketFd addr addrLen domainClass|
-
- filePointer notNil ifTrue:[
- ^ self errorAlreadyOpen
- ].
-
- domain := aSocket domain.
- socketType := aSocket type.
- serverSocketFd := aSocket fileDescriptor.
- serverSocketFd isNil ifTrue:[
- ^ self error:'invalid server socket'
- ].
- (serverSocketFd isMemberOf:SmallInteger) ifFalse:[
- ^ self error:'invalid server socket'
- ].
-
- domainClass := self class socketAddressClassForDomain:domain.
- domainClass isNil ifTrue:[
- ^ self error:'invalid (unsupported) domain'.
- ].
- addrLen := domainClass socketAddressSize.
- addr := domainClass new.
-
-%{ /* STACK: 100000 */
-#ifndef NO_SOCKET
- FILE *fp;
- int flags;
- SOCKET sock, newSock;
- union sockaddr_u sa;
- unsigned int alen, alen0;
- struct hostent *he ;
- char dotted[20] ;
-
- if (!__isSmallInteger(addrLen)) {
- DBGPRINTF(("SOCKET: bad addrLen\n"));
- RETURN (false);
- }
- alen0 = __intVal(addrLen);
- sock = SOCKET_FROM_FD(__intVal(serverSocketFd));
-
- if (blocking == false) {
-# if defined(O_NONBLOCK) && defined(SET_NDELAY)
- flags = fcntl(sock, F_GETFL);
- fcntl(sock, F_SETFL, flags | O_NONBLOCK);
-# endif
- }
-
-# ifdef DO_WRAP_CALLS
- do {
- __threadErrno = 0;
- alen = alen0;
- newSock = STX_WSA_CALL3("accept", accept, sock, &sa, &alen);
- } while ((newSock < 0) && (__threadErrno == EINTR));
-# else
- __BEGIN_INTERRUPTABLE__
- do {
- alen = alen0;
- newSock = accept(sock, (struct sockaddr *) &sa, &alen);
- } while ((newSock < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-# endif
- DBGFPRINTF((stderr, "SOCKET: accept newSock=%d\n", newSock));
-
- if (blocking == false) {
-# if defined(O_NDELAY) && defined(SET_NDELAY)
- fcntl(sock, F_SETFL, flags);
-# endif
- }
-
- if (newSock < 0) {
- DBGPRINTF(("SOCKET: accept call failed errno=%d\n", errno));
- __INST(lastErrorNumber) = __MKSMALLINT(errno);
- RETURN (false);
- }
-
- if (__isNonNilObject(addr)) {
- OBJ oClass;
- int nInstVars, nInstBytes, objSize;
- char *cp;
-
- oClass = __qClass(addr);
- if (! __isBytes(addr) ) {
- DBGPRINTF(("SOCKET: bad addr\n"));
- closesocket(newSock);
- RETURN (false);
- }
-
- nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
- nInstBytes = OHDR_SIZE + (nInstVars * sizeof(OBJ));
- objSize = __qSize(addr) - nInstBytes;
- cp = (char *)__InstPtr(addr) + nInstBytes;
- if (objSize < alen) {
- DBGPRINTF(("SOCKET: bad addr\n"));
- closesocket(newSock);
- RETURN (false);
- }
-
- /*
- * extract the partners address
- */
- bcopy((char *)&sa, cp, alen);
- addrLen = __MKSMALLINT(alen);
- }
-
- /*
- * make it a FILE *
- */
-# ifdef WIN32
- {
- int _fd = _open_osfhandle(newSock, 0);
- fp = fdopen(_fd, "r+");
- DBGPRINTF(("SOCKET: sock=%d fd=%d fp=%x\n",newSock,_fd, fp));
- }
-# else
- fp = fdopen(newSock, "r+");
-# endif
-
- if (! fp) {
- DBGPRINTF(("SOCKET: fdopen call failed\n"));
- __INST(lastErrorNumber) = __MKSMALLINT(errno);
-# ifdef DO_WRAP_CALLS
- {
- int ret;
- do {
- __threadErrno = 0;
- ret = STX_WSA_CALL1("closesocket", closesocket, newSock);
- } while ((ret < 0) && (__threadErrno == EINTR));
- }
-# else
- closesocket(newSock);
-# endif
- DBGFPRINTF((stderr, "SOCKET: close (fdopen failed) (%d)\n", newSock));
- RETURN (false);
- }
-
- if ((@global(FileOpenTrace) == true) || __debugging__) {
-# ifdef WIN32
- {
- HANDLE h;
- int _fd = fileno(fp);
- h = (HANDLE)_get_osfhandle(_fd);
- console_fprintf(stderr, "fdopen [Socket] -> %x (fd: %d) (H: %x)\n", fp, _fd, h);
- }
-# else
- console_fprintf(stderr, "fdopen [Socket] -> %x (fd: %d)\n", fp, newSock);
-# endif
- }
-
-# ifdef BUGGY_STDIO_LIB
- setbuf(fp, NULL);
- __INST(buffered) = false;
-# endif
-
-# if 0
- // The original code was:
- __INST(filePointer) = __MKOBJ((INT)fp); __STORESELF(filePointer);
- // but for that, gcc generates wrong code, which loads self (volatile) into
- // a register (bp), then calls __MKOBJ, then stores indirect bp.
- // That is wrong if a scavenge occurs in MKOBJ, as bp is now still pointing to the old
- // object.
-# endif
- {
- OBJ t;
-
- t = __MKOBJ(fp);
- __INST(filePointer) = t;
- __STORE(self, t);
- }
-#endif /* not NO_SOCKET */
-%}.
- mode := #readwrite.
- Lobby register:self.
- binary := false.
- port := aSocket port.
-
- addr notNil ifTrue:[
- peer := addr.
- ].
-
- ^ true
-!
-
-waitForNewConnectionOrDataOnAny:otherConnections timeout:timeoutSeconds
- "suspend the current process, until either a new connection comes
- in at the receiver, or data arrives on any of the otherConnections.
- For a new connection, an accept is performed and the new socket is returned.
- For an old connection, that socket is returned.
- In any case, the caller gets a socket to operate on as return value,
- or nil, if a timeout occured.
- This method implements the inner wait-primitive of a multi-connection
- server application."
-
-
- |wasBlocked sema|
-
- "first, a quick check if data is already available"
- self canReadWithoutBlocking ifTrue:[
- ^ self accept.
- ].
- otherConnections do:[:aConnection |
- aConnection canReadWithoutBlocking ifTrue:[
- ^ aConnection
- ]
- ].
-
- [
- "check again - prevent incoming interrupts from disturbing our setup"
- wasBlocked := OperatingSystem blockInterrupts.
-
- self canReadWithoutBlocking ifTrue:[
- ^ self accept.
- ].
- otherConnections do:[:aConnection |
- aConnection canReadWithoutBlocking ifTrue:[
- ^ aConnection
- ]
- ].
-
- "nope - must wait"
- sema := Semaphore new name:'multiReadWait'.
- otherConnections do:[:aConnection |
- Processor signal:sema onInput:(aConnection fileDescriptor).
- ].
- Processor signal:sema onInput:(self fileDescriptor).
- timeoutSeconds notNil ifTrue:[
- Processor signal:sema afterSeconds:timeoutSeconds
- ].
- Processor activeProcess state:#ioWait.
- sema wait.
- ] ensure:[
- sema notNil ifTrue:[Processor disableSemaphore:sema].
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- ].
-
- "see who it was ..."
- self canReadWithoutBlocking ifTrue:[
- ^ self accept.
- ].
- otherConnections do:[:aConnection |
- aConnection canReadWithoutBlocking ifTrue:[
- ^ aConnection
- ]
- ].
-
- "none - a timeout"
- ^ nil
-!
-
-waitForNewConnectionWithTimeout:timeoutSeconds
- "suspend the current process, until a new connection comes
- in at the receiver or a timeout occurs.
- For a new connection, an accept is performed and the new socket is returned.
- Returns nil, if a timeout occured.
- This method implements the inner wait-primitive of a single-connection
- server application."
-
- (self readWaitWithTimeout:timeoutSeconds) ifTrue:[
- "a timeout occurred - no connection within timeout"
- ^ nil
- ].
- ^ self accept.
-! !
-
-!Socket methodsFor:'low level-connecting'!
-
-connectTo:hostOrPathName port:portNrOrName
- "low level connect; connect to port, portNrOrNameOrNil on host, hostName.
- For backward compatibility, host may be also a string or a byteArray,
- but it is recommended to pass socketAddress instances.
-
- Return true if ok, false otherwise.
- The current process will block (but not the whole Smalltalk) until the connection is established.
- See also: #connectTo:port:withTimeout: for a somewhat nicer interface."
-
- ^ self connectTo:hostOrPathName port:portNrOrName withTimeout:nil
-!
-
-connectTo:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil withTimeout:timeout
- "low level connect; connect to port, portNrOrNameOrNil on host, hostName.
- For backward compatibility, host may be also a string or a byteArray,
- but it is recommended to pass socketAddress instances.
-
- Return true if ok, false otherwise.
- The current process will block (but not the whole Smalltalk) until the connection is established,
- or timeout millliseconds have passed."
-
- |isAsync err domainClass addr addrName|
-
- filePointer isNil ifTrue:[
- ^ self errorNotOpen
- ].
-
- (hostOrPathNameOrSocketAddr isKindOf:SocketAddress) ifTrue:[
- addr := hostOrPathNameOrSocketAddr.
- portNrOrNameOrNil notNil ifTrue:[
- addr port:portNrOrNameOrNil.
- ].
- ] ifFalse:[
- "backward compatibility: support for byteArray and string arg"
- domainClass := self class socketAddressClassForDomain:domain.
- domainClass isNil ifTrue:[
- ^ self error:'invalid (unsupported) domain'.
- ].
-
- hostOrPathNameOrSocketAddr isString ifTrue:[
- addr := domainClass hostName:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil.
- addrName := hostOrPathNameOrSocketAddr.
- ] ifFalse:[
- hostOrPathNameOrSocketAddr isByteCollection ifFalse:[
- ^ self error:'bad host (socketAddress) argument'
- ].
- addr := domainClass hostAddress:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil.
- ].
- ].
-
-%{ /* STACK: 100000 */
-
-#ifndef NO_SOCKET
- OBJ fp = __INST(filePointer);
- union sockaddr_u sa;
- SOCKET sock;
- int a;
- int ret, oldFlags;
- int on = 1;
- int sockaddr_size;
-
- if (!__isNonNilObject(addr) || !__isBytes(addr)) {
- DBGPRINTF(("SOCKET: invalid addrBytes\n"));
- RETURN (false);
- }
-
- {
- int sockAddrOffs, nIndex;
- OBJ cls;
-
- sockAddrOffs = 0;
- if ((cls = __qClass(addr)) != @global(ByteArray))
- sockAddrOffs += __OBJS2BYTES__(__intVal(__ClassInstPtr(cls)->c_ninstvars));
- nIndex = __qSize(addr) - OHDR_SIZE;
- sockaddr_size = nIndex - sockAddrOffs;
- if (sockaddr_size > sizeof(sa)) {
- console_fprintf(stderr, "Socket: bad socketAddr\n");
- RETURN (false);
- }
- bcopy((__byteArrayVal(addr) + sockAddrOffs), &sa, sockaddr_size);
- }
-
- sock = SOCKET_FROM_FILE_OBJECT(fp);
-
-# if defined(O_NONBLOCK)
-# if !defined(WIN32)
- /*
- * set to non-blocking and wait later
- */
- oldFlags = fcntl(sock, F_GETFL, 0);
- /* on SUNOS4.x, would use fcntl(osfd, F_SETFL, flags | FNDELAY); */
- fcntl(sock, F_SETFL, oldFlags | O_NONBLOCK);
-# endif
-# endif
-
- /*
- * connect
- */
-# ifdef DO_WRAP_CALLS
- do {
- DBGFPRINTF((stderr, "SOCKET: connect...\n"));
- __threadErrno = 0;
- ret = STX_WSA_NOINT_CALL3("connect", connect, sock, &sa, sockaddr_size);
- DBGFPRINTF((stderr, "SOCKET: connect(%d) -> %d (%d)\n", sock, ret, __threadErrno));
- } while ((ret < 0) && (__threadErrno == EINTR));
-# else
- __BEGIN_INTERRUPTABLE__
- do {
- ret = connect(sock, (struct sockaddr *)&sa, sockaddr_size);
- } while ((ret < 0)
- && ((errno == EINTR)
-# ifdef EAGAIN
- || (errno == EAGAIN)
-# endif
- ));
- __END_INTERRUPTABLE__
-#endif
-
- if (ret < 0) {
-# if defined(EINPROGRESS) || defined(EALREADY)
- if (0
-# ifdef EINPROGRESS
- || (errno == EINPROGRESS)
-# endif
-# ifdef EALREADY
- || (errno == EALREADY)
-# endif
- ) {
- /*
- * This was a nonblocking operation that will take some time.
- * Do a select on read to get informed when the operation is ready.
- */
- DBGFPRINTF((stderr, "SOCKET: isAsync is true\n"));
- isAsync = true;
- } else
-# endif /* EINPROGRESS or EALREADY */
- {
- DBGFPRINTF((stderr, "SOCKET: connect failed ret=%d errno=%d __threadErrno=%d\n",
- ret, errno, __threadErrno ));
-# ifdef DUMP_ADDRESS
- {
- char *cp = (char *)(&sa);
- int i;
-
- console_printf("address data:\n");
- for (i=0; i<sockaddr_size; i++) {
- console_printf(" %02x\n", *cp++);
- }
- }
-# endif
- __INST(lastErrorNumber) = __MKSMALLINT(errno);
- RETURN (false);
- }
- }
-
-# if defined(O_NONBLOCK)
-# if !defined(WIN32)
- fcntl(sock, F_SETFL, oldFlags);
-# endif
-# endif
-
-# else /* NO_SOCKET */
- RETURN (false);
-# endif /* NO_SOCKET */
-%}.
- isAsync == true ifTrue:[
- (self writeWaitWithTimeoutMs:timeout) ifTrue:[
- "/ a timeout occured
- "/ should cancel the connect?
- ^ false.
- ].
- err := self getSocketError.
- err ~~ 0 ifTrue:[
- lastErrorNumber := err.
- ^ false.
- ].
- ].
- port := portNrOrNameOrNil.
- peer := addr.
- peerName := addrName.
- ^ true
-
- "
- |sock|
- sock := Socket newTCP.
- sock connectTo:'localhost' port:21 withTimeout:1000.
- sock
-
- |sock|
- sock := Socket newTCP.
- sock connectTo:'localhost' port:9876 withTimeout:2000.
- sock
- "
-! !
-
!Socket methodsFor:'printing & storing'!
printOn:aStream
aStream nextPutAll:self className; nextPutAll:'(protocol='.
protocol printOn:aStream.
aStream nextPutAll:' port='.
- port printOn:aStream.
+ self port printOn:aStream.
aStream nextPutAll:' peer='.
peer printOn:aStream.
aStream nextPut:$).
@@ -3258,19 +3178,19 @@
!
getFullSocketAddress
- "implemented for swazoo project (primitive code cant be loaded as extension)
+ "implemented for swazoo project (primitive code can't be loaded as extension)
Answer my own address (I am bound to this address).
Note that this address may change after a connect or accept."
|error domainClass addr addrLen|
filePointer isNil ifTrue:[
- ^ self errorNotOpen
+ ^ self errorNotOpen
].
domainClass := self class socketAddressClassForDomain:domain.
domainClass isNil ifTrue:[
- ^ self error:'invalid (unsupported) domain'.
+ ^ self error:'invalid (unsupported) domain'.
].
addrLen := domainClass socketAddressSize.
addr := domainClass new.
@@ -3286,31 +3206,31 @@
int addrObjSize, nAddrInstBytes;
if (!__isSmallInteger(addrLen)) {
- DBGPRINTF(("SOCKET: bad addrLen\n"));
- error = @symbol(badArgument);
- goto err;
+ DBGPRINTF(("SOCKET: bad addrLen\n"));
+ error = @symbol(badArgument);
+ goto err;
}
alen = alen0 = __intVal(addrLen);
if (!__isNonNilObject(addr) || !__isBytes(addr)) {
- DBGPRINTF(("SOCKET: bad addr\n"));
- error = @symbol(badArgument);
- goto err;
+ DBGPRINTF(("SOCKET: bad addr\n"));
+ error = @symbol(badArgument);
+ goto err;
}
{
- OBJ addrClass;
- int nAddrInstVars;
-
- addrClass = __qClass(addr);
- nAddrInstVars = __intVal(__ClassInstPtr(addrClass)->c_ninstvars);
- nAddrInstBytes = OHDR_SIZE + (nAddrInstVars * sizeof(OBJ));
- addrObjSize = __qSize(addr) - nAddrInstBytes;
- if (addrObjSize < alen0) {
- DBGPRINTF(("SOCKET: bad addr/alen\n"));
- error = @symbol(badArgument);
- goto err;
- }
+ OBJ addrClass;
+ int nAddrInstVars;
+
+ addrClass = __qClass(addr);
+ nAddrInstVars = __intVal(__ClassInstPtr(addrClass)->c_ninstvars);
+ nAddrInstBytes = OHDR_SIZE + (nAddrInstVars * sizeof(OBJ));
+ addrObjSize = __qSize(addr) - nAddrInstBytes;
+ if (addrObjSize < alen0) {
+ DBGPRINTF(("SOCKET: bad addr/alen\n"));
+ error = @symbol(badArgument);
+ goto err;
+ }
}
sock = SOCKET_FROM_FILE_OBJECT(fp);
@@ -3320,17 +3240,17 @@
ret = getsockname(sock, (struct sockaddr *)&sa, &alen);
if (ret < 0) {
# ifdef WIN32
- errno = WSAGetLastError();
+ errno = WSAGetLastError();
# endif
- DBGPRINTF(("SOCKET: getsocketname failed ret=%d errno=%d\n", ret, errno));
- error = __MKSMALLINT(errno);
- goto err;
+ DBGPRINTF(("SOCKET: getsocketname failed ret=%d errno=%d\n", ret, errno));
+ error = __MKSMALLINT(errno);
+ goto err;
}
if (addrObjSize < alen) {
- DBGPRINTF(("SOCKET: bad addr\n"));
- error = @symbol(badArgument);
- goto err;
+ DBGPRINTF(("SOCKET: bad addr\n"));
+ error = @symbol(badArgument);
+ goto err;
}
addrP = (char *)__InstPtr(addr) + nAddrInstBytes;
@@ -3344,7 +3264,7 @@
#endif /* NO_SOCKET */
%}.
error notNil ifTrue:[
- ^ self errorReporter reportOn:error
+ ^ self errorReporter reportOn:error
].
^ addr
!
@@ -3352,7 +3272,7 @@
getName
"return the name; here, we return the ports name"
- ^ port printString
+ ^ self port printString
!
getPeer
@@ -3360,9 +3280,6 @@
my hostname/port combination.
If you are interested in the hostname, use getPeerName directly."
- peer isNil ifTrue:[
- peer := self class peerFromDomain:domain name:peerName port:port.
- ].
^ peer
!
@@ -3382,15 +3299,6 @@
Note that this address may change after connect or accept."
^ self getFullSocketAddress hostAddress
- "/ ^ self getFullSocketAddress copyFrom:5 to:8
-!
-
-getSocketAdress
- "BAD SPELLING, of #getSocketAddress, kept for compatibility with swazoo"
-
- <resource: #obsolete>
-
- ^ self getSocketAddress
!
isActive
@@ -3403,66 +3311,17 @@
"return true, if the receiver has a connection"
^ self isActive
- and:[port notNil
- and:[peerName notNil or:[peer notNil]]]
-!
-
-old_getFullSocketAddress
- "implemented for swazoo project primitive code cant load as extension
- answer my own address (I am bound to this address).
- Note that this address may change after connect or accept."
-
- |socketAddress error|
-
- filePointer isNil ifTrue:[
- ^ self errorNotOpen
- ].
- socketAddress := ByteArray new:16.
-
-%{
-#ifndef NO_SOCKET
- OBJ fp = __INST(filePointer);
- SOCKET sock;
- unsigned int sockaddr_size;
- int ret;
-
- if (!__isNonNilObject(socketAddress) ||
- (__intVal(__ClassInstPtr(__qClass(socketAddress))->c_flags) & ARRAYMASK) != BYTEARRAY) {
- error = @symbol(badArgument1);
- goto err;
- }
- sockaddr_size = __byteArraySize(socketAddress);
-
- sock = SOCKET_FROM_FILE_OBJECT(fp);
- ret = getsockname(sock, (struct sockaddr *)__byteArrayVal(socketAddress), &sockaddr_size);
- if (ret < 0) {
-# ifdef WIN32
- errno = WSAGetLastError();
-# endif
- DBGPRINTF(("SOCKET: getsocketname failed errno=%d\n", errno));
- error = __MKSMALLINT(errno);
- }
-err:;
-#else /* NO_SOCKET */
- error = @symbol(notImplemented);
-#endif /* NO_SOCKET */
-%}.
- error notNil ifTrue:[
- ^ self errorReporter reportOn:error
- ].
- ^ socketAddress
+ and:[peer notNil]
!
port
- "return the port number (or name for unix-sockets) to which the socket is bound"
-
-"/ sometimes maybe we do am lazy fetch of the port
-"/ port == 0 ifTrue:[ |p|
-"/ p := self getPort.
-"/ p notNil ifTrue:[
-"/ port := p
-"/ ]
+ "return the port number (or name for unix-sockets) to which the socket is bound
+ - so this is the local port."
+
+"/ port isNil ifTrue:[
+"/ port := self getFullSocketAddress port.
"/ ].
+
^ port
!
@@ -3998,8 +3857,91 @@
^ false
! !
+!Socket methodsFor:'waiting'!
+
+waitForNewConnectionOrDataOnAny:otherConnections timeout:timeoutSeconds
+ "suspend the current process, until either a new connection comes
+ in at the receiver, or data arrives on any of the otherConnections.
+ For a new connection, an accept is performed and the new socket is returned.
+ For an old connection, that socket is returned.
+ In any case, the caller gets a socket to operate on as return value,
+ or nil, if a timeout occured.
+ This method implements the inner wait-primitive of a multi-connection
+ server application."
+
+
+ |wasBlocked sema|
+
+ "first, a quick check if data is already available"
+ self canReadWithoutBlocking ifTrue:[
+ ^ self accept.
+ ].
+ otherConnections do:[:aConnection |
+ aConnection canReadWithoutBlocking ifTrue:[
+ ^ aConnection
+ ]
+ ].
+
+ [
+ "check again - prevent incoming interrupts from disturbing our setup"
+ wasBlocked := OperatingSystem blockInterrupts.
+
+ self canReadWithoutBlocking ifTrue:[
+ ^ self accept.
+ ].
+ otherConnections do:[:aConnection |
+ aConnection canReadWithoutBlocking ifTrue:[
+ ^ aConnection
+ ]
+ ].
+
+ "nope - must wait"
+ sema := Semaphore new name:'multiReadWait'.
+ otherConnections do:[:aConnection |
+ Processor signal:sema onInput:(aConnection fileDescriptor).
+ ].
+ Processor signal:sema onInput:(self fileDescriptor).
+ timeoutSeconds notNil ifTrue:[
+ Processor signal:sema afterSeconds:timeoutSeconds
+ ].
+ Processor activeProcess state:#ioWait.
+ sema wait.
+ ] ensure:[
+ sema notNil ifTrue:[Processor disableSemaphore:sema].
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ].
+
+ "see who it was ..."
+ self canReadWithoutBlocking ifTrue:[
+ ^ self accept.
+ ].
+ otherConnections do:[:aConnection |
+ aConnection canReadWithoutBlocking ifTrue:[
+ ^ aConnection
+ ]
+ ].
+
+ "none - a timeout"
+ ^ nil
+!
+
+waitForNewConnectionWithTimeout:timeoutSeconds
+ "suspend the current process, until a new connection comes
+ in at the receiver or a timeout occurs.
+ For a new connection, an accept is performed and the new socket is returned.
+ Returns nil, if a timeout occured.
+ This method implements the inner wait-primitive of a single-connection
+ server application."
+
+ (self readWaitWithTimeout:timeoutSeconds) ifTrue:[
+ "a timeout occurred - no connection within timeout"
+ ^ nil
+ ].
+ ^ self accept.
+! !
+
!Socket class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libbasic2/Socket.st,v 1.242 2007-11-08 14:04:43 stefan Exp $'
+ ^ '$Header: /cvs/stx/stx/libbasic2/Socket.st,v 1.243 2008-03-28 12:47:59 stefan Exp $'
! !