This document summarizes the GT.M Socket Device Interface. The complete document, including the sample programs is found in the FTP area of this project. The intended readers of this document are (GT.)M programmers who need to write GT.M programs that interface with other TCP/IP processes.
The GT.M Socket Device Interface is the successor of the GT.M TCP Device Interface. It is closer to the MDC standard in its format and functionality. Major improvements over the GT.M TCP Device Interface lie in the delimiter handling and the device organization. There are, however, some deviations from the MDC standard, that relate directly to the GT.M model:
In most operating environments the system maintains a "pool" of sockets. A program that needs to cummunicate over the network will request a socket (from the system), use it for a network connection, and return the socket (to the system). One program can work with multiple sockets, and sockets can often be passed to different processes. This model of handling and passing sockets is essential for many internet-based services.
The other important model in this context is the TCP/IP client-server model. In this model, there are servers, that provide services, and clients that request services. The essential difference is that the server is more or less "passive", whereas the client "actively" requests a service. The following steps depict the general flow of this model:
The example client program connects to a specified host, at a specified port, sends one or more requests to the server, closes the connection and quits. The most important steps are discussed here:
set %ZNDev="SCK$"_$J
OPEN %ZNDev:(
param1:param2):%ZNTimeS:"SOCKET"
:"SOCKET"
that follows the timeout specifies
that the SOCKET-mnemonicspace is in effect (and thus device
parameters apply to that mnemonicspace).
The meaning of the device parameters (in the sample program) is as
follows:
CONNECT=%ZNHost_":"_%ZNPort_":TCP"
DELIMITER=$CHAR(13,10,58,27,95)
$CHAR(13,10)
and $CHAR(27,95)
. They are
separated by a colon ($CHAR(58)
).ATTACH="client"
The ATTACH parameter gives a name to the socket that will be
allocated for the connection. Explicit naming of the client socket
in this example has no extra use, because the socket is never
moved around (e.g. by DETACHing it from this device, and ATTACHing
it to another device owned by this process).
So this too, is a matter of taste.USE %ZNDev:(IOERROR="TRAP":EXCEPTION="ZGOTO "_$ZLEVEL_":clientD")
SET ^SCKCLI($job,0,3)=$KEY
ESTABLISHED|
socket_handle|host_ip_address".
The IOERROR device parameter on the USE command specifies that IO
exceptions shall be trapped as runtime errors.
The GT.M Socket Device Interface recognizes this MDC
standard parameter, but ignores it.
The EXCEPTION device parameter is supported on all GT.M IO devices.
It instructs GT.M to generate a runtime exception on device errors,
and specifies the errorhandling code to be executed (similar to
set $ZTRAP=
).
. WRITE %ZNReq(%Z0),!
. READ %ZNRsp:%ZNTimeS ELSE set ^SCKCLI($job)="-2,timeout" quit
. set ^SCKCLI($job,%Z0,2)=$DEVICE
. set ^SCKCLI($job,%Z0,3)=$KEY
,!
to the WRITE command, the first delimiter (in our case
$CHAR(13,10)
) is appended to the data stream, and the
data is flushed.
After writing the data, the response from the server is READ.
This may result in a non-zero $DEVICE, or in data terminated by one
of the delimiters (reflected in $KEY).
. WRITE %ZNReq_$CHAR(27,95) ;,#
#
) to
the WRITE-command (or wait until the TCP/IP layer decides to flush the
data).
However, the current GT.M Linux version will append a formfeed character
($CHAR(12)
) to the datastream when you issue a
WRITE #
. So in the example programs the ",#" is
commented out.
ZSHOW "DI":^SCKCLI($job,"CLOSE") CLOSE %ZNDev
The example single connection server program opens a socket device, listens for an incoming connection, handles the requests from that connection, and quits. The most important steps are discussed here:
OPEN %ZNDev:(
param1:param2):%ZNTimeS:"SOCKET"
ZLISTEN=%ZNPort_":TCP"
DELIMITER=$CHAR(13,10,58,27,95)
ATTACH="listener"
The ATTACH parameter gives a name to the socket that will be
allocated on the OPEN. This socket will, by definition, be used to
listen for incoming connections.
By naming this socket explicitely, it is possible to close the
listen socket without closing the connection socket. use %ZNDev set ^SCKSERV($job,0)=$KEY
BOUND|
socket_handle|portnumber".
WRITE /LISTEN(1) set ^SCKSERV($job,1)=$KEY
LISTENING|
socket_handle|portnumber".
FOR DO QUIT:^SCKSERV($job)
. WRITE /WAIT(%ZNTimeS)
. IF $KEY]"" SET ^SCKSERV($job,2)=$KEY,^SCKSERV($job)=2 QUIT
CONNECT|
socket_handle|remote_ipaddress"),
and read events (reflected by a $KEY value with the format
"READ|
socket_handle|remote_ipaddress").
The sample program just waits for the first event (which must be a
connection event).
set %ZNSock=$piece($KEY,"|",2)
CLOSE %ZNDev:(SOCKET="listener")
USE %ZNDev:(SOCKET=%ZNSock)
SOCKET="listener"
to the CLOSE command.
Atfer closing the listen-socket, the program directs its IO operations
explicitely to the connection socket.
WRITE !
, or WRITE #
.
IF $KEY=$CHAR(13,10) DO ;database request
. IF %ZNCmd="get" WRITE "val ",@%ZNData,! QUIT
IF $KEY=$CHAR(27,95) DO ;command
. WRITE %ZNCmd_$CHAR(27,95) ;,#
CLOSE %ZNDev
stop
command, (or "someone"
explicitely SET ^SCKSERV(jobnr),) this procedure is about to quit.
It can close the device. This implicitely closes "all" sockets that
are still associted with the device.