Socket.st
author Claus Gittinger <cg@exept.de>
Mon, 20 Dec 2004 20:57:03 +0100
changeset 1504 92005eec5c9d
parent 1503 4d97e4191d04
child 1505 5f65beb4aedd
permissions -rw-r--r--
fixed moving-object-when-interrupted-with-scavenge bugs; preps to re-merge with win32_socket

"
 COPYRIGHT (c) 1992 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

"{ Package: 'stx:libbasic2' }"

NonPositionableExternalStream subclass:#Socket
	instanceVariableNames:'domain socketType protocol port serviceName peer peerName listening'
	classVariableNames:''
	poolDictionaries:''
	category:'Streams-External'
!

!Socket primitiveDefinitions!
%{

/* #define DGRAM_DEBUG /* */

#include "stxOSDefs.h"

#ifdef WIN32

# undef __BEGIN_INTERRUPTABLE__
# undef __END_INTERRUPTABLE__
# define __BEGIN_INTERRUPTABLE__ ..
# define __END_INTERRUPTABLE__ ..

# define WRAP_STDIO
# define DO_WRAP_CALLS

# ifndef DO_WRAP_CALLS
#  define STX_C_CALL0(__nm__, __f__)                                                    __f__()
#  define STX_C_CALL1(__nm__, __f__, __a1__)                                            __f__((__a1__))
#  define STX_C_CALL2(__nm__, __f__, __a1__, __a2__)                                    __f__((__a1__), (__a2__))
#  define STX_C_CALL3(__nm__, __f__, __a1__, __a2__, __a3__)                            __f__((__a1__), (__a2__), (__a3__))
#  define STX_API_CALL0(__nm__, __f__)                                                  __f__((__a1__))
#  define STX_API_CALL1(__nm__, __f__, __a1__)                                          __f__((__a1__))
#  define STX_API_CALL2(__nm__, __f__, __a1__, __a2__)                                  __f__((__a1__), (__a2__))
#  define STX_API_CALL3(__nm__, __f__, __a1__, __a2__, __a3__)                          __f__((__a1__), (__a2__), (__a3__))
#  define STX_API_CALL4(__nm__, __f__, __a1__, __a2__, __a3__, __a4__)                  __f__((__a1__), (__a2__), (__a3__), (__a4__))
#  define STX_API_CALL5(__nm__, __f__, __a1__, __a2__, __a3__, __a4__, __a5__)          __f__((__a1__), (__a2__), (__a3__), (__a4__), (__a5__))
#  define STX_API_CALL6(__nm__, __f__, __a1__, __a2__, __a3__, __a4__, __a5__, __a6__)  __f__((__a1__), (__a2__), (__a3__), (__a4__), (__a5__), (__a6__))
# else
#  define STX_C_CALL0(__nm__, __f__)                                                    __STX_C_CALL0(__nm__,(void*)__f__)
#  define STX_C_CALL1(__nm__, __f__, __a1__)                                            __STX_C_CALL1(__nm__,(void*)__f__, (void*)(__a1__))
#  define STX_C_CALL2(__nm__, __f__, __a1__, __a2__)                                    __STX_C_CALL2(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__))
#  define STX_C_CALL3(__nm__, __f__, __a1__, __a2__, __a3__)                            __STX_C_CALL3(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__), (void*)(__a3__))
#  define STX_API_CALL0(__nm__, __f__)                                                  __STX_API_CALL0(__nm__,(void*)__f__)
#  define STX_API_CALL1(__nm__, __f__, __a1__)                                          __STX_API_CALL1(__nm__,(void*)__f__, (void*)(__a1__))
#  define STX_API_CALL2(__nm__, __f__, __a1__, __a2__)                                  __STX_API_CALL2(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__))
#  define STX_API_CALL3(__nm__, __f__, __a1__, __a2__, __a3__)                          __STX_API_CALL3(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__), (void*)(__a3__))
#  define STX_API_CALL4(__nm__, __f__, __a1__, __a2__, __a3__, __a4__)                  __STX_API_CALL4(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__), (void*)(__a3__), (void*)(__a4__))
#  define STX_API_CALL5(__nm__, __f__, __a1__, __a2__, __a3__, __a4__, __a5__)          __STX_API_CALL5(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__), (void*)(__a3__), (void*)(__a4__), (void*)(__a5__))
#  define STX_API_CALL6(__nm__, __f__, __a1__, __a2__, __a3__, __a4__, __a5__, __a6__)  __STX_API_CALL6(__nm__,(void*)__f__, (void*)(__a1__), (void*)(__a2__), (void*)(__a3__), (void*)(__a4__), (void*)(__a5__), (void*)(__a6__))
# endif

# ifdef DO_WRAP_CALLS
#  define GETHOSTBYNAME(hp, name)                    \
      {                                              \
	char __hostNm[512];                          \
						     \
	strncpy(__hostNm, name, sizeof(__hostNm)-1); \
	__hostNm[sizeof(__hostNm)-1] = '\0';         \
	do {                                         \
	  __threadErrno = 0;                         \
	  hp = STX_API_CALL1("gethostbyname", gethostbyname, __hostNm); \
	} while ((hp < 0) && (__threadErrno == EINTR));      \
      }

#  define GETHOSTBYADDR(hp, addr, alen, af) \
      do {                                 \
	__threadErrno = 0;                 \
	hp = STX_API_CALL3("gethostbyaddr", gethostbyaddr, addr, alen, af);    \
      } while ((hp < 0) && (__threadErrno == EINTR));
# else
#  define GETHOSTBYNAME(hp, name) \
	__BEGIN_INTERRUPTABLE__  \
	hp = gethostbyname((char *) name); \
	__END_INTERRUPTABLE__

#  define GETHOSTBYADDR(hp, addr, alen, af) \
	__BEGIN_INTERRUPTABLE__ \
	hp = gethostbyaddr(addr, alen, af); \
	__END_INTERRUPTABLE__
# endif /* DO_WRAP_CALLS */

#else /* not WIN32 */

# define STX_C_CALL0(__nm__, __f__)                                                    __f__()
# define STX_C_CALL1(__nm__, __f__, __a1__)                                            __f__((__a1__))
# define STX_C_CALL2(__nm__, __f__, __a1__, __a2__)                                    __f__((__a1__), (__a2__))
# define STX_C_CALL3(__nm__, __f__, __a1__, __a2__, __a3__)                            __f__((__a1__), (__a2__), (__a3__))
# define STX_API_CALL0(__nm__, __f__)                                                  __f__((__a1__))
# define STX_API_CALL1(__nm__, __f__, __a1__)                                          __f__((__a1__))
# define STX_API_CALL2(__nm__, __f__, __a1__, __a2__)                                  __f__((__a1__), (__a2__))
# define STX_API_CALL3(__nm__, __f__, __a1__, __a2__, __a3__)                          __f__((__a1__), (__a2__), (__a3__))
# define STX_API_CALL4(__nm__, __f__, __a1__, __a2__, __a3__, __a4__)                  __f__((__a1__), (__a2__), (__a3__), (__a4__))
# define STX_API_CALL5(__nm__, __f__, __a1__, __a2__, __a3__, __a4__, __a5__)          __f__((__a1__), (__a2__), (__a3__), (__a4__), (__a5__))
# define STX_API_CALL6(__nm__, __f__, __a1__, __a2__, __a3__, __a4__, __a5__, __a6__)  __f__((__a1__), (__a2__), (__a3__), (__a4__), (__a5__), (__a6__))

#endif /* WIN32 */

#include <stdio.h>
#include <errno.h>

#ifdef WANT__TCP_DOT_H
# include <netinet/tcp.h>
#endif

#ifdef WANT__NETDB_DOT_H
# include <netdb.h>
#endif

#if defined(TRY_AGAIN) || defined(HOST_NOT_FOUND)
# define USE_H_ERRNO
#endif

/*
 * on some systems errno is a macro ... check for it here
 */
#ifndef errno
 extern errno;
#endif

#ifdef USE_H_ERRNO
# ifndef h_errno
 extern h_errno;
# endif
#endif

#ifdef DEBUG
# define DBGPRINTF(x)    { if (__debugging__) printf x; }
# define DBGFPRINTF(x)   { if (__debugging__) fprintf x; }
#else
# define DBGPRINTF(x)    /* as nothing */
# define DBGFPRINTF(x)   /* as nothing */
#endif
#ifdef SRWDEBUG
# define SRWPRINTF(x)    { if (__debugging__) printf x; }
#else
# define SRWPRINTF(x)    /* as nothing */
#endif

%}
! !

!Socket primitiveVariables!
%{
static int __debugging__ = 0;
%}
! !

!Socket primitiveFunctions!
%{

static
setupBufferParameters(aDataBuffer, startIndex, p_extPtr, p_offs, p_objSize)
    OBJ aDataBuffer, startIndex;
    char **p_extPtr;
    int *p_offs;
    int *p_objSize;
{
	char *extPtr = 0;
	int sIdx = 0, objSize = 0, offs = 0;

	sIdx = 0;
	if (__isSmallInteger(startIndex)) {
	    sIdx = __intVal(startIndex) - 1;
	}

	if (__isExternalBytesLike(aDataBuffer)) {
	    OBJ sz;

	    extPtr = (char *)(__externalBytesAddress(aDataBuffer));
	    sz = __externalBytesSize(aDataBuffer);
	    if (__isSmallInteger(sz)) {
		objSize = __intVal(sz);
	    } else {
		objSize = 0; /* unknown */
	    }
	    offs = sIdx;
	} else {
	    OBJ oClass;
	    int nInstVars, nInstBytes;

	    oClass = __Class(aDataBuffer);
	    switch (__intVal(__ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
		case BYTEARRAY:
		    offs = sIdx;
		    break;
		case WORDARRAY:
		case SWORDARRAY:
		    offs = sIdx * 2;
		    break;
		case LONGARRAY:
		case SLONGARRAY:
		    offs = sIdx * 4;
		    break;
		case LONGLONGARRAY:
		case SLONGLONGARRAY:
		    offs = sIdx * 8;
# ifdef __NEED_LONGLONG_ALIGN
		    offs += 4;
# endif
		    break;
		case FLOATARRAY:
		    offs = sIdx * sizeof(float);
		    break;
		case DOUBLEARRAY:
		    offs = sIdx * sizeof(double);
# ifdef __NEED_DOUBLE_ALIGN
		    offs += 4;
# endif
		    break;
		default:
		    objSize = -1;
		    break;
	    }
	    extPtr = (char *)0;
	    nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
	    nInstBytes = OHDR_SIZE + nInstVars * sizeof(OBJ);
	    offs = offs + nInstBytes;
	    objSize = _Size(aDataBuffer) - offs;
	}
	*p_extPtr = extPtr;
	*p_objSize = objSize;
	*p_offs = offs;
}
%}
! !

!Socket class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1992 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    This class provides access to sockets for interprocess communication.
    The message protocol is preliminary, until someone tells me how
    other smalltalk's socket interfaces look like.

    Currently, only IP and UNIX domain sockets are supported.
    Code for appletalk is included, but was never tested ...
    More may be added in the future.
    (the code is prepared for things like SNA or decNet;
     however, right now, this code is empty and needs a little work.
     Implementing those is pretty straight forward, once the address
     data structures are known.)

    Due to historic reasons (I started this class, before I got hold of some
    code using ST-80 Sockets i.e. RemoteInvocation), there is some old interface
    still provided.
    This will vanish; use the #family:type: or #newTCPxxx and #newUDPxxx interfaces,
    together with the bind/listen and accept calls,
    which are meant to be compatible to ST-80's UnixSocketAccessor interface.

    ST/X does not yet use IPSocketAddress, UDSocketAddress etc; all addressing
    is done by passing appropriate string- or byteArray objects containing
    the addresses. This may change, too.

    TODO: cleanup historic leftovers, implement other than inet domain stuff.
	  (mhmh - how can I test those ?)
	  change to raise more signals on errors.
	  Help - could someone who has a linux machine connected to an appletalk
		 net please test this (and send bug fixes ...)

    [author:]
	Claus Gittinger
"
!

examples
"
    example (get help info from an nntp server):
									[exBegin]
	|sock host|

	host := OperatingSystem getEnvironment:'NNTPSERVER'.

	sock := Socket newTCPclientToHost:host port:'nntp'.
	Transcript showCR:sock nextLine.
	sock buffered:false.

	sock nextPutAll:'HELP'; cr.
	[:exit |
	    |line|

	    line := sock nextLine.
	    line = '.' ifTrue:[exit value:nil].
	    Transcript showCR:line.
	] loopWithExit.
	sock close
									[exEnd]


    example (connect to finger daemon, get users entry):
									[exBegin]
	|sock host entry|

	host := OperatingSystem getHostName.

	sock := Socket newTCPclientToHost:host port:'finger'.
	sock isNil ifTrue:[
	    self warn:'no finger daemon is running'.
	    ^ self
	].
	sock useCRLF:true.
	sock buffered:false.
	sock isNil ifTrue:[
	    Transcript showCR:'cannot connect to local finger daemon'
	] ifFalse:[
	    sock nextPutAll:(OperatingSystem getLoginName).
	    sock cr.

	    entry := sock nextLine.
	    Transcript showCR:entry.

	    sock close
	]
									[exEnd]

    example (connect to an ftp server):
									[exBegin]
	|sock host|

	host := OperatingSystem getHostName.
	sock := Socket newTCPclientToHost:host port:'ftp'.

	sock buffered:false.
	Transcript showCR:sock nextLine.
	sock nextPutAll:('USER ' , 'anonymous'); cr.
	Transcript showCR:sock nextLine.
	sock nextPutAll:('PASS ' , 'fooBar'); cr.
	Transcript showCR:sock nextLine.
	sock nextPutAll:'LIST'; cr.
	Transcript showCR:sock nextLine.
	sock close.

	'dont know enough of the ftp protocol to continue here ...'
									[exEnd]


    example (connect to an snmp server [UDP]):
									[exBegin]
	|sock port|

	sock := Socket newUDP.
	port := Socket portOfService:'snmp'.
	sock connectTo:(OperatingSystem getHostName) port:port.
	sock buffered:false.
	Transcript showCR:'got it'.
	sock close.
									[exEnd]


    example (await connection from a client and read some data):
									[exBegin]
	|connectSock sock|

	connectSock := Socket newTCPserverAtPort:9998.
	connectSock isNil ifTrue:[
	    Transcript showCR:'socket setup failed.'.
	] ifFalse:[
	    Transcript showCR:'listen ..'.
	    (connectSock listenFor:5) ifFalse:[
		Transcript showCR:'listen failed.'.
	    ] ifTrue:[
		Transcript showCR:'wait'.
		connectSock readWait.
		Transcript showCR:'accept'.
		sock := connectSock accept.
		sock isNil ifTrue:[
		    Transcript showCR:'accept failed.'.
		] ifFalse:[
		    sock buffered:false.
		    Transcript showCR:'server: got it'.
		    'can now do transfer via sock'.
		    Transcript showCR:'read'.
		    Transcript showCR:('got: ' , sock nextLine).

		    Transcript showCR:'close'.
		    sock close
		].
		connectSock close.
	    ]
	]
									[exEnd]


    example (connect to above server and send some data):
									[exBegin]
	|sock|

	sock := Socket newTCPclientToHost:(OperatingSystem getHostName) port:9998.
	sock isNil ifTrue:[
	    Transcript showCR:'nope'
	] ifFalse:[
	    sock buffered:false.
	    Transcript showCR:'client: got it'.
	    'can now do transfer via sock'.
	    Transcript showCR:'sending <hello>'.
	    sock nextPutLine:'hello'.
	    sock close
	]
									[exEnd]

    example: UNIX domain socket (await connection from a client and read some data):

	|connectSock sock|

	'/tmp/ud_socket' asFilename remove.
	connectSock := Socket newUNIXserverAt:'/tmp/ud_socket'.
	connectSock isNil ifTrue:[
	    Transcript showCR:'socket setup failed.'.
	] ifFalse:[
	    Transcript showCR:'listen ..'.
	    (connectSock listenFor:5) ifFalse:[
		Transcript showCR:'listen failed.'.
	    ] ifTrue:[
		Transcript showCR:'wait'.
		connectSock buffered:false.
		connectSock readWait.
		Transcript showCR:'accept'.
		sock := connectSock accept.
		sock isNil ifTrue:[
		    Transcript showCR:'accept failed.'.
		] ifFalse:[
		    sock buffered:false.
		    Transcript showCR:'server: got it'.
		    'can now do transfer via sock'.
		    Transcript showCR:'read'.
		    Transcript showCR:('got: ' , sock nextLine).

		    Transcript showCR:'close'.
		    sock close
		].
		connectSock close.
	    ]
	]


    example (connect to above server and send some data;
	     Notice, this fails, if above server code is executed in the same ST/X image
		     (at least on LINUX), since the OS does not correctly handle
		     a connect from within an interrupted accept system call
		     On SGI's SVR4, this works ok
									[exBegin]
	|sock|

	sock := Socket newUNIXclientTo:'/tmp/ud_socket'.
	sock isNil ifTrue:[
	    Transcript showCR:'nope'
	] ifFalse:[
	    sock buffered:false.
	    Transcript showCR:'client: got it'.
	    'can now do transfer via sock'.
	    Transcript showCR:'sending <hello>'.
	    sock nextPutLine:'hello'.
	    sock close
	]
									[exEnd]


    example (UDP await packet from a client and read some data):
									[exBegin]
	|udpSock sock addr n dataBuffer|

	udpSock := Socket newUDPserverAtPort:9999.
	udpSock isNil ifTrue:[
	    Transcript showCR:'socket setup failed.'.
	] ifFalse:[
	    Transcript showCR:'wait'.
	    udpSock readWait.

	    addr := IPSocketAddress new.
	    dataBuffer := ByteArray new:1000.
	    n := udpSock receiveFrom:addr buffer:dataBuffer start:1 for:dataBuffer size.
	    n > 0 ifTrue:[
		Transcript showCR:('got: ' , n printString , 'bytes  from ' , addr printString).
		Transcript showCR:('data: ' , (dataBuffer copyTo:n) printString).
	    ] ifFalse:[
		Transcript showCR:'read failed'.
	    ].

	    Transcript showCR:'close'.
	    udpSock close
	]
									[exEnd]
    example (connect to above UDP server and send some data;
									[exBegin]
	|sock|

	sock := Socket newUDP.
	sock isNil ifTrue:[
	    Transcript showCR:'nope'
	] ifFalse:[
	    sock sendTo:(IPSocketAddress new hostName:'localhost' port:9999) buffer:'hello world'.
	    sock close
	]
									[exEnd]

    example: pingWalk (try to ping hosts on the local network)
									[exBegin]
	|myName myAddress list top hosts walkProcess port|

	myName := OperatingSystem getHostName.
	myAddress := Socket ipAddressOfHost:myName.

	port := Socket portOfService:'echo'.
	port isNil ifTrue:[
	    self error:'dont know echo port'.
	    ^ self
	].

	top := StandardSystemView new.
	top label:'PING net walk'.

	list := ScrollableView for:ListView in:top.
	list origin:0.0@0.0 corner:1.0@1.0.

	top openAndWait.

	walkProcess := [
	    |l low hi direction tryHostID dottedName hostName conn addr|

	    l := SortedCollection new.

	    ' only works with type C-net
	      the code below could simply do 1 to:254 do:[:hostID }
	      but, to probe likely hosts earlier, the probing is done
	      ping-pong like around my ip-address (assuming, that other machines
	      have numbers around my own)'.

	    low := hi := (myAddress at:4).
	    direction := 1.

	    [low > 0 or:[hi < 255]] whileTrue:[
		direction > 0 ifTrue:[
		    hi := hi + 1.
		    tryHostID := hi.
		    direction := -1.
		] ifFalse:[
		    low := low - 1.
		    tryHostID := low.
		    direction := 1.
		].
		(tryHostID between:1 and:254) ifTrue:[
		    dottedName := (myAddress at:1) printString
				  , '.' , (myAddress at:2) printString
				  , '.' , (myAddress at:3) printString
				  , '.' , tryHostID printString.

		    top label:'PING net walk - trying ' , dottedName.

		    top windowGroup withCursor:Cursor wait do:[
			conn := Socket newTCPclientToHost:dottedName port:port withTimeout:1000.
			conn notNil ifTrue:[
			    addr := Socket ipAddressOfHost:dottedName.
			    hostName := Socket hostWithIpAddress:addr.
			    hostName isNil ifTrue:[
				hostName :='?'
			    ].
			    l add:(dottedName paddedTo:15 with:Character space)
				   , ' '
				   , (hostName paddedTo:15 with:Character space)
				   , ' up & reachable'.
			    list list:l.
			    conn close.
			]
		    ].
		].
	    ].
	    top label:'PING reachable hosts'.
	] forkAt:(Processor userBackgroundPriority).
	walkProcess name:'ping net walker'.
									[exEnd]


	This example creates a simple UDP server that accepts
	single packets from anybody and broadcasts them to all
	clients that have connected so far.

									[exBegin]
	| socket address buffer msgSize clients |
	clients := Set new.
	address := IPSocketAddress new.
	buffer := String new: 1024.

	socket := self newUDPserverAtPort: 6666.

	Transcript showCR: 'server starting'.

	[
	    [true] whileTrue: [
		(socket readWaitWithTimeoutMs: 200) ifFalse: [
		    msgSize := socket
			    receiveFrom: address
			    buffer: buffer
			    start: 1
			    for: buffer size.

		    clients add: address copy.
		    clients do: [ :clientAddress |
			    socket
				    sendTo: clientAddress
				    buffer: buffer
				    start: 1
				    for: msgSize]]
	    ]
	] valueNowOrOnUnwindDo:[
	    Transcript showCR: 'server shutting down'.
	    socket close
	]
									[exEnd]

   send a datagram to above server:
									[exBegin]

	| socket address buffer host msg |

	host := Dialog
		request: 'What is the name of the server''s host?'
		initialAnswer: 'localhost'.

	socket := self newUDP.

	address := IPSocketAddress hostName: host port: 6666.

	buffer := ByteArray new: 1000.
	[
	    [(msg := Dialog request: 'Say something') isEmpty] whileFalse:[
		| replySize stream |

		socket writeWait.
		stream := buffer writeStream.
		stream nextPutAll: msg.
		socket sendTo:address buffer:buffer start:1 for:stream position.
		socket readWait.

		replySize := socket receiveFrom:address buffer:buffer.
		replySize > 0 ifTrue: [
		    Transcript cr; nextPutAll: 'Server acknowledged: '.
		    Transcript show: ((buffer copyFrom: 1 to: replySize) asString)
		]
	    ]
	] valueNowOrOnUnwindDo: [socket close].
	Transcript cr
									[exEnd]


   loopBack:
									[exBegin]

	|readerTask readingSocket writingSocket|

	readingSocket := self newTCPserverAtPort:9999.
	readerTask :=
	    [
		|connection|

		readingSocket listenFor:1.
		connection := readingSocket accept.
		readingSocket close.
		[connection atEnd] whileFalse:[
		    Transcript showCR:(connection nextLine).
		].
		connection close.
	    ] fork.

	Delay waitForSeconds:1.
	writingSocket := self newTCPclientToHost:(OperatingSystem getHostName) port:9999.
	writingSocket nextPutLine:'Hello'.
	writingSocket nextPutLine:'World'.
	writingSocket close.
									[exEnd]
"
! !

!Socket class methodsFor:'instance creation'!

domain:domainSymbol type:type
    "create a socket for domain and type -
     neither any connect nor binding is done.
     Domain must be one of the symbols:
	#inet, #unix, #appletalk, #decnet, #xns, ...;
     Type must be:
	#stream, #datagram or #raw

     XXX: currently only the #inet domain is supported"

    ^ self new domain:domainSymbol type:type

    "
     Socket domain:#inet type:#stream
     Socket domain:#inet type:#datagram
     Socket domain:#unix type:#stream
     Socket domain:#appletalk type:#stream
     Socket domain:#decnet type:#stream
     Socket domain:#irda type:#stream
    "
!

new
    "create a TCP socket"

    ^ super new buffered:false
!

newTCP
    "create a TCP socket - no binding or other setup is done,
     neither connect nor connect-wait is done."

    ^ self new domain:#inet type:#stream

    "Socket newUDP"
!

newTCP:aServiceOrNil
    "create a TCP socket for a service -
     neither connect nor connect-wait is done."

    |newSock|

    newSock := self newTCP.
    (newSock notNil and:[aServiceOrNil notNil]) ifTrue:[
	^ newSock tryToBindTo:(self portOfService:aServiceOrNil)
    ].
    ^ newSock


    "Socket newTCP:'nntp'"
    "Socket newTCP:9995"
!

newTCPclientToAddress:aHostAddress port:aService
    "create a new TCP client socket connecting to a service.
     Return a socket instance if ok, nil on failure.
     Block until a connection is established (but only the current thread;
     not the whole smalltalk).
     See also: #newTCPclientToAddress:port:withTimeout:"

    ^ self newTCPclientToAddress:aHostAddress port:aService withTimeout:nil
!

newTCPclientToAddress:aHostAddress port:aService withTimeout:millis
    "create a new TCP client socket connecting to a service.
     Return a socket instance if ok, nil on failure.
     If the millis arg is nonNil, stop trying to connect after that many milliseconds
     and return nil."

    ^ self newTCPclientToHost:aHostAddress port:aService withTimeout:millis
!

newTCPclientToHost:hostname port:aService
    "create a new TCP client socket connecting to a service.
     Return a socket instance if ok, nil on failure.
     Block until a connection is established (but only the current thread;
     not the whole smalltalk).
     See also: #newTCPclientToHost:port:withTimeout:"

    ^ self newTCPclientToHost:hostname port:aService withTimeout:nil

    "
      Socket newTCPclientToHost:'localhost' port:'nntp'
    "

    "Created: 31.10.1995 / 18:54:11 / cg"
!

newTCPclientToHost:hostname port:aService withTimeout:millis
    "create a new TCP client socket connecting to a service.
     Return a socket instance if ok, nil on failure.
     If the millis arg is nonNil, stop trying to connect after that many milliseconds
     and return nil.."

    |newSock|

    newSock := self newTCP.
    newSock notNil ifTrue:[
	(newSock connectTo:hostname
		 port:(self portOfService:aService protocol:'tcp')
		 withTimeout:millis
	) ifFalse:[
	    newSock close.
	    ^ nil
	]
    ].
    ^ newSock
"
same as:
    ^ (self new) for:hostname port:(self portOfService:aPort).
"
    "
      Socket newTCPclientToHost:'slsv6bt' port:'nntp'
      Socket newTCPclientToHost:'localhost' port:'nntp' withTimeout:1000
    "

    "Modified: / 16.1.1998 / 09:47:06 / stefan"
!

newTCPserverAtAnonymousPort
    "create a new TCP server socket providing service on
     a new anonymous port. The portNr is assigned by the OS."

    ^ self newTCPserverAtPort:0

!

newTCPserverAtPort:aService
    "create a new TCP server socket providing service."

    |newSock|

    newSock := self newTCP.
    newSock notNil ifTrue:[
	^ newSock tryToBindTo:(self portOfService:aService)
    ].
    ^ newSock
!

newUDP
    "create a UDP socket - no binding or other setup is done,
     neither connect nor connect-wait is done."

    ^ self new domain:#inet type:#datagram

    "Socket newUDP"
!

newUDP:aServiceOrNil
    "create a UDP socket for a service -
     neither connect nor connect-wait is done."

    |newSock|

    newSock := self newUDP.
    (newSock notNil and:[aServiceOrNil notNil]) ifTrue:[
	^ newSock tryToBindTo:(self portOfService:aServiceOrNil)
    ].
    ^ newSock

    "Socket newUDP:nil"
!

newUDPserverAtPort:aService
    "create a new UDP server socket providing service."

    |newSock|

    newSock := self newUDP.
    newSock notNil ifTrue:[
	^ newSock tryToBindTo:(self portOfService:aService)
    ].
    ^ newSock
"
same as:
    ^ (self new) for:nil udpPort:aPort
"
!

newUNIX
    "create a UNIX domain socket - no binding or other setup is done,
     neither connect nor connect-wait is done.
     If the system does not support unix domain sockets (i.e. VMS or MSDOS),
     return nil."

    ^ self new domain:#unix type:#stream

    "
     Socket newUNIX
    "
!

newUNIXclientTo:pathName
    "create a new UNIX client socket connecting to a pathname.
     Return a socket instance if ok, nil on failure.
     Block until a connection is established (but only the current thread;
     not the whole smalltalk).
     If the system does not support unix domain sockets (i.e. VMS or MSDOS),
     return nil.
     See also: #newUNIXclientTo:withTimeout:"

    ^ self newUNIXclientTo:pathName withTimeout:nil

!

newUNIXclientTo:pathName withTimeout:millis
    "create a new UNIX client socket connecting to a pathname.
     Return a socket instance if ok, nil on failure.
     If the millis arg is nonNil, stop trying to connect after that many milliseconds
     and return nil.
     If the system does not support unix domain sockets (i.e. VMS or MSDOS),
     return nil."

    |newSock|

    newSock := self newUNIX.
    newSock notNil ifTrue:[
	(newSock connectTo:'localhost' port:pathName withTimeout:millis) ifFalse:[
	    newSock close.
	    ^ nil
	]
    ].
    ^ newSock

    "
     |s|

     s := Socket newUNIXclientTo:'/tmp/foo'
    "
!

newUNIXserverAt:pathName
    "create a new UNIX server socket providing service at a pathname.
     If the system does not support unix domain sockets (i.e. VMS or MSDOS),
     return nil."

    |newSock|

    newSock := self newUNIX.
    newSock notNil ifTrue:[
	^ newSock tryToBindTo:pathName
    ].
    ^ newSock

    "
     |s s2|

     s := Socket newUNIXserverAt:'/tmp/foo'.
     s listenFor:5.
     s2 := s accept.
    "
! !

!Socket class methodsFor:'Compatibility-ST80'!

family:domainSymbol type:typeSymbol
    "create a socket for domain and type - ST80 simply uses a different name.
     Domain must be one of the symbols: #inet, #unix, #appletalk or #ns;
     Type must be #stream, #datagram or #raw."

    ^ self domain:domainSymbol type:typeSymbol

    "
     Socket family:#inet type:#stream
     Socket family:#inet type:#datagram
     Socket family:#unix type:#stream
    "
!

getHostname
    "return the computers hostname string"

    ^ OperatingSystem getHostName

    "Created: / 27.2.1998 / 02:32:17 / cg"
!

sockStream
    "return the type code for stream sockets"

    ^ #stream
! !

!Socket class methodsFor:'Compatibility-VW'!

AF_INET
    ^ #AF_INET
!

SOCK_STREAM
    ^ #SOCK_STREAM
! !

!Socket class methodsFor:'Signal constants'!

brokenConnectionSignal
    "return the signal used to tell broken connections.
     Since in unix, this is the same as the broken pipe signal,
     return that one.
     (for other Operatingsystems, this may change ..)"

    ^ PipeStream brokenPipeSignal
!

invalidArgumentsSignal
    "dummy for compatibility"

    ^ self errorSignal
! !

!Socket class methodsFor:'debugging'!

debug:aBoolean
    "turn on/off internal debugprints.
     This method is for ST/X debugging only and
     may  be removed in later versions"

%{  /* NOCONTEXT */

    __debugging__ = (aBoolean == true);
%}
    "
     Socket debug:true
     Socket debug:false
    "
! !

!Socket class methodsFor:'host queries'!

appletalkAddressOfHost:aHostName
    "return the APPLETALK address for a hostname as a byteArray,
     where the network bytes come first (no matter what the local byteorder is)
     followed by the node byte.
     If the host is unknown, return nil.
     This is the reverse operation to #hostWithAppletalkAddress:.
     WARNING: untested code - I have no appletalk to test this."

    NameLookupError
	handle:[:ex |
	    ^ nil
	]
	do:[
	    ^ (AppletalkSocketAddress hostName:aHostName) address
	]
!

hostWithAppletalkAddress:addrByteArray
    "return the hostname for an APPLETALK address.
     The address is supposed to be a byteArray consisting of 3 bytes,
     the network bytes come first (no matter what the local byteorder is).
     The last byte is the node number.
     Nil is returned for an unknown host or if its not an appletalk host.
     This is is the reverse operation to #appletalkAddressOfHost:.
     WARNING: untested code - I have no appletalk to test this."

    NameLookupError
	handle:[:ex |
	    ^ nil
	]
	do:[
	    ^ (AppletalkSocketAddress hostAddress:addrByteArray) hostName
	]

    "
     Socket appletalkAddressOfHost:'yourAppleHere'
     Socket hostWithAppletalkAddress:#[1 2 3]
     "
!

hostWithIpAddress:addrByteArray
    "return the hostname for an IP (internet-) address.
     The address is supposed to be a byteArray consisting of 4 bytes,
     the network bytes come first (no matter what the local byteorder is).
     Nil is returned for an unknown host or if its not an internet host.
     This is the reverse operation to #ipAddressOfHost:."

    NameLookupError
	handle:[:ex |
	    ^ nil
	]
	do:[
	    ^ (IPSocketAddress hostAddress:addrByteArray) hostName
	]

    "
     Socket ipAddressOfHost:'clam'
     Socket hostWithIpAddress:(Socket ipAddressOfHost:'clam')
     Socket ipAddressOfHost:'porty'
     Socket hostWithIpAddress:(Socket ipAddressOfHost:'porty')
     Socket hostWithIpAddress:#[1 2 3 4]
     Socket hostWithIpAddress:#[127 0 0 1]
     Socket hostWithIpAddress:(Socket ipAddressOfHost:'1.2.3.4')
     Socket hostWithIpAddress:(Socket ipAddressOfHost:'www.altavista.com')
     "
!

hostWithIpV6Address:addrByteArray
    "return the hostname for an IPv6 (internet-) address.
     The address is supposed to be a byteArray consisting ??? bytes,
     the network bytes come first (no matter what the local byteorder is).
     Nil is returned for an unknown host or if its not an internet host.
     This is the reverse operation to #ipV6AddressOfHost:."

    NameLookupError
	handle:[:ex |
	    ^ nil
	]
	do:[
	    ^ (IPv6SocketAddress hostAddress:addrByteArray) hostName
	]

    "
     Socket ipV6AddressOfHost:'clam'
     Socket hostWithIpV6Address:(Socket ipAddressOfHost:'clam')
     Socket ipV6AddressOfHost:'porty'
     Socket hostWithIpV6Address:(Socket ipAddressOfHost:'porty')
     Socket hostWithIpV6Address:#[1 2 3 4 5 6 7 8 9 10 11 12 13 14]
     Socket ipV6AddressOfHost:'www.exept.de'
     "
!

ipAddressOfHost:aHostName
    "return the IP (internet-) number for a hostname as a byteArray,
     where the network bytes come first (no matter what the cpus byteOrder is).
     If the host is unknown, return nil.
     This is the reverse operation to #hostWithIpAddress:."

    NameLookupError
	handle:[:ex |
	    ^ nil
	]
	do:[
	    ^ (IPSocketAddress hostName:aHostName) address
	]

    "
     Socket ipAddressOfHost:'localhost'
     Socket ipAddressOfHost:'exept'
     Socket ipAddressOfHost:'1.2.3.4'
     Socket ipAddressOfHost:'193.15.16.17'
     Socket ipAddressOfHost:'josef'
     Socket ipAddressOfHost:'styx.com'
     Socket hostWithIpAddress:(Socket ipAddressOfHost:'localhost')
     Socket ipAddressOfHost:(Socket hostWithIpAddress:'127.0.0.1')
    "
!

ipV6AddressOfHost:aHostName
    "return the IPv6 (internet-) number for a hostname as a byteArray,
     where the network bytes come first (no matter what the cpus byteOrder is).
     If the host is unknown, return nil.
     This is the reverse operation to #hostWithIpV6Address:."

    NameLookupError
	handle:[:ex |
	    ^ nil
	]
	do:[
	    ^ (IPv6SocketAddress hostName:aHostName) address
	]

    "
     Socket ipV6AddressOfHost:'localhost'
     Socket ipV6AddressOfHost:'exept'
     Socket ipV6AddressOfHost:'exept.exept.de'
     Socket ipV6AddressOfHost:'www.google.de'
     Socket ipV6AddressOfHost:'1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16'
     Socket ipV6AddressOfHost:'josef'
     Socket ipV6AddressOfHost:'styx.com'
     Socket hostWithIpV6Address:(Socket ipV6AddressOfHost:'localhost')
     Socket ipV6AddressOfHost:(Socket hostV6WithIpAddress:'127.0.0.1')
    "
! !

!Socket class methodsFor:'obsolete'!

connectTo:service on:host
    "standard & easy client setup:
	create new client tcp socket, bind and connect;
	return the socket.
     The thread blocks (interruptable), until the connection is established."

    ^ self newTCPclientToHost:host port:(self portOfService:service).

    "
     Socket connectTo:9995 on:'clam'
     Socket connectTo:4711 on:'exept'
     Socket connectTo:'finger' on:'clam'
     Socket connectTo:'ftp' on:'exept'
     Socket connectTo:'nntp' on:(OperatingSystem getEnvironment:'NNTPSERVER')
    "
!

provide:aService
    "standard & easy server setup:
     create a new TCP server socket providing a service."

    <resource:#obsolete>

    |newSock|

    self obsoleteMethodWarning:'use newTCPserverAtPort: / listen'.

    newSock := self newTCPserverAtPort:(self portOfService:aService).
    newSock notNil ifTrue:[
	newSock listenFor:5.
    ].
    ^ newSock

    "
     Socket provide:9995
     (Socket provide:9996) accept
     Socket provide:'nntp'
    "
! !

!Socket class methodsFor:'queries'!

domainOfProtocol:aProtocol
    "given a protocols name (i.e. tcp, udp etc) return the domain.
     This method needs more ... - or is there a way to get this from the system ?"

    "
     tcp/ip stuff
    "
    (aProtocol = 'tcp') ifTrue:[^ #inet].
    (aProtocol = 'udp') ifTrue:[^ #inet].
    (aProtocol = 'ip')  ifTrue:[^ #inet].
    "
     unix domain
    "
    (aProtocol = 'ud')  ifTrue:[^ #unix].

    "
     add x25 stuff (if any) here ...
    "
    "
     add appletalk stuff (if any) here ...
    "
    "
     add other stuff (if any) here ...
    "
    ^ nil

    "
     Socket domainOfProtocol:'tcp'
     Socket domainOfProtocol:'ucp'
     Socket domainOfProtocol:(Socket protocolOfService:'nntp')
     Socket domainOfProtocol:(Socket protocolOfService:'echo')
    "
!

networkLongOrderIsMSB
    "return true, if the network-byte-order of longs is MSB (which it is).
     To be used as in:
	'aStream nextPutLong:someValue MSB:(Socket networkLongOrderIsMSB)'."

    ^ true

    "
     Socket networkLongOrderIsMSB
    "
!

networkShortOrderIsMSB
    "return true, if the network-byte-order of shorts is MSB.
     To be used as in:
	'aStream nextPutShort:someValue MSB:(Socket networkShortOrderIsMSB)'."

%{  /* NOCONTEXT */
#ifndef NO_SOCKET
    /*
     * mhmh - do c-compilers optimize this away ?
     */
    if (htons(0x3210) == 0x3210) {
	RETURN (true);
    }
#endif
%}.
    ^ false

    "
     Socket networkShortOrderIsMSB
    "
!

peerFromDomain:domain name:peerName port:port
    |addrClass|

    addrClass := self socketAddressClassForDomain:domain.
    ^ addrClass hostName:peerName serviceName:port type:nil
!

peerNameFromDomain:domain peer:peer
    |addrClass|

    addrClass := self socketAddressClassForDomain:domain.
    ^ addrClass peerNameFromPeer:peer
!

portOfService:aNameOrNumber
    "returns the port-number for a given IP-service
     or nil if no such service exists;
     - used to convert service names to portNumbers"

    ^ self portOfService:aNameOrNumber protocol:nil

    "
     Socket portOfService:'finger'
     Socket portOfService:'nntp'
     Socket portOfService:'echo'
     Socket portOfService:'snmp'
    "
!

portOfService:aNameOrNumber protocol:aProtocol
    "returns the port-number for a given IP-service
     or nil if no such service exists;
     - used to convert service names to portNumbers"

%{ /* UNLIMITEDSTACK */
#ifndef NO_SOCKET
    struct servent *servent = NULL;
    char *protocol;
    int tryBoth = 0;
    short portNo;

    if (__isSmallInteger(aNameOrNumber)) {
	RETURN ( aNameOrNumber );
    }

    if (__isString(aProtocol)) {
	protocol = (char *)__stringVal(aProtocol);
    } else {
	protocol = "tcp";
	tryBoth = 1;
    }

    if (__isString(aNameOrNumber) || __isSymbol(aNameOrNumber)) {
	servent = getservbyname((char *) __stringVal(aNameOrNumber), protocol);
	if (servent != NULL) {
	    RETURN ( __MKSMALLINT(ntohs(servent->s_port)) );
	}
	if (tryBoth) {
	    servent = getservbyname((char *) __stringVal(aNameOrNumber), "udp");
	    if (servent != NULL) {
		RETURN ( __MKSMALLINT(ntohs(servent->s_port)) );
	    }
	}
	RETURN ( nil );
    }
#endif
    RETURN ( nil );
%}
    "
     Socket portOfService:'echo' protocol:'udp'
     Socket portOfService:'echo' protocol:'tcp'
    "
!

protocolOfService:aNameOrNumber
    "returns the protocol (as string) for a given IP-service
     or nil if no such service exists."

%{  /* UNLIMITEDSTACK(noWIN32) */
#ifndef NO_SOCKET
    struct servent *servent = NULL;
    short portNo;

    if (__isSmallInteger(aNameOrNumber)) {
	portNo = __intVal(aNameOrNumber);
	servent = getservbyport(htons(portNo), "tcp") ;
	if (servent == NULL) {
	    servent = getservbyport(htons(portNo), "udp") ;
	    if (servent == NULL) {
		RETURN ( nil );
	    }
	}
    } else {
	if (__isString(aNameOrNumber)) {
	    servent = getservbyname((char *) __stringVal(aNameOrNumber), "tcp");
	    if (servent == NULL) {
		servent = getservbyname((char *) __stringVal(aNameOrNumber), "udp");
		if (servent == NULL) {
		    RETURN ( nil );
		}
	    }
	}
    }
    if (servent) {
	RETURN ( __MKSTRING(servent->s_proto) );
    }
#endif /* !NO_SOCKET */
    RETURN ( nil );
%}
    "
     Socket protocolOfService:'finger'
     Socket protocolOfService:'nntp'
     Socket protocolOfService:'xxx'
     Socket protocolOfService:79
     Socket protocolOfService:'snmp'
    "
!

socketAddressClassForDomain:domain
    ^ SocketAddress knownClassFromCode:domain

    "
     self socketAddressClassForDomain:#inet
     self socketAddressClassForDomain:#unix
    "
!

supportedProtocolFamilies
    "return a collection of supported protocol families.
     This list specifies what the Socket class supports -
     socket creation may still fail, if your system was built
     without it."

    ^ OperatingSystem supportedProtocolFamilies

    "
     Socket supportedProtocolFamilies
    "
!

typeOfProtocol:aProtocol
    "given a protocols name (i.e. tcp, udp etc) return the connection type.
     This method needs more ... - or is there a way to get this from the system ?"

    (aProtocol = 'tcp') ifTrue:[^ #stream].
    (aProtocol = 'udp') ifTrue:[^ #datagram].
    (aProtocol = 'ip')  ifTrue:[^ #raw].
    "
     unix domain
    "
    (aProtocol = 'ud')  ifTrue:[^ #stream].
    "
     add x25 stuff (if any) here ...
    "
    "
     add appletalk stuff (if any) here ...
    "
    "
     add other stuff (if any) here ...
    "
    ^ nil

    "
     Socket typeOfProtocol:'tcp'
     Socket typeOfProtocol:'ucp'
     Socket typeOfProtocol:(Socket protocolOfService:'nntp')
     Socket typeOfProtocol:(Socket protocolOfService:'echo')
    "
! !

!Socket methodsFor:'Compatibility-Dolphin'!

setReceiveTimeout: milliseconds
    self receiveTimeout:(milliseconds / 1000)
!

setSendTimeout: milliseconds
    self sendTimeout:(milliseconds / 1000)
! !

!Socket methodsFor:'Compatibility-ST80'!

acceptNonBlock
    ^ self accept
!

connectTo:aSocketAddress
    ^ self connectTo:aSocketAddress hostAddress port:aSocketAddress port
!

errorReporter
    "ST-80 mimicry."

    ^ self
!

ioConnection
    ^ self
!

notReadySignal
    "ST-80 mimicry.
     for now - this is not yet raised"

    ^ Signal new
!

readAppendStream
    "ST-80 mimicry.
     In ST-80, socket is not a stream, but referes to one.
     ST-80 code therefore uses 'Socket readWriteStream' to access
     the actual stream.
     In ST/X, sockets inherit from stream, so
     this method returns the receiver, for transparency"

    ^ self
!

readStream
    "ST-80 mimicry.
     In ST-80, socket is not a stream, but referes to one.
     ST-80 code therefore uses 'Socket readStream' to access
     the actual stream.
     In ST/X, sockets inherit from stream, so
     this method returns the receiver, for transparency"

    ^ self

    "Created: 24.1.1997 / 23:52:57 / cg"
!

writeStream
    "ST-80 mimicry.
     In ST-80, socket is not a stream, but referes to one.
     ST-80 code therefore uses 'Socket writeStream' to access
     the actual stream.
     In ST/X, sockets inherit from stream, so
     this method returns the receiver, for transparency"

    ^ self

    "Created: 24.1.1997 / 10:34:35 / cg"
    "Modified: 24.1.1997 / 23:52:52 / cg"
! !

!Socket methodsFor:'Compatibility-Squeak'!

address
    ^ self getSocketAddress
!

dataAvailable
    ^ self canReadWithoutBlocking
!

listenOn:aPortNr
    self bindTo:aPortNr address:nil.
    self listenWithBacklog:5
!

peerName
    "return my peer (i.e. ipAddr + port);
     May return nil if not yet setup completely."

    ^ self getPeer
!

setOption: optionName value: optionValue
    optionName = 'TCP_NODELAY' ifTrue:[
	^ self setTCPNoDelay:optionValue
    ].
    self error:'unimplemented socketoption' mayProceed:true
!

waitForConnectionUntil:aMillisecondClockValue
    self halt.
! !

!Socket methodsFor:'datagram transmission'!

receiveBuffer:aDataBuffer start:startIndex for:nBytes
    "receive data
     Return the number of bytes received, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable.
     The thread blocks until data arrives - you may want to wait before
     receiving, using #readWait or #readWaitWithTimeout:."

    |nReceived|

%{
#ifndef NO_SOCKET
    OBJ fp = __INST(filePointer);
    int objSize, offs;
    int sock;
    int n;
    char *extPtr;
    unsigned char *buffer;
    unsigned char *allocatedBuffer;
    int flags = 0;

    if (fp != nil) {
	sock = fileno(__FILEVal(fp));

	setupBufferParameters(aDataBuffer, startIndex, &extPtr, &offs, &objSize);
	if (objSize < 0) goto bad;
	if (__isSmallInteger(nBytes)) {
	    if (__intVal(nBytes) < objSize) {
		objSize = __intVal(nBytes);
	    }
	}

#ifdef WIN32
	if (extPtr) {
	    buffer = extPtr + offs;
	} else {
	    allocatedBuffer = buffer = (char *)malloc(objSize);
	}
#endif
	__BEGIN_INTERRUPTABLE__
#ifdef WIN32
	do {
	    __threadErrno = 0;
	    n = STX_API_CALL4("recv", recv, sock, buffer, objSize, flags);
	} while ((n < 0) && (__threadErrno == EINTR));
#else
	do {
	    if (extPtr) {
		n = recv(sock, extPtr + offs, objSize, flags);
	    } else {
		n = recv(sock, (char *)__InstPtr(aDataBuffer) + offs, objSize, flags);
	    }
	} while ((n < 0) && (errno == EINTR));
#endif
	__END_INTERRUPTABLE__

#ifdef WIN32
	if (allocatedBuffer) {
	    if (n > 0) {
		bcopy(allocatedBuffer, (char *)__InstPtr(aDataBuffer) + offs, n);
	    }
	    free(allocatedBuffer);
	}
#endif

	if (n < 0) {
	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
	}
	nReceived = __MKSMALLINT(n);
    }
#endif
bad: ;
%}.
    nReceived notNil ifTrue:[
	nReceived < 0 ifTrue:[
	    'Socket [warning]: ' infoPrint.
	    (OperatingSystem errorTextForNumber:lastErrorNumber) infoPrintCR.
	].
	^ nReceived
    ].
    "
     arrive here if you try to receive into an invalid buffer (i.e. not ByteArray-like)
    "
    self primitiveFailed
!

receiveFrom:anAddressBuffer buffer:aDataBuffer
    "receive datagramm data - put address of originating host into
     anAddressBuffer, data into aBuffer.
     Both must be ByteArray-like. The addressBuffer must
     provide space for a valid address for my domain (i.e. for inet, a 4-byte byteArray).
     Return the number of bytes received, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable."

    ^ self receiveFrom:anAddressBuffer buffer:aDataBuffer start:1 for:(aDataBuffer size)
!

receiveFrom:anAddressBuffer buffer:aDataBuffer start:startIndex for:nBytes
    "receive datagramm data
     - put address of originating host into anAddressBuffer, data into aBuffer.
     For backward compatibility, the addressBuffer may be a non-SocketAddress;
     then, it must be a byteArray with appropriate size for the addressBytes.

     Return the number of bytes received, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable.
     The thread blocks until data arrives - you may want to wait before
     receiving, using #readWait or #readWaitWithTimeout:."

    |domainClass addr addrLen nReceived|

    domainClass := self class socketAddressClassForDomain:domain.
    domainClass isNil ifTrue:[
	^ self error:'invalid (unsupported) domain'.
    ].
    (anAddressBuffer isKindOf:SocketAddress) ifTrue:[
	anAddressBuffer class == domainClass ifFalse:[
	    ^ self error:'addressBuffer class mismatch (domain)'.
	].
	addr := anAddressBuffer.
    ] ifFalse:[
	anAddressBuffer notNil ifTrue:[
	    addr := domainClass new.
	].
    ].

%{
#ifndef NO_SOCKET
    OBJ fp = __INST(filePointer);
    int objSize;
    int sock;
    union sockaddr_u sa;
    int alen = 0;
    int n, offs;
    int flags = 0;
    char *extPtr;
    unsigned char *allocatedBuffer = NULL;
    unsigned char *buffer = NULL;

    if (fp != nil) {
	sock = fileno(__FILEVal(fp));

	setupBufferParameters(aDataBuffer, startIndex, &extPtr, &offs, &objSize);
	if (objSize < 0) goto bad;
	if (__isSmallInteger(nBytes)) {
	    if (__intVal(nBytes) < objSize) {
		objSize = __intVal(nBytes);
	    }
	}
#ifdef WIN32
	if (extPtr) {
	    buffer = extPtr + offs;
	} else {
	    allocatedBuffer = buffer = (char *)malloc(objSize);
	}
#endif
	__BEGIN_INTERRUPTABLE__
#ifdef WIN32
	do {
	    __threadErrno = 0;
	    alen = sizeof(sa);
	    n = STX_API_CALL6("recvfrom", recvfrom, sock, buffer, objSize, flags, (struct sockaddr *)&sa, &alen);
	} while ((n < 0) && (__threadErrno == EINTR));
#else
	do {
	    alen = sizeof(sa);
	    if (extPtr) {
		n = recvfrom(sock, extPtr + offs, objSize, flags, (struct sockaddr *) &sa, &alen);
	    } else {
		n = recvfrom(sock, (char *)__InstPtr(aDataBuffer) + offs, objSize, flags, (struct sockaddr *) &sa, &alen);
	    }
	} while ((n < 0) && (errno == EINTR));
#endif
	__END_INTERRUPTABLE__

#ifdef WIN32
	if (allocatedBuffer) {
	    if (n > 0) {
		bcopy(allocatedBuffer, (char *)__InstPtr(aDataBuffer) + offs, n);
	    }
	    free(allocatedBuffer);
	}
#endif

	if (n >= 0) {
	    if (__isNonNilObject(addr)) {
		char *addrPtr;
		OBJ oClass;
		int nInstVars, nInstBytes, objSize;

		oClass = __qClass(addr);
		if (! __isBytes(addr) )
		    goto bad;
		nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
		nInstBytes = OHDR_SIZE + (nInstVars * sizeof(OBJ));
		objSize = __qSize(addr) - nInstBytes;
		addrPtr = (char *)__InstPtr(addr) + nInstBytes;
		if (objSize < alen)
		    goto bad;

		/*
		 * extract the datagrams address
		 */
		bcopy((char *)&sa, addrPtr, alen);
		addrLen = __MKSMALLINT(alen);
	    }
	}
	if (n < 0) {
	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
	}
	nReceived = __MKSMALLINT(n);
    }
#endif
bad: ;
%}.
    nReceived notNil ifTrue:[
	nReceived < 0 ifTrue:[
	    'Socket [warning]: ' infoPrint.
	    (OperatingSystem errorTextForNumber:lastErrorNumber) infoPrintCR.
	].
	addrLen notNil ifTrue:[
	    (addr == anAddressBuffer) ifFalse:[
		self obsoleteFeatureWarning:'please use a socketAddress argument'.

		"/ can be a ByteArray for backward compatibility
		anAddressBuffer replaceFrom:1 to:addrLen with:(addr hostAddress).
	    ].
	].
	^ nReceived
    ].
    "
     arrive here if you try to receive into an invalid buffer
     (i.e. not ByteArray-like),
     or if the addressBuffer is nonNil AND not a SocketAddress/ByteArray
     or if the addressBuffer is nonNil AND too small.
    "
    self primitiveFailed
!

sendBuffer:aDataBuffer start:startIndex for:nBytes flags:flags
    "send data.
     Both must be ByteArray-like. The bytes in the addressBuffer must
     be a valid address for my domain (i.e. for inet, a 4-byte byteArray).
     Return the number of bytes transmitted, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable."

    |nReceived portNo|

%{
#ifndef NO_SOCKET
    OBJ oClass;
    OBJ fp = __INST(filePointer);
    int objSize;
    int sock;
    int n;
    char *extPtr;
    int _flags = 0;
    int offs;
    unsigned long norder;
    unsigned char *buffer;
    unsigned char *allocatedBuffer;

    _flags = __longIntVal(flags);

    if ((fp != nil)
     && __isSmallInteger(startIndex)
     && __isSmallInteger(nBytes)) {
	sock = fileno(__FILEVal(fp));

	setupBufferParameters(aDataBuffer, startIndex, &extPtr, &offs, &objSize);
	if (objSize < 0) goto bad;
	if (__isSmallInteger(nBytes)) {
	    if (__intVal(nBytes) < objSize) {
		objSize = __intVal(nBytes);
	    }
	}

# ifdef DGRAM_DEBUG
	printf("sending %d bytes ...\n", nBytes);
# endif

#ifdef WIN32
	if (extPtr) {
	    buffer = extPtr + offs;
	} else {
	    allocatedBuffer = buffer = (char *)malloc(objSize);
	    bcopy((char *)__InstPtr(aDataBuffer) + offs, allocatedBuffer, objSize);
	}
#endif

	__BEGIN_INTERRUPTABLE__
#ifdef WIN32
	do {
	    __threadErrno = 0;
	    n = STX_API_CALL4("send", send, sock, buffer, objSize, _flags);
	} while ((n < 0) && (__threadErrno == EINTR));
#else
	do {
	    if (extPtr) {
		n = send(sock, extPtr + offs, objSize, _flags);
	    } else {
		n = send(sock, (char *)__InstPtr(aDataBuffer) + offs, objSize, _flags);
	    }
	} while ((n < 0) && (errno == EINTR));
#endif
	__END_INTERRUPTABLE__

#ifdef WIN32
	if (allocatedBuffer) {
	    free(allocatedBuffer);
	}
#endif

	if (n < 0) {
	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
	}
	RETURN (__MKSMALLINT(n));
    }
#endif
bad: ;
%}.
    "
     arrive here if you try to send from an invalid buffer (i.e. not ByteArray-like),
    "
    self primitiveFailed
!

sendTo:anAddressBuffer buffer:buffer
    "send datagramm data - fetch address of destination host from
     anAddressBuffer, data from aDataBuffer.
     Both must be ByteArray-like. The bytes in the addressBuffer must
     be a valid address for my domain (i.e. for inet, a 4-byte byteArray).
     Return the number of bytes transmitted, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable.
     Flags is currently ignored; it is there for ST-80 compatibility."

    ^ self sendTo:anAddressBuffer buffer:buffer start:1 for:buffer size flags:0
!

sendTo:anAddressBuffer buffer:buffer start:startIndex for:count
    "send datagramm data - fetch address of destination host from
     anAddressBuffer, data from aDataBuffer.
     Both must be ByteArray-like. The bytes in the addressBuffer must
     be a valid address for my domain (i.e. for inet, a 4-byte byteArray).
     Return the number of bytes transmitted, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable.
     Flags is currently ignored; it is there for ST-80 compatibility."

    ^ self sendTo:anAddressBuffer buffer:buffer start:startIndex for:count flags:0
!

sendTo:anAddressBuffer buffer:aDataBuffer start:startIndex for:nBytes flags:flags
    "send datagramm data - fetch address of destination host from
     anAddressBuffer, data from aDataBuffer starting at startIndex,
     sending count bytes.
     Both must be ByteArray-like. The bytes in the addressBuffer must
     be a valid address for my domain (i.e. for inet, a 4-byte byteArray).
     Return the number of bytes transmitted, or a negative number on error.
     On error, the unix error code is left in the lastErrorNumber
     instance variable."

    |domainClass addr|

    domainClass := self class socketAddressClassForDomain:domain.
    domainClass isNil ifTrue:[
	^ self error:'invalid (unsupported) domain'.
    ].

    (anAddressBuffer isKindOf:SocketAddress) ifTrue:[
	addr := anAddressBuffer.
    ] ifFalse:[
	anAddressBuffer isByteArray ifFalse:[
	    ^ self error:'bad socketAddress argument'
	].
	addr := domainClass hostAddress:anAddressBuffer.
    ].
%{
#ifndef NO_SOCKET
    OBJ oClass;
    OBJ fp = __INST(filePointer);
    int objSize;
    struct sockaddr *sockaddr_ptr;
    union sockaddr_u sa;
    int alen = 0;
    int sockAddrOffs, sockaddr_size;
    int sock;
    int n;
    char *extPtr;
    int _flags = 0;
    int offs;
    unsigned long norder;
    unsigned char *buffer;
    unsigned char *allocatedBuffer;

    _flags = __longIntVal(flags);

    if ((fp != nil)
     && __isSmallInteger(startIndex)
     && __isSmallInteger(nBytes)) {
	sock = fileno(__FILEVal(fp));

	if (! __isBytes(addr)) {
	    sockaddr_size = 0;
	    sockaddr_ptr = (struct sockaddr *)0;
	} else {
	    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)) {
		fprintf(stderr, "bad socketAddr\n");
		goto bad;
	    }
	    bcopy((__byteArrayVal(addr) + sockAddrOffs), &sa, sockaddr_size);
	    sockaddr_ptr = (struct sockaddr *)(&sa);
	}

	setupBufferParameters(aDataBuffer, startIndex, &extPtr, &offs, &objSize);
	if (objSize < 0) goto bad;
	if (__isSmallInteger(nBytes)) {
	    if (__intVal(nBytes) < objSize) {
		objSize = __intVal(nBytes);
	    }
	}

#ifdef WIN32
	if (extPtr) {
	    buffer = extPtr + offs;
	} else {
	    allocatedBuffer = buffer = (char *)malloc(objSize);
	    bcopy((char *)__InstPtr(aDataBuffer) + offs, allocatedBuffer, objSize);
	}
#endif
	__BEGIN_INTERRUPTABLE__
#ifdef WIN32
	do {
	    __threadErrno = 0;
	    n = STX_API_CALL4("sendto", sendto, sock, buffer, objSize, _flags, sockaddr_ptr, sockaddr_size);
	} while ((n < 0) && (__threadErrno == EINTR));
#else
	do {
	    if (extPtr) {
		n = sendto(sock, extPtr + offs, objSize, _flags, sockaddr_ptr, sockaddr_size);
	    } else {
		n = sendto(sock, (char *)__InstPtr(aDataBuffer) + offs, objSize, _flags, sockaddr_ptr, sockaddr_size);
	    }
	} while ((n < 0) && (errno == EINTR));
#endif
	__END_INTERRUPTABLE__

#ifdef WIN32
	if (allocatedBuffer) {
	    free(allocatedBuffer);
	}
#endif

	if (n < 0) {
	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
	}
	RETURN (__MKSMALLINT(n));
    }
#endif
bad: ;
%}.
    "
     arrive here if you try to send from an invalid buffer
     (i.e. not ByteArray-like),
     or if the addressBuffer is nonNil AND not a ByteArray/String
     or if the addressBuffer is nonNil AND too small.
    "
    self primitiveFailed
! !

!Socket methodsFor:'low level'!

bindAnonymously
    ^ self
	bindTo:0
	address:nil
	reuseAddress:false
!

bindAnonymouslyToAddress:addressString
    ^ self
	bindTo:0
	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|

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].

    domainClass := self class socketAddressClassForDomain:domain.
    domainClass isNil ifTrue:[
	^ self error:'invalid (unsupported) domain'.
    ].

    "/ backward compatibility: support for byteArray and string arg

    hostOrPathNameOrSocketAddrOrNil isNil ifTrue:[
	addr := domainClass anyHost.
    ] ifFalse:[
	hostOrPathNameOrSocketAddrOrNil isString ifTrue:[
	    addr := domainClass hostName:hostOrPathNameOrSocketAddrOrNil.
	    addrName := hostOrPathNameOrSocketAddrOrNil.
	] ifFalse:[
	    (hostOrPathNameOrSocketAddrOrNil isKindOf:SocketAddress) ifTrue:[
		addr := 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 t = __INST(filePointer);
    int sock;
    union sockaddr_u sa;
    struct sockaddr *sockaddr_ptr;
    int sockaddr_size;
    int ret;
    int sockAddrOffs;

    if (! __isBytes(addr)) {
	addr = nil;
	fprintf(stderr, "Socket: bad sddr\n");
	RETURN (false);
    }

    {
	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)) {
	    fprintf(stderr, "Socket: bad socketAddr\n");
	    RETURN (false);
	}
	bcopy((__byteArrayVal(addr) + sockAddrOffs), &sa, sockaddr_size);
    }

    sock = fileno(__FILEVal(t));

# 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 */

    __BEGIN_INTERRUPTABLE__
    do {
	ret = bind(sock, (struct sockaddr *)&sa, sockaddr_size);
    } while ((ret < 0) && (errno == EINTR));
    __END_INTERRUPTABLE__

    if (ret < 0) {
	DBGPRINTF(("SOCKET: bind failed errno=%d\n", errno));
	__INST(lastErrorNumber) = __MKSMALLINT(errno);
	RETURN (false);
    } else {
	ok = true;

	if (! __isSmallInteger(portNrOrNameOrNil)
	 || (portNrOrNameOrNil == __MKSMALLINT(0))) {
	    int p;
	    int alen;

	    /*
	     * anonymous port - get the actual portNr
	     */
	    if (getsockname(sock, (struct sockaddr *)&sa, &alen) < 0) {
		DBGPRINTF(("SOCKET: cannot get peername\n"));
	    }
	    if ((alen > sizeof(sa)) || (alen > sockaddr_size)) {
		fprintf(stderr, "SOCKET: large addr returned\n");
	    } else {
		bcopy(&sa, (__byteArrayVal(addr) + sockAddrOffs), alen);
	    }
	}
    }

#endif /* NO_SOCKET */

getOutOfHere: ;
%}.
    ok ~~ true ifTrue:[
	^ false
    ].

    peer := addr.
    port := addr port.
    peerName := addrName.

    ^ true

    "
     (Socket domain:#inet type:#stream)
	 bindTo:9999
	 address:nil
    "
!

tryToBindTo:portNrOrNameOrNil
    (self bindTo:portNrOrNameOrNil address:nil) ifFalse:[
	self close.
	^ nil
    ].
    ^ self.
! !

!Socket protectedMethodsFor:'low level'!

closeFile
    "low level close"

%{  /* NOCONTEXT */
#ifndef NO_SOCKET

    OBJ t;

    t = __INST(filePointer);
    if (t != nil) {
	FILE *fp;

	fp = __FILEVal(t);
	DBGFPRINTF((stderr, "socket fclose %x (%d)\n", fp, fileno(fp)));
	fflush(fp);
	/* shutdown(fileno(fp), 2); */
	if (@global(FileOpenTrace) == true) {
	    fprintf(stderr, "fclose [Socket] %x\n", fp);
	}
	fclose(fp);
	__INST(filePointer) = nil;
    }
#endif
%}
! !

!Socket methodsFor:'low level'!

getSocketError
    "get the SO_ERROR form the socket, which indicates the
     result of an asynchronous operation"

%{
#ifndef NO_SOCKET
    OBJ t;
    int err;

    t = __INST(filePointer);
    if (t == nil)
	err = EBADF;
    else {
	int sz, fd;

	fd = fileno(__FILEVal(t));
	sz = sizeof(err);
	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &sz) < 0)
	    err = errno;
    }

    RETURN(__MKSMALLINT(err));
#endif
%}
!

listenFor:aNumber
    "same as listenWithBacklog: - for ST-80 compatibility"

    ^ self listenWithBacklog:aNumber
!

listenWithBacklog:aNumber
    "start listening; return true if ok, false on error"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#ifndef NO_SOCKET
    OBJ fp = __INST(filePointer);
    int sock;
    int ret;

    if (! __isSmallInteger(aNumber)) {
	DBGPRINTF(("SOCKET: invalid arg\n"));
	RETURN (false);
    }

    sock = fileno(__FILEVal(fp));

    __BEGIN_INTERRUPTABLE__
    do {
	ret = listen(sock, __intVal(aNumber));
    } while ((ret < 0) && (errno == EINTR));
    __END_INTERRUPTABLE__

    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
!

shutDown
    "shutDown and close the socket.
     This does not wait for the data to be delivered."

    self shutdown:2.
    self close
!

shutdown: howNum
    "shutDown the socket - inform it that no more I/O will be performed.
	 0 - read side   (no further reads)
	 1 - write side  (no further writes)
	 2 - both        (no further I/O at all)
     shutDown:2
	discards any pending data (as opposed to close, which waits until data is delivered)"

    filePointer isNil ifTrue:[^ self].

%{
#ifndef NO_SOCKET

    OBJ fp;

    fp = __INST(filePointer);
    if ((fp != nil) && __isSmallInteger(howNum)) {
	FILE *f;
	int fd;

	__INST(filePointer) = nil;
	f = __FILEVal(fp);
	fd = fileno(f);
	__BEGIN_INTERRUPTABLE__
	shutdown(fd, __intVal(howNum));
	DBGFPRINTF((stderr, "socket shutDown %x (%d)\n", fp, fd));
	__END_INTERRUPTABLE__
    }
#endif
%}.
! !

!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

    "Modified: / 11.3.1996 / 14:21:31 / stefan"
    "Modified: / 1.8.1998 / 23:39:10 / cg"
!

primAcceptOn:aSocket
    "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;
    int sock, newSock;
    union sockaddr_u sa;
    int alen, alen0;
    struct hostent *he ;
    char dotted[20] ;

    if (!__isSmallInteger(addrLen)) {
	DBGPRINTF(("SOCKET: bad addrLen\n"));
	RETURN (false);
    }
    alen0 = __intVal(addrLen);

    sock = __intVal(serverSocketFd);

# if defined(O_NONBLOCK) && defined(SET_NDELAY)
    flags = fcntl(sock, F_GETFL);
    fcntl(sock, F_SETFL, flags | O_NONBLOCK);
# endif

    __BEGIN_INTERRUPTABLE__
    do {
	alen = alen0;
	newSock = accept(sock, (struct sockaddr *) &sa, &alen);
    } while ((newSock < 0) && (errno == EINTR));
    __END_INTERRUPTABLE__
    DBGFPRINTF((stderr, "socket accept newSock=%d\n", newSock));

# 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"));
	    close(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"));
	    close(newSock);
	    RETURN (false);
	}

	/*
	 * extract the partners address
	 */
	bcopy((char *)&sa, cp, alen);
	addrLen = __MKSMALLINT(alen);
    }

    /*
     * make it a FILE *
     */
    fp = fdopen(newSock, "r+");
    if (! fp) {
	DBGPRINTF(("SOCKET: fdopen call failed\n"));
	__INST(lastErrorNumber) = __MKSMALLINT(errno);
	close(newSock);
	DBGFPRINTF((stderr, "socket close (fdopen failed) (%d)\n", newSock));
	RETURN (false);
    }
    if (@global(FileOpenTrace) == true) {
	fprintf(stderr, "fdopen [Socket] -> %x\n", fp);
    }

# 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."

    "/ first, a quick check if data is already available

    |wasBlocked sema|

    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
    ].

    domainClass := self class socketAddressClassForDomain:domain.
    domainClass isNil ifTrue:[
	^ self error:'invalid (unsupported) domain'.
    ].

    "/ backward compatibility: support for byteArray and string arg

    hostOrPathNameOrSocketAddr isString ifTrue:[
	addr := domainClass hostName:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil.
	addrName := hostOrPathNameOrSocketAddr.
    ] ifFalse:[
	hostOrPathNameOrSocketAddr isByteArray ifFalse:[
	    ^ self error:'bad host (socketAddress) argument'
	].
	(hostOrPathNameOrSocketAddr isKindOf:SocketAddress) ifTrue:[
	    addr := hostOrPathNameOrSocketAddr.
	    portNrOrNameOrNil notNil ifTrue:[
		addr port:portNrOrNameOrNil.
	    ].
	] ifFalse:[
	    addr := domainClass hostAddress:hostOrPathNameOrSocketAddr port:portNrOrNameOrNil.
	].
    ].

%{  /* STACK: 100000 */

#ifndef NO_SOCKET
    OBJ t = __INST(filePointer);
    union sockaddr_u sa;
    int a, sock;
    FILE *fp;
    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)) {
	    fprintf(stderr, "Socket: bad socketAddr\n");
	    RETURN (false);
	}
	bcopy((__byteArrayVal(addr) + sockAddrOffs), &sa, sockaddr_size);
    }

    sock = fileno(__FILEVal(t));

# if defined(O_NONBLOCK)
    /*
     * 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

    /*
     * connect
     */
    __BEGIN_INTERRUPTABLE__
    do {
	ret = connect(sock, (struct sockaddr *)&sa, sockaddr_size);
    } while ((ret < 0)
	     && ((errno == EINTR)
# ifdef EAGAIN
		 || (errno == EAGAIN)
# endif
		));
    __END_INTERRUPTABLE__

    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.
	     */

	    isAsync = true;
	} else
# endif /* EINPROGRESS or EALREADY */
	{
	    DBGPRINTF(("SOCKET: connect failed errno=%d\n", errno));
# ifdef DUMP_ADDRESS
	    {
		char *cp = (char *)(&sa);
		int i;

		printf("address data:\n");
		for (i=0; i<sockaddr_size; i++) {
		    printf(" %02x\n", *cp++);
		}
	    }
# endif
	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
	    RETURN (false);
	}
    }

# if defined(O_NONBLOCK)
    fcntl(sock, F_SETFL, oldFlags);
# 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:'Socket(protocol='.
    protocol printOn:aStream.
    aStream nextPutAll:' port='.
    port printOn:aStream.
    aStream nextPutAll:' peer='.
    peer printOn:aStream.
    aStream nextPutAll:')'.
! !

!Socket methodsFor:'queries'!

domain
    "return the sockets addressing domain (i.e. #inet, #unix, #x25, #appletalk)"

    ^ domain
!

getName
    "return the name; here, we return the ports name"

    ^ port printString
!

getPeer
    "ST-80 compatibility: return an IPSocketAddress instance representing
     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
!

getPeerName
    "return the peer name; thats the hostname (or dotted name) of the
     partners host after an accept."

    peerName isNil ifTrue:[
	peerName := self class peerNameFromDomain:domain peer:peer.
    ].
    ^ peerName
!

getSocketAddress
    "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 t = __INST(filePointer);
    int sock;
    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 = fileno(__FILEVal(t));
    ret = getsockname(sock, (struct sockaddr *)__byteArrayVal(socketAddress), &sockaddr_size);
    if (ret < 0) {
	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 copyFrom:5 to:8
!

getSocketAdress
    "implemented for swazoo project primitive code cant load as extension
     answer the my own address (I am bound to this address).
     Note that this address may change after connect or accept.

     BAD SPELLING, kept for compatibility with swazoo"


    ^ self getSocketAddress
!

isActive
    "return true, if the receiver has a connection"

    ^ filePointer notNil
!

isConnected
    "return true, if the receiver has a connection"

    ^ self isActive
      and:[port notNil
      and:[peerName notNil or:[peer notNil]]]
!

port
    "return the port number (or name for unix-sockets) to which the socket is bound"

    |p|

    port == 0 ifTrue:[
	p := self getPort.
	p notNil ifTrue:[
	    port := p
	]
    ].
    ^ port
!

type
    "return the sockets connection type (i.e. #datagram, #stream etc)"

    ^ socketType
! !

!Socket methodsFor:'socket setup'!

domain:domainArg type:typeArg
    "set up socket with domain and type.
     This is a low level entry; no binding, listening or connect
     is done. Both arguments must be symbols from one of
     #inet,#unix, #appletalk, #x25 .. and #stream, #datagram, #raw resp."

    ^ self domain:domainArg type:typeArg protocol:0 reuseAddress:true
!

domain:domainArg type:typeArg protocol:protocolNumber
    "set up socket with domain, type and protocol number.
     This is a low level entry; no binding, listening or connect
     is done. Both arguments must be symbols from one of
     #inet,#unix, #appletalk, #x25 .. and #stream, #datagram, #raw resp."

    ^ self domain:domainArg type:typeArg protocol:protocolNumber reuseAddress:true

    "
     Socket new domain:#inet type:#stream
     Socket new domain:#unix type:#stream
    "
!

domain:domainArg type:typeArg protocol:protocolNumber reuseAddress:reuse
    "set up socket with domain, type and protocol number.
     This is a low level entry; no binding, listening or connect
     is done. Both arguments must be symbols from one of
     #inet,#unix, #appletalk, #x25 .. and #stream, #datagram, #raw resp.

     The reuse boolean argument controls if the SO_REUSEADDR socket option
     is to be set (to avoid the 'bind: address in use' error)."

    |domainName domainCode typeCode|

    filePointer notNil ifTrue:[
	^ self errorAlreadyOpen
    ].
    domainName := SocketAddress domainCodeFromName:domainArg.
    domainCode := OperatingSystem domainCodeOf:domainName.
    typeCode := OperatingSystem socketTypeCodeOf:typeArg.
%{

#ifndef NO_SOCKET
    FILE *fp;
    int dom, typ, pf, proto = 0, sock;
    int on = 1;

    if (! __isSmallInteger(domainCode)) {
	DBGPRINTF(("SOCKET: bad domain\n"));
	RETURN ( nil );
    }
    if (! __isSmallInteger(typeCode)) {
	DBGPRINTF(("SOCKET: bad type\n"));
	RETURN ( nil );
    }
    if (protocolNumber != nil) {
	if (!__isSmallInteger(protocolNumber)) {
	    DBGPRINTF(("SOCKET: bad protocol\n"));
	    RETURN ( nil );
	}
	proto = __intVal(protocolNumber);
    }


    /*
     * get address and protocol-family
     */
    dom = __intVal(domainCode);
    typ = __intVal(typeCode);

    __BEGIN_INTERRUPTABLE__
    do {
	DBGPRINTF(("SOCKET: opening socket domain=%d type=%d proto=%d\n", dom, typ, proto));
	sock = socket(dom, typ, proto);
#if defined(EPROTONOSUPPORT) /* for SGI */
	if ((proto != 0) && (sock < 0) && (errno == EPROTONOSUPPORT)) {
	    DBGPRINTF(("SOCKET: retry with UNSPEC protocol\n"));
	    proto = 0;
	    sock = socket(dom, typ, 0);
	}
#endif
    } while ((sock < 0) && (errno == EINTR));
    __END_INTERRUPTABLE__
    DBGFPRINTF((stderr, "socket create newSock=%d\n", sock));

    if (sock < 0) {
	DBGPRINTF(("SOCKET: socket(dom=%d typ=%d proto=%d) call failed errno=%d\n", dom, typ, proto, errno));
	__INST(lastErrorNumber) = __MKSMALLINT(errno);
    } else {
# ifdef SO_REUSEADDR
	if (reuse == true) {
	    DBGPRINTF(("SOCKET: setsockopt - SO_REUSEADDR\n"));
	    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof (on)) < 0) {
		DBGPRINTF(("SOCKET: setsockopt - SO_REUSEADDR failed\n"));
	    }
	}
# endif /* SO_REUSEADDR */

	/*
	 * make it a FILE *
	 */
	fp = fdopen(sock, "r+");
	if (! fp) {
	    DBGPRINTF(("SOCKET: fdopen call failed\n"));
	    __INST(lastErrorNumber) = __MKSMALLINT(errno);
	    __BEGIN_INTERRUPTABLE__
	    close(sock);
	    DBGFPRINTF((stderr, "socket close (fdopen failed) (%d)\n", sock));
	    __END_INTERRUPTABLE__
	} else {
	    if (@global(FileOpenTrace) == true) {
		fprintf(stderr, "fdopen [Socket] -> %x\n", fp);
	    }

# 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
%}.

    "all ok?"
    filePointer notNil ifTrue:[
	domain := domainArg.
	socketType := typeArg.
	Lobby register:self.
    ] ifFalse:[
	^ nil
    ].

    "
     Socket new domain:#inet type:#stream
     Socket new domain:#unix type:#stream
    "
! !

!Socket methodsFor:'specials'!

receiveBufferSize
    "get the send buffer size - for special applications only.
     Not all operatingSystems offer this functionality
     (returns nil, if unsupported)"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#if defined(SO_RCVBUF) && defined(SOL_SOCKET)
    {
	OBJ fp = __INST(filePointer);
	int sock;
	int opt;
	int size;

	sock = fileno(__FILEVal(fp));
	if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &size) >= 0) {
	    RETURN( __MKSMALLINT(opt) );
	}
    }
#endif
%}.
    ^ nil
!

receiveBufferSize:size
    "set the receive buffer size - for special applications only.
     Not all operatingSystems offer this functionality
     (returns false, if unsupported)"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#if defined(SO_RCVBUF) && defined(SOL_SOCKET)
    if (__isSmallInteger(size)) {
	OBJ fp = __INST(filePointer);
	int sock;
	int opt;

	sock = fileno(__FILEVal(fp));
	opt = __intVal(size);
	setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&opt, sizeof(int));
	RETURN(true);
    }
#endif
%}.
    ^ false
!

receiveTimeout:seconds
    "set the receive timeout - for special applications only.
     Not all operatingSystems offer this functionality
     (returns false, if unsupported)"

    |millis|

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
    millis := (seconds * 1000) rounded.
%{
#if defined(SO_RCVTIMEO) && defined(SOL_SOCKET) && defined(HZ)
    if (__isSmallInteger(millis)) {
	OBJ fp = __INST(filePointer);
	int sock;
	int opt;

	sock = fileno(__FILEVal(fp));
	opt = __intVal(millis) / (1000 / HZ);
	setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&opt, sizeof(int));
	RETURN(true);
    }
#endif
%}.
    ^ false
!

sendBufferSize
    "get the send buffer size - for special applications only.
     Not all operatingSystems offer this functionality
     (returns nil, if unsupported)"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#if defined(SO_SNDBUF) && defined(SOL_SOCKET)
    {
	OBJ fp = __INST(filePointer);
	int sock;
	int opt;
	int size;

	sock = fileno(__FILEVal(fp));
	if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&opt, &size) >= 0) {
	    RETURN( __MKSMALLINT(opt) );
	}
    }
#endif
%}.
    ^ nil
!

sendBufferSize:size
    "set the send buffer size - for special applications only.
     Not all operatingSystems offer this functionality
     (returns false, if unsupported)"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#if defined(SO_SNDBUF) && defined(SOL_SOCKET)
    if (__isSmallInteger(size)) {
	OBJ fp = __INST(filePointer);
	int sock;
	int opt;

	sock = fileno(__FILEVal(fp));
	opt = __intVal(size);
	setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&opt, sizeof(int));
	RETURN(true);
    }
#endif
%}.
    ^ false
!

sendTimeout:seconds
    "set the send timeout - for special applications only.
     Not all operatingSystems offer this functionality
     (returns false, if unsupported)"

    |millis|

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
    millis := (seconds * 1000) rounded.
%{
#if defined(SO_SNDTIMEO) && defined(SOL_SOCKET) && defined(HZ)
    if (__isSmallInteger(millis)) {
	OBJ fp = __INST(filePointer);
	int sock;
	int opt;

	sock = fileno(__FILEVal(fp));
	opt = __intVal(millis) / (1000 / HZ);
	setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&opt, sizeof(int));
	RETURN(true);
    }
#endif
%}.
    ^ false
!

setTCPCork:aBoolean
    "enable/disable TCP_CORK (do-not-send-partial-frames)
     For special applications only.
     Not all OperatingSystems offer this functionality
     (returns false, if unsupported)"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#if defined(IPPROTO_TCP) && defined(TCP_CORK)
    int onOff = (aBoolean == true);

    if ((aBoolean == true) || (aBoolean == false)) {
	OBJ fp = __INST(filePointer);
	int sock;
	int onOff = (aBoolean == true);

	sock = fileno(__FILEVal(fp));
	setsockopt(sock, IPPROTO_TCP, TCP_CORK, (char *)&onOff, sizeof(int));
	RETURN(true);
    }
#endif
%}.
    ^ false
!

setTCPNoDelay:aBoolean
    "enable/disable TCP_NODELAY (i.e. disable/enable the Nagle algorithm)
     For special applications only.
     Not all OperatingSystems offer this functionality
     (returns false, if unsupported)"

    filePointer isNil ifTrue:[
	^ self errorNotOpen
    ].
%{
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
    int onOff = (aBoolean == true);

    if ((aBoolean == true) || (aBoolean == false)) {
	OBJ fp = __INST(filePointer);
	int sock;
	int onOff = (aBoolean == true);

	sock = fileno(__FILEVal(fp));
	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&onOff, sizeof(int));
	RETURN(true);
    }
#endif
%}.
    ^ false
! !

!Socket class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic2/Socket.st,v 1.205 2004-12-20 19:57:03 cg Exp $'
! !