Program Examples using Interprocess Communication
ORDER Client/Server
The SALES system has a program to take care of orders being placed for products. The order entry clerk must check the customer's credit rating from data held on the FINANCE system. The orders program on the SALES system is written as a client, which makes use of a server program running on the FINANCE system.
Client Program
The SALES order processing program includes the following code:
*(near the beginning of the program) CONNECT "FINANCE":AM:"SALES-ACCOUNT":AM:... "ORDER-SERVER" TO SESS ELSE.. * This starts and establishes contact with the * server on the FINANCE system. * *(while processing an order) INFO<1>=ORDER.NO INFO<2>=CUSTOMER INFO<3>=COST * SEND INFO TO SESS ELSE.. * RECWAIT RESPONSE FROM SESS ELSE.. IF RESPONSE="OK" THEN PRINT "Credit OK, order accepted by FINANCE." END ELSE PRINT "Credit NOT OK, do not trust this one!" * Take care of bad customer END * *(near end of program) DISCONNECT SESS ELSE.. *
Server Program
On the FINANCE system, the program ORDER-SERVER on the SALES-ACCOUNT includes the following code:
* near the beginning ACCEPT "ORDER-SERVER" TO SESS ELSE.. * This connects with the client process. * RECWAIT INFO FROM SESS ELSE.. * ORDER.NO=INFO<1> CUSTOMER=INFO<2> COST=INFO<3> * READ CREDIT FROM CUST.FILE,CUSTOMER ELSE.. IF CREDIT>=COST THEN SEND "OK" TO SESS ELSE.. WRITE INFO TO ACCOUNT.REC.FILE,ORDER.NO END ELSE SEND "NOT OK" TO SESS ELSE.. END *
Analysis
When the ORDER program runs, the SALES system talks to the FINANCE system. The operating system on FINANCE checks to see if a program has issued an ACCEPT with the server name "ORDER-SERVER". If there is an ACCEPT outstanding, a session is established between the two programs. If not, "ORDER-SERVER" is issued as a TCL command on the "SALES-ACCOUNT". This invokes the DataBasic program titled "ORDER-SERVER". When the "ORDER-SERVER" program issues its ACCEPT statement, the connection is established.
Multiple Server Program
The server program could be designed to accommodate several client programs, all programmed to make the same inquiry and obtain the same type of information from the server program.
In the following example, three client programs connect to the server program. The sessions are called SESS1, SESS2, and SESS3. The server in the CONNECT statements of the three client programs are ORD-SERV1, ORD-SERV2, and ORD-SERV3. The MD of the SALES-ACCOUNT contains items labeled ORD-SERV1, ORD-SERV2, and ORD-SERV3. Each contains the following attributes:
PQN
HRUN SALES ORDER-SERVER
P
The ORDER-SERVER program includes the following code:
ACPT1: ACCEPT "ORD-SERV1" TO SESS1 THEN GO READ1 ELSE FLG1=0 GO READ2 ACPT2: ACCEPT "ORD-SERV2" TO SESS2 THEN GO READ2 ELSE FLG2=0 GO READ3 ACPT3: ACCEPT "ORD-SERV3" TO SESS3 THEN GO READ3 ELSE FLG3=0 GO READ1 * READ1: IF FLG1=0 THEN GO ACPT1 RECEIVE INFO FROM SESS1 THEN GOSUB ... RUN.IT ELSE GO READ2 SEND REPLY TO SESS1 SETTING E1 ELSE ... GO ERR.VAL READ2: IF FLG2=0 THEN GO ACPT2 RECEIVE INFO FROM SESS2 THEN GOSUB ... RUN.IT ELSE GO READ3 SEND REPLY TO SESS2 SETTING E2 ELSE ... GO ERR.VAL READ3: IF FLG3=0 THEN GO ACPT3 RECEIVE INFO FROM SESS3 THEN GOSUB ... RUN.IT ELSE GO READ1 SEND REPLY TO SESS3 SETTING E3 ELSE ... GO ERR.VAL GOTO READ1 RUN.IT: ORDER.NO=INFO<1> CUSTOMER=INFO<2> COST=INFO<3> * READ CREDIT FROM CUST.FILE,CUSTOMER ELSE.. IF CREDIT>=COST THEN REPLY="OK" WRITE INFO TO ACCOUNT.REC.FILE,ORDER.NO END ELSE REPLY="NOT OK" END RETURN * ERR.VAL: * Evaluate error code, why can't send data
Notice that the RECWAIT statement of the original program is replaced with RECEIVE statements. If data is there to be read, it is read; otherwise, the program advances to the next receive operation.
FPUT Client/FGET Server
The example program FPUT listed below communicates with the example program FGET that follows. These programs are executed from separate processes (usually running on different systems connected to a common network, although for testing purposes they can be run on the same system).
Client Program
FPUT sets up the connection and starts FGET, then waits for input from FGET. It cycles round until the session is terminated by FGET. It then reports statistics and stops. See below.
EQUATE AM TO CHAR(254) PRINT "REMOTE SYSTEM ": INPUT RSYS CRT "CONNECTING..." CONNECT RSYS:AM:'CHRIS':AM:'FGET' TO S TIMEOUT 2 SETTING ECODE THEN STTIME = SYSTEM(12) BYTES = 0 CNT = 0 XCNT = 0 FAILED= 0 CRT "CONNECTED" LOOP UNTIL FAILED DO RECWAIT DT FROM S SETTING ECODE ELSE PRINT "RECEIVE ERROR: ":ECODE CRT "AVERAGE TRANSMISSION RATE = ":... 200100/(SYSTEM(12)-SSTIME):"BYTES PER SECOND." DISCONNECT S SETTING ECODE ELSE PRINT "DISCONNECT ERROR: ":ECODE END FAILED = 1 END END ELSE CRT "CAN'T CONNECT - ERROR: ":ECODE END END
Server Program
The program FGET is started as a server by FPUT. (It must therefore be cataloged on the account CHRIS on remote system RSYS to which connection is made by FPUT.) It accepts the connection then transmits a series of values to FPUT before disconnecting. See below.
1 CRT 'ACCEPTING...': ACCEPT "FGET" TO S TIMEOUT 2 SETTING ECODE ELSE PRINT 'CONNECT ERROR : ' : ECODE STOP END CRT 'ACCEPTED.' ! DT = STR("+",2000) FOR X = 1 TO 100 SEND CHAR(X):DT ON S SETTING ECODE ELSE CRT "SEND ERROR = ":ECODE 100 DISCONNECT S SETTING ECODE ELSE PRINT 'DISCONNECT ERROR : ' : ECODE END STOP END NEXT X ! SEND "END" ON S SETTING ECODE ELSE CRT "SEND ERROR = ":ECODE GOTO 100 END 2 DISCONNECT S SETTING ECODE ELSE PRINT 'DISCONNECT ERROR : ' : ECODE STOP END GOTO 1 END
PERFORM Client/Server
Note
The code for these example programs can be found in the file /SYSPROG/BP.
Client Program
The program PERFORM-CLIENT connects to a system and an account selected by the user, and starts program PERFORM-SERVER on that system/account. A command entered by the user is sent to PERFORM-SERVER. The response from PERFORM-SERVER is displayed. This continues until END is entered, disconnecting the link. See below.
AM = CHAR(254) PRINT 'SYSTEM ': INPUT SYS PRINT 'ACCOUNT ': INPUT ACCN CRT 'CONNECTING...': CONNECT SYS : AM : ACCN : AM : "PERFORM-SERVER" TO S TIMEOUT 1... SETTING ECODE ELSE GOTO 99 CRT 'OK.' 1 INPUT COMMAND IF COMMAND='END' THEN GOTO 88 SEND COMMAND ON S SETTING ECODE ELSE GOTO 71 RECWAIT STUFF FROM S SETTING ECODE ELSE GOTO 72 * CRT STUFF N = COUNT(STUFF,AM)+1 FOR I=1 TO N CRT STUFF<I> NEXT I GOTO 1 71 CRT 'TX ERROR - ':ECODE GOTO 88 72 CRT 'RX ERROR - ':ECODE 88 CRT 'DISCONNECTING...': DISCONNECTING S SETTING ECODE ELSE GOTO 99 CRT 'OK.' STOP 99 CRT "ERROR - ":ECODE END
Server Program
This program is started following a request from PERFORM-CLIENT. It accepts the connection, then waits to receive input from PERFORM-CLIENT.
The data from PERFORM-CLIENT should be a command. PERFORM-SERVER executes the command via a PERFORM statement and sends the result to PERFORM-CLIENT (which displays the result on the local terminal). This continues until PERFORM-CLIENT terminates the session. See below.
CRT 'ACCEPTING...': ACCEPT "PERFORM-SERVER" TO S TIMEOUT 1 SETTING ECODE ELSE GOTO 99 CRT 'OK.' 1 CRT '?': RECWAIT X FROM S SETTING ECODE ELSE GOTO 71 CRT X PERFORM X CAPTURING Y SEND Y ON S SETTING ECODE ELSE GOTO 72 GOTO 1 71 CRT 'RX ERROR - ':ECODE GOTO 88 72 CRT 'TX ERROR - ':ECODE 88 CRT 'DISCONNECTING...': DISCONNECT S SETTING ECODE ELSE GOTO 99 CRT 'OK.' STOP 99 CRT 'ERROR - ':ECODE END
TCP Client/Server
The following client and server programs demonstrate how you might use TCP/IP to communicate between two Reality systems.
Note
The code for these example programs can be found in the file /SYSPROG/BP.
Client Program
* RTCL - Execute remote TCL command * * RTCL Host|* TclCommand -- * for localhost" * * Connect to XTCL listening on HostName, port 52002, send TCL * command which XTCL will PERFORM and return the response. * * Set environment variable RNWS_LOG_LEVEL in range 0 to 7 for * tracing. Set environment variable RNWS_LOG_FILE to specify trace * file. * EQU EDISCONN TO 4235 * TCL = SENTENCE() HOST = FIELD(TCL," ",2) CMD = FIELD(TCL," ",3) PORT = 52002 * IF HOST = "" OR HOST = "?" THEN PRINT "RTCL Host|* {TclCommand} -- * for localhost" STOP END * IF HOST = "*" THEN HOST = "127.0.0.1" SYS="*TCP*":HOST:";port=":PORT * CONNECT SYS TO SESS SETTING ERRNO ELSE OPN = "CONNECT" GOTO FIN END * ONEOFF = CMD # "" LOOP IF NOT(ONEOFF) THEN INPUT CMD WHILE CMD # "" DO SDAT=CMD GOSUB SEND_SDAT IF ERRNO THEN GOTO FIN GOSUB RECV_RDAT IF ERRNO THEN GOTO FIN PRINT RDAT<1> IF ONEOFF THEN GOTO FIN REPEAT * FIN: * IF ERRNO THEN IF ERRNO = EDISCONN THEN PRINT SYS:" disconnected" END ELSE PRINT OPN:" (":SYS:") failed, ERRNO=":ERRNO END END * IF NOT(UNASSIGNED(SESS)) THEN DISCONNECT SESS SETTING ERRNO ELSE PRINT "DISCONNECT (":SYS:") failed, ERRNO=":ERRNO END END * IF NOT(UNASSIGNED(LSESS)) THEN DISCONNECT LSESS SETTING ERRNO ELSE PRINT "DISCONNECT(listener) (":SYS:") failed, ERRNO=":ERRNO END END * STOP * ** *** SEND_SDAT: ********** MSG = "<":SDAT:">" SEND MSG TO SESS SETTING ERRNO ELSE OPN = "SEND" END RETURN * ** *** RECV_RDAT: ********** RDAT = "" LOOP RECEIVE MSG FROM SESS SETTING ERRNO ELSE OPN = "RECEIVE" GOTO FIN_RECV_RDAT END RDAT = RDAT:MSG AGAIN = RDAT[-1,1] # ">" WHILE AGAIN DO REPEAT * RDAT[-1,1] = "" RDAT[1,1] = "" * FIN_RECV_RDAT: * RETURN * ** *** END ***
Server Program
* XTCL - Perform TCL command from remote client * * XTCL LocalHostName|* -- * for all local interfaces * * Listen on port 52002 for incoming connection, receive and perform * TCL command and return response to client. Loop back for next * command or client disconnect. 'RTCL host OFF' will log us off. * * Set environment variable RNWS_LOG_LEVEL in range 0 to 7 for tracing. * Set environment variable RNWS_LOG_FILE to specify trace file. * EQU EDISCONN TO 4235 * LOOPING = 0 TCL = SENTENCE() HOST = FIELD(TCL," ",2) PORT = 52002 * IF HOST = "" OR HOST = "?" THEN PRINT "XTCL LocalHostname|* -- * for all local interfaces" STOP END * IF HOST = "*" THEN HOST = "" END * SYS="*TCP*":HOST:";port=":PORT * * Activate listening socket * ACCEPT SYS:";listen=1" TO LSESS SETTING ERRNO ELSE OPN = "ACCEPT(listen)" GOTO FIN END * * Accept incoming connection * LOOPING = 1 ERRNO = 0 LOOP ACCEPT SYS TO SESS SETTING ERRNO ELSE OPN = "ACCEPT" GOTO FIN END * LOOP ERRNO = 0 GOSUB RECV_RDAT IF ERRNO = 0 THEN IF RDAT = "STOP" THEN SDAT = "CLOSING SERVER" LOOPING = 0 END ELSE PERFORM RDAT CAPTURING SDAT END GOSUB SEND_SDAT END WHILE ERRNO = 0 DO REPEAT IF ERRNO = EDISCONN THEN ERRNO=0 * FIN: * IF ERRNO THEN PRINT OPN:" (":SYS:") failed, ERRNO=":ERRNO END * IF NOT(UNASSIGNED(SESS)) THEN DISCONNECT SESS SETTING ERRNO ELSE PRINT "DISCONNECT (":SYS:") failed, ERRNO=":ERRNO END END * WHILE ERRNO = 0 AND LOOPING DO REPEAT * IF NOT(UNASSIGNED(LSESS)) THEN DISCONNECT LSESS SETTING ERRNO ELSE PRINT "DISCONNECT(listener) (":SYS:") failed, ERRNO=":ERRNO END END * STOP * ** *** SEND_SDAT: ********** MSG = "<":SDAT:">" SEND MSG TO SESS SETTING ERRNO ELSE OPN = "SEND" END RETURN * ** *** RECV_RDAT: ********** RDAT = "" LOOP RECEIVE MSG FROM SESS SETTING ERRNO ELSE OPN = "RECEIVE" GOTO FIN_RECV_RDAT END RDAT = RDAT:MSG AGAIN = RDAT[-1,1] # ">" WHILE AGAIN DO REPEAT * RDAT[-1,1] = "" RDAT[1,1] = "" * FIN_RECV_RDAT: * RETURN * ** *** END ***
TCP Ping
Note
The code for this example program can be found in the file /SYSPROG/BP.
* PING - Reality sockets test program to send/receive to echo server * * PING HostName * * Provides similar functionality to the UNIX ping utility. A * connection is established to a remote TCP 'echo' server specified * by argument 1. A message is sent and echo received, five times. * * Set environment variable RNWS_LOG_LEVEL in range 0 to 7 for * tracing. Set environment variable RNWS_LOG_FILE to specify trace * file. * TCL = SENTENCE() HOST = FIELD(TCL," ",2) PORT=7 * IF HOST = "" OR HOST = "?" THEN PRINT "PING [HostName|IP address]" STOP END * SYS="*TCP*":HOST:";port=":PORT * CONNECT SYS TO SECHO TIMEOUT 1 SETTING ERRNO ELSE PRINT "Failed to connect to ":SYS:", error=":ERRNO GOTO L_ABORT END * SMSG="Data/Basic socket call to echo port" * FOR I = 1 TO 5 SEND SMSG TO SECHO SETTING ERRNO ELSE PRINT "Failed to send msg to ":SYS:", error=":ERRNO GOTO L_ABORT END * RECWAIT RMSG FROM SECHO TIMEOUT 1 SETTING ERRNO ELSE PRINT "Failed to receive msg from ":SYS:", error=":ERRNO GOTO L_ABORT END PRINT "Received:'":RMSG:"' from ":HOST NEXT I * L_ABORT: IF NOT(UNASSIGNED(SECHO)) THEN DISCONNECT SECHO SETTING ERRNO ELSE PRINT "Failed to disconnect from ":SYS:", error=":ERRNO END END * STOP * END
TCP Email
Note
The code for this example program can be found in the file /SYSPROG/BP.
* MAIL - send email * * MAIL <To:> <From:> <SmtpServer> * * Connect to SMTP service on specified DNS name and pass sender and * recipient's mail path, viz email addresses, finally send the email * body. * * This program was based on information found in RFC 821, it in no * way handles every eventuality and is for example purposes only. * EQU EDISCONN TO 4235 EQU TRACING TO 1 CRLF = CHAR(13):CHAR(10) EML.HOST = "mysystem.northgate-is" * TCL = SENTENCE() EML.TO = FIELD(TCL," ",2) EML.FROM = FIELD(TCL," ",3) SMTPHOST = FIELD(TCL," ",4) PORT = 25 * IF SMTPHOST = "" THEN PRINT "MAIL <To:> <From:> <SmtpServer>" STOP END * SYS="*TCP*":SMTPHOST:";port=":PORT * * Build email header * EML.HEADER = "Subject: TEST Sockets API":CRLF:"From: ":EML.FROM EML.HEADER = EML.HEADER:CRLF:"To: ":EML.TO * PRINT "Enter message text:" INPUT EML.TEXT * CONNECT SYS TO SESS SETTING ERRNO ELSE OPN = "CONNECT" GOTO FIN END * SDAT = "HELO ":EML.HOST GOSUB POST; IF ERRNO THEN GOTO FIN IF RDAT[1,6] # "250 OK" THEN GOTO PROTOCOL.ERROR * SDAT = "MAIL FROM:":EML.FROM GOSUB POST; IF ERRNO THEN GOTO FIN IF RDAT[1,6] # "250 OK" THEN GOTO PROTOCOL.ERROR * SDAT = "RCPT TO:":EML.TO GOSUB POST; IF ERRNO THEN GOTO FIN IF RDAT[1,6] # "250 OK" THEN GOTO PROTOCOL.ERROR * SDAT = "DATA" GOSUB POST; IF ERRNO THEN GOTO FIN IF RDAT[1,3] # "354" THEN GOTO PROTOCOL.ERROR * * Build email body - header, blank line, text & a '.' on it's own line, * this, with the final CRLF added by POST, terminates the message body. * SDAT = EML.HEADER:CRLF:CRLF:EML.TEXT:CRLF:"." GOSUB POST; IF ERRNO THEN GOTO FIN IF RDAT[1,6] # "250 OK" THEN GOTO PROTOCOL.ERROR * GOTO FIN * PROTOCOL.ERROR: * PRINT "Unexpected response from SMTP server." PRINT "SDAT=":SDAT PRINT "RDAT=":RDAT * FIN: * IF ERRNO THEN IF ERRNO = EDISCONN THEN PRINT SYS:" disconnected" END ELSE PRINT OPN:" (":SYS:") failed, ERRNO=":ERRNO END END * IF NOT(UNASSIGNED(SESS)) THEN DISCONNECT SESS SETTING ERRNO ELSE PRINT "DISCONNECT (":SYS:") failed, ERRNO=":ERRNO END END * STOP * ** *** POST: ***** IF TRACING THEN PRINT "POST:":SDAT MSG = SDAT:CRLF SEND MSG TO SESS SETTING ERRNO THEN GOSUB RECV END ELSE OPN = "SEND" END RETURN * ** *** RECV: **** RDAT = "" LOOP RECEIVE MSG FROM SESS SETTING ERRNO ELSE OPN = "RECEIVE" GOTO FIN_RECV END RDAT = RDAT:MSG AGAIN = RDAT[-2,2] # CRLF WHILE AGAIN DO REPEAT * RDAT[-2,2] = "" * IF TRACING THEN PRINT "RECV:":RDAT IF RDAT[1,3] = "220" THEN * This is the greeting reply from server, is ok, go do another receive GOTO RECV END FIN_RECV: * RETURN * ** *** END ***