PowerNet supports multithreaded TCP servers that can accept multiple connections. The model used is that one task listens on a port, establishes a connection, passes the socket to a service task to run the service, and then creates a new listening socket. When a connection to a service task is no longer established, a support task cleans up the service specific data, closes the service socket, and terminates the service task.
The focus of the tools is for servers using text, e.g. HTTP
and Telnet. To enable use of standard Forth words and parsing
tools, a Generic I/O device and a TIB
are established
for each connection .
From PowerNet v4.6 onwards and then again in v4.8, much
attention to reducing RAM usage has taken place. This comes
at the expense of performance. Use of the the low RAM
configuration may be necessary for single-chip applications.
From v4.8 onwards, you can use the equates SVlowRAM?
andSVsingle?
in the PowerNet configuration file to
reduce RAM consumption in listen state by up to 2kb per
listening socket above the reductions already achieved in
the TCP layer.
These are MPE defined for use in the socket structure.
0 equ service_none \ -- n
Defines a null service.
1 equ service_Telnet \ -- n
Defines a Telnet service.
2 equ service_HTTP \ -- n
Defines an HTTP service.
3 equ service_FTP \ -- n
Defines an FTP service.
8 equ service_Echo \ -- n
Defines an Echo service.
9 equ service_MultiChat \ -- n
Defines a MultiChat service. Windows version only.
10 equ service_ModBus
Defines a ModBus TCP service.
20 equ service_App
Service number for an application-specific service.
: .service \ n --
Display the service type corresponding to n.
These data definitions are required by each server task. The
data is allocated at the start of the task and released when
the task is TERMINATE
d. The chain SVchain
links
all the service tasks. Each task has USER
variables
MY_SOCKET
and MY_HSOCKET
which hold the socket
address and socket number. From these, the service specific
data can be found.
The first part of a service data area is common to all
services, and additional fields can be added as required.
See TELNET.FTH and HTTP.FTH for examples. The
common data area includes service managment data and facilities
for routing input and output through the normal Forth KEY
and EMIT
wordset.
variable SVchain \ -- addr
Holds the tail of the service chain.
#64 equ /SVOB \ -- n
Size of a service output buffer. A value of 64 or 128 bytes
is sufficient to provide satisfactory output for Telnet.
#64 equ /SVIB \ -- n
Size of a service input buffer.
#256 equ /SVtib \ -- n
Size of the service's TIB area.
struct /SVdata \ -- len
Defines the common data in a service specific data area.
int SVlink \ link to previous service \ N.B. MUST BE FIRST int SVtask \ task that runs this service int SVsk# \ socket# used by this service int SVsendflags \ TCP override flags for lower layers int SVdone \ set nz to close service int SVappClean \ *sv -- *sv ; application specific clean up xt dup equ /CSVdata \ size of core service data \ end of core structure /SVtib field SVtibBuff \ service TIB, e.g. for Telnet dup equ /MSVdata \ size of core+TIB service data \ end of core plus input buffer SVlowRAM? 0= [if] int #SVOB \ number of characters in SVOB ; SFP002 /SVOB field SVopBuff \ service output buffer ; SFP002 int #SVIB \ number of characters in SVIB int ^SVIB \ offset of next character in SVIB /SVIB field SVipBuff \ service raw input buffer [then] end-struct
The field SVappClean
contains the xt of a word
cleanFTP ( *sv -- *sv )
that frees any aqdditional resources that are not released
by free the service data area. For an example, see cleanFTP
in SERVICES/FTP.fth.
: MySVD \ -- addr
Returns the address of the task's service data.
: SVTib \ -- tib
Returns the address of the TIB buffer for this task.
: SVdone? \ -- flag
Return true if the socket can be closed.
: SVbye \ --
Set the SVdone
exit flag.
: CheckSV \ -- flag
Run by the service tasks to check whether the service task should
be closed. Flag is returned true if the service task should be closed.
$0A equ AcceptChar \ -- char
The character returned bySVkey
below when an error
has occurred. This should be the character that
SVaccept
uses as a line terminator, usually LF.
In this version all buffering is performed in the socket layer. This saves RAM at the cost of performance. On a local area network the penalty may be significant - it certainly is for Telnet.
: SVkey? \ -- flag
Return true if a character is avilable from the service's
client or an error has occurred.
: SVkey \ -- char ; receive char
Return a character from the service's client.
If the service's SVdone
flag is set, an LF is
returned.
: SVtype \ caddr len --
Send a string/buffer to the service's client.
: SVemit \ char --
Send a character to the service's client.
: SVcr \ --
Send a CR/LF pair to the service's client.
: SVflushOP \ --
A compatibility NOOP
in the low RAM version.
: SVIBuffer \ -- addr
Returns the address of the input buffer for this task.
: SVOBuffer \ -- addr
Returns the address of the output buffer for this task.
: isSVInput \ --
Read any available characters from the incoming TCP stream
into the service input buffer.
: (SVkey?) \ -- flag ; check receive char
Return true if a character has been received by the server
or the service must be closed.
: SVflushOP \ -- ; SFP002
Send current buffer if not empty by passing it to the
socket. SVflushOP
is used by SVkey?
and
SVkey
so that pending output is transmitted. If
your code does not call either of these, e.g. through the
Generic I/O, you should add SVflushOP
to your code
where appropriate.
: (SVemit) \ char -- ; SFP002
Send a character to a client of this service.
: SVkey? \ -- flag ; check receive char
Return true if a character has been received by the server.
Any pending output characters are sent.
: SVkey \ -- char ; receive char
Return a character from the service's client. Any pending
output characters are sent first. If the service's SVdone
flag is set or a socket error has occurred, an LF character
is returned.
: SVemit \ char -- ; emit char
Send a character to a service's client.
: SVtype \ caddr len -- ; display string
Send a string to a service's client
: SVcr \ -- ; display new line
Send a new line sequence to a service's client
create ConsoleSV \ -- addr ; OUT managed by upper driver
Function despatch table for service I/O.
OUT
is managed by the upper level driver.
: Console=SV \ --
Select the service I/O as the console.
: Init-ConsoleSV \ --
Initialise for console I/O by the service. Note that the
service's socket must have been set up and and the private
service area initialised.
: +SV_responsive \ --
Set the socket to use the TCP_PSH flag when sending
to improve interactivity.
: -SV_responsive \ --
Set the socket not to use the TCP_PSH flag when sending.
: SV_responsive? \ -- res
Return non-zero if the socket's TCP_PSH flag override is set.
: SVaccept \ c-addr +n1 -- +n2 ; read up to LEN chars into ADDR
Read a string of maximum size n1 characters to the buffer at
c-addr, returning n2 the number of characters actually read.
Input is terminated by LF, and CR is ignored. This satisfies
the requiresments of DOS, Windows, Unices and the TCP/IP NVT
(Network Virtual Terminal).
If ECHOING
is non-zero, characters are echoed.
If XON/XOFF
is non-zero, an XON character is sent at the
start and an XOFF character is sent at the the end.
: SVquery \ -- ; fetch line into TIB
Reset the input source specification to the console and
accept a line of text into the input buffer.
: isMySocket \ hs --
Set up USER
variables for this socket.
: NoSocket \ --
Zero the USER
variables MY_HSOCKET
and MY_SOCKET
.
: ShutErrSocket \ -- socket_error
Close the current server socket and return SOCKET_ERROR
.
All allocated socket and service memory is released by the
close.
: InitServerSocket { #service /data #port -- res }
Create a listening socket and the private data for
a TCP service conversation. On success, the USER
variables MY_HSOCKET
and MY_SOCKET
contain the
socket number and socket address. On failure, these variables
contain zero.
#service |
service type number. |
/data |
size of the service private data area, at least /SVDATA. |
#port |
port number to listen on. |
res |
socket number (1..n) or SOCKET_ERROR. |
: SVinitiate \ xt -- task|0
Allocates memory for a server task and INITIATE
s it with the
given xt, returning the task's TCB address. If memory
cannot be allocated, zero is returned.
: (SVterminate) \ task --
Terminates a server or service task and frees off the task
memory. Does not PAUSE
: SVterminate \ task --
Terminates a server or service task and frees off the task
memory.
: .SVmessage \ caddr len --
Display a message to the current output (usually the console)
in the form:
<service> <given text> on socket <n>
The USER
variables MY_SOCKET
and MY_HSOCKET
must contain valid data.
: ServiceCreate \ #service /data port# --
Create a new service listening socket.
: StartService \ hs xt --
Initialise and launch a new service task on socket hs.
The action of the new task is given by xt
given by xt.
: waitSocketSent \ hs --
Wait until all transmit data for a socket has been sent and
acked.
: wait-socket-empty \ --
Wait until all output has been sent from the socket.
: SVdisconnect \ --
Disconnect
the current service socket and run SVbye
.
: SVstartup \ --
Performs the default actions when a service starts.
I/O is set to the console, BASE
to decimal, and
a console message is issued.
: SVshutdown \ --
Performs actions required when a service finishes.
: SRVRstartup \ caddr len --
The first action a server task should perform. The string
is displayed on the console with a startup message.
In order to avoid race conditions, a separate task handles testing whether a service should be closed.
variable SVkillChain \ -- addr
Holds the tail of service tasks to be destroyed.
: ?SVkill \ *SVdata --
If the service has a task, i.e. is a service rather than a
server, move it to the kill chain, otherwise just free the
memory.
: ?ServiceClose \ --
Close any service tasks with a non-zero SVDONE
field
in the private service area.
: ?ServiceKill \ --
Kill any service tasks that are on the kill chain and free
the task and service memory. The kill chain is extended by
CloseSocket
. The service task clean up action (the xt
in the SVappClean
field) is run before memory is freed.
The field SVappClean
contains the xt of a word
cleanFTP ( *sv -- *sv )
that frees any aqdditional resources that are not released
by free the service data area. For an example, see cleanFTP
in SERVICES/FTP.fth.
: ServiceIO
Handle any queued output - called in the service task.
Diagnostic code is only compiled if the EQU
ate DIAGS?
is set non-zero.