Despite its complexity, this layer is unashamedly provided to ease porting applications whose description in the TCP/IP literature is in terms of the well known BSD API. The Windows Winsock API is similar to the BSD API. Note that the PowerNet version does not provide all the facilities of the full BSD API. See SOCKETS\BSD.FTH.
These words are provided to factor out error handling in the BSD layer. By default they are implemented as COMPILER macros. If implemented as discrete words, remove the comments around the "R> DROP" phrases.
: ?se0X \ flag -- ; flag -- SOCKET_ERROR ; exits caller
If flag is true,
returns SOCKET_ERROR and exits the CALLING WORD; otherwise
does nothing.
This is provided as a factor for parameter testing.
: ?se1X \ n flag -- n | SOCKET_ERROR ; exits caller
If flag is true, drops 1 item,
returns SOCKET_ERROR and exits the CALLING WORD; otherwise
does nothing.
This is provided as a factor for parameter testing.
: ?se2X \ n1 n2 flag -- n1 n2 | SOCKET_ERROR ; exits caller
If flag is true, drops 2 items,
returns SOCKET_ERROR and exits the CALLING WORD; otherwise
does nothing.
This is provided as a factor for parameter testing.
: ?se3X \ n1 n2 n3 flag -- n1 n2 n3 | SOCKET_ERROR ; exits caller
If flag is true, drops 3 items,
returns SOCKET_ERROR and exits the CALLING WORD; otherwise
does nothing.
This is provided as a factor for parameter testing.
These words are not part of the official BSD interface. They are either factors or useful in low-level code.
: (pollSocket) \ *sk -- *pb|0|SOCKET_ERROR
Non-BSD function to poll a socket and return the next input
packet buffer if available.
: (ioctlRead) \ *arg *sk -- 0|SOCKET_ERROR
Return the amount of data that can be read from a socket,
storing it at address *arg.
: (ioctlState) \ *arg *sk -- res|SOCKET_ERROR
Return the current socket state to the address *arg.
: sendtoUDP { hsocket *buffer bufflen flags | *pb res -- res }
A factor used to send the *buffer/bufflen block of
memory by UDP.
: SaveSktRem \ *sk -- remip remport
A factor to return the socket's current remote IP address and port.
: RestoreSktRem \ remip remport *sk --
A factor to restore the socket's current remote IP address and port.
: recvInfo \ *sk *name --
A factor to extract the remote IP and port details from a
received TCP or UDP packet.
N.B. Replaces the now obsolete RecvUDPinfo
and
RecvTCPinfo
. RecvInfo
uses a socket structure as
input.
: getSocketInfo \ hs *name *len --
If *len contains at least SOCKADDR_IN
and
*name is non-zero, *name is the address of a
SOCKADDR_IN
structure. Copy the remote IP address and
port details to *name
.
This is a loose implementation of the BSD sockets interface. It is not exactly compliant but will run most BSD style code without too many nasty surprises.
$04004667F constant FIONREAD \ -- x
IOCTLSOCKET command code to obtain the number of bytes
available.
$080000001 constant GET_TCPSTATE \ -- x
IOCTLSOCKET command code to get the TCP state..
: ioctlsocket \ hs command *arg -- res|SOCKET_ERROR
Perform socket operations selected by the command
parameter. Data is returned at *arg. The supported
operations are FIONREAD and GET_TCPSTATE. FIONREAD works
with both UDP and TCP sockets.
: pollSocket \ hsock -- #bytes|SOCKET_ERROR
Non-BSD function to poll a socket and return the number of
bytes available to be read. Can be used with both UDP and TCP
sockets.
: bind \ hs *name namelen -- res
Associate a socket with a family, protocol and port. The
parameters *name and namelen (addr/len) describe a
SOCKADDR_IN
structure. The family must be AF_INET. The
port is the port number that will be listened to. The IP
address is usually 0, in which case the system's IP address
will be used. This allows use with systems which obtain an
IP address dynamically, e.g. through DHCP.
: bindTo \ hs af port ipaddr -- res
A non-BSD function that binds a socket to the given set of
address family (af), port (port) and IP address (ipaddr).
The returned result (res) is 0 for success, otherwise
SOCKET_ERROR. N.B. subject to change. See BIND
.
: closesocket \ hs -- 0|SOCKET_ERROR
Close the socket. This is the close of last resort
as it simply reclaims socket memory without performing
any notification to the other end. Use DISCONNECT
in
preference.
: connect \ hs *name namelen -- res
Open a connection to a destination defined by the
SOCKADDR structure described by *name/namelen.
The adress family in the structure must be AF_INET.
On success (res=0), the socket is ready to send and
receive data.
: disconnect \ hs -- res
Disconnect the socket. Returns 0 on success.
: DiscAllSockets \ --
Disconnect all socket connections.
: socket \ address-family socket-type proto-group -- hs|0
Create a new socket of the given characteristics, returning
the new socket number (1..n) on success, or zero for failure.
DO NOT CHANGE the return of INVALID_SOCKET
=zero
for failure!
: sendto \ hsocket *buffer bufflen flags *name namelen -- len/err
A factor used to send the *buffer/bufflenlen block of
memory by TCP. If *name is a valid SOCKADDR_IN
structure then save the current remote address for the socket
and override with supplied settings using CONNECT
.
: send \ hsocket *buffer bufflen flags -- #sent|socket_error
The more common form of sendto
when no address override
is required.
: recvfrom \ hsocket *buffer bufflen flags *name *namelen -- res
Receive data from a socket. The parameters are as for
SENDTO
, except that *namelen is a pointer to the
length. If *name/*namelen is a valid SOCKADDR_IN
structure, on return it will contain the sender's address and port.
Note that only one packet is read, and if you do not read
all of it, the remaining data is not discarded.
: recv \ hsocket *buffer bufflen flags -- len|err
Receive up to bufflen bytes of memory from the current packet
returning the length read.
Note that only one packet is read, and if you do not read
all of it, the remaining data is not discarded.
: Listen \ hs -- 0|SOCKET_ERROR
Starts a bound socket listening on the port specified
by the previous BIND
or BINDTO
operation.
LISTEN
just changes the mode of the socket; it does
not wait for anything to happen.
The socket must be a TCP socket.
: -Listen? \ hs -- state true | 0
Check the socket state, and return the state and true if it
not listening. Otherwise, just return false.
: waitListener \ hs -- flag
Wait until a connection is established or closed/closing.
Return flag=false if established. If the socket is not
established, there has been an error in the listening socket.
: waitConnQ \ hs -- ior
Wait until the listening socket has a connection pending.
Return 0 on success, or non-zero if the listening socket
has failed.
: repSocket \ hs1 -- hs2|0
Given a socket, make another of the same type using hs1
as a template.
: repService \ *sk1 *sk2 -- ior
create a new service data structure for sk2 using
the details in sk1 as a template.
: WaitNextConn \ hsl -- hsa 0 | reason -1
Wait for the next connection on listening socket hsl,
create a socket and service for the connection, and return
the new socket hsa and 0. If a failure occurs, return
reason and -1. If the listening socket failed reason
is non-zero.
: Saccept \ hs *skaddr *sklen -- hsnew|SOCKET_ERROR
The PowerNet version of the BSD accept() call is named
Saccept
to prevent a name conflict with the input
routine accept
. Wait until a connection has been made
to the listening TCP socket hs or there is an error in the
socket. If a connection has been established, transfer it to
a new socket hsnew. If *skaddr is non-zero the
contents of *sklen are at least SOCKADDR_IN, *skaddr
is filled in with the IP address and port of the remote end.
After a successful operation, the new socket is used for the connection just made, and the old socket remains listening.
If SOCKET_ERROR is returned, no new socket is available and previous socket hs should be checked to see if it should be closed and remade.
These extensions make life a bit simpler when connecting to servers.
: TCPsocket \ -- hs|0
Create a TCP socket. Return the socket handle on success
or INVALID_SOCKET
(0) on error.
: connectTo \ caddr u port# socket -- socket ior
Attempt to connect to a server. The socket has already been
created in the appropriate mode. The value of socket must
be a positive non-zero number.
Caddr/u describes the server address either as a name
or an IPaddress string and port# is the requested port.
If u is zero, caddr is treated as a 32 bit number
representing an IPv4 address.
In this implementation, u must be zero.
On success, the socket and zero are returned, otherwise
SOCKET_ERROR and an error code are returned.
: TCPConnect \ c-addr u port# -- socket ior
Attempt to create a TCP socket and connect to a server.
Caddr/u describes the server address either as a name
or an IPaddress string and port# is the requested port.
If u is zero, caddr is treated as a 32 bit number
representing an IPv4 address.
In this implementation, u must be zero.
On success, the socket and zero are returned, otherwise
SOCKET_ERROR and an error code are returned.
: UDPConnect \ c-addr u port# -- socket ior
Attempt to create a UDP socket and connect to a server.
Caddr/u describes the server address either as a name
or an IPaddress string and port# is the requested port.
If u is zero, caddr is treated as a 32 bit number
representing an IPv4 address.
In this implementation, u must be zero.
On success, the socket and zero are returned, otherwise
SOCKET_ERROR and an error code are returned.