Progress
External Program
Interfaces


Examples Using 4GL Sockets

The following sample procedures show a simple interaction between a socket server (e-sktsv1.p) and a socket client (e-sktcl1.p). In this case, the socket server exits after handling a single connection and the socket client exits after receiving one data transmission from the server. These are event-driven examples, where the socket server only writes and the socket client only reads on their respective sockets:

e-sktsv1.p 
DEFINE VARIABLE hServerSocket AS HANDLE.
DEFINE VARIABLE aOk AS LOGICAL.
DEFINE VARIABLE mBuffer AS MEMPTR.
DEFINE VARIABLE greeting AS CHAR.

MESSAGE "We are in the socket server".
CREATE SERVER-SOCKET hServerSocket.

/* Marshal data to write */
SET-SIZE(mBuffer) = 64.
greeting = "SERVER - hello".
PUT-STRING(mBuffer,1) = greeting.

MESSAGE "Start event handling".
hServerSocket:SET-CONNECT-PROCEDURE( "connProc").
aOk = hServerSocket:ENABLE-CONNECTIONS( "-S 3333").
MESSAGE "Enabled connections:" aOk.

IF NOT aOk THEN
   RETURN.
   
/* This WAIT-FOR completes after the first connection */
WAIT-FOR CONNECT OF hServerSocket.

MESSAGE "Freeing up resources".
hServerSocket:DISABLE-CONNECTIONS().
DELETE OBJECT hServerSocket.
MESSAGE "Finished".
   
PROCEDURE connProc.
  /* Connection procedure for server socket */
  DEFINE INPUT PARAMETER hSocket AS HANDLE.  /*Socket implicitly created*/
     
  MESSAGE "We are in CONNECT event procedure, connProc".
  hSocket:WRITE (mBuffer,1,LENGTH(greeting)).
  MESSAGE hSocket:BYTES-WRITTEN "bytes written".
END. 

s-sktcl1.p 
DEFINE VARIABLE hSocket AS HANDLE.
DEFINE VARIABLE aOk AS LOGICAL.
DEFINE VARIABLE mBuffer AS MEMPTR.
DEFINE VARIABLE cString AS CHARACTER.

MESSAGE "We are in socket client".
CREATE SOCKET hSocket.

hSocket:CONNECT ("-H localhost -S 3333").
IF hSocket:CONNECTED() THEN
  MESSAGE "Connected OK".
ELSE DO:
  MESSAGE "Could not connect".
  RETURN.
END.

/* Do READ-RESPONSE event handling */
MESSAGE "Start event handling".
hSocket:SET-READ-RESPONSE-PROCEDURE( "readProc").

/* This WAIT-FOR completes after the first data reception */
WAIT-FOR READ-RESPONSE OF hSocket.
  
MESSAGE "Freeing up resources".
hSocket:DISCONNECT().
DELETE OBJECT hSocket.
MESSAGE "Finished".

PROCEDURE readProc.
  /* Read procedure for socket */
  DEFINE VARIABLE mBuffer AS MEMPTR.
     
  MESSAGE "We are in READ-RESPONSE event procedure, readProc".
  SET-SIZE(mBuffer) = 64.
  SELF:READ (mBuffer,1,SELF:GET-BYTES-AVAILABLE()).
  MESSAGE SELF:BYTES-READ "bytes read".
  cString = GET-STRING(mBuffer,1).  /*Unmarshal data*/
  DISPLAY cString FORMAT "x(64)".
END PROCEDURE. 

The following sample procedures show how you can transfer a database record between a socket server (e-sktsv2.p) and a socket client (e-sktcl2.p) using a raw transfer. It uses the sports database as the source (server) and a copy of the sports database as the target (client).

This is a simple example in which the server waits for connections indefinitely (until you press the STOP key), but always sends the same database record to the client for each CONNECT event. Note how the server uses the RAW transfer to first copy the record from the database, then to specify the size of the MEMPTR memory from which the record is written on the socket. The server stores the size of the record as the first piece of information sent to the client, followed by the record:

e-sktsv2.p
/* Sends a new customer record from the sports  database through a socket. */
DEFINE VARIABLE hServerSocket AS HANDLE NO-UNDO.
DEFINE VARIABLE aOk AS LOGICAL NO-UNDO.
DEFINE VARIABLE mBuffer AS MEMPTR NO-UNDO.
DEFINE VARIABLE r1 AS RAW NO-UNDO.
DEFINE VARIABLE len AS INTEGER NO-UNDO.
DEFINE VARIABLE custNum AS INTEGER NO-UNDO.

MESSAGE "We are in the raw transfer socket server".
   
/* Create a new customer record with a unique Cust-num */
FIND LAST customer.
custNum = customer.Cust-num + 1.
CREATE customer.
customer.Cust-num = custNum.
customer.Name = "Jack Sprat".
customer.Address = "222 Ferdinand St".
customer.City = "Woburn".
customer.State = "MA".
customer.Postal-Code = "01801".

CREATE SERVER-SOCKET hServerSocket.
hServerSocket:SET-CONNECT-PROCEDURE("connProc", THIS-PROCEDURE).

/* Put customer record into a RAW variable and store in local DB. */
RAW-TRANSFER customer TO r1.
RELEASE customer. 
/* Put the length of the record followed by a copy of the full 
   record into a MEMPTR to send it through the socket. 
*/
len = LENGTH(r1).
SET-BYTE-ORDER(mBuffer) = LITTLE-ENDIAN.
SET-SIZE(mBuffer) = len + 4.
PUT-LONG(mBuffer, 1) = len.
PUT-BYTES(mBuffer, 5) = r1.

aOk = hServerSocket:ENABLE-CONNECTIONS( "-S 3334").
MESSAGE "Enabled connections:" aOk.

DO ON STOP UNDO, LEAVE:
  WAIT-FOR CLOSE OF THIS-PROCEDURE.
end.

PROCEDURE connProc.
  /* Connection procedure for server socket */
  DEFINE INPUT PARAMETER hSocket AS HANDLE.  /*Socket implicitly created*/
  MESSAGE "We are in the CONNECT event procedure, connProc".

  /* Send the size followed by the record that we put into the MEMPTR */
  hSocket:WRITE (mBuffer,1, GET-SIZE(mBuffer)).
  MESSAGE "Sent" hSocket:BYTES-WRITTEN "bytes containing customer record".
END. 

In this example, the client (e-sktcl2.p) polls its socket procedurally until the data for the record is available to read. In this case, the client first waits for the size information, then waits for that number of bytes of customer data. It also uses this size information to set the size of the MEMPTR region for reading the record off the socket. Finally, note that the client deletes the socket object and frees MEMPTR memory after it disconnects from the server:

e-sktcl2.p 
/* Receives a new customer record through a socket and puts it into the 
   sports DB.
*/   
DEFINE VARIABLE hSocket AS HANDLE NO-UNDO.
DEFINE VARIABLE mBuffer AS MEMPTR NO-UNDO.
DEFINE VARIABLE r1 AS RAW NO-UNDO.
DEFINE VARIABLE recLen AS INTEGER NO-UNDO.

MESSAGE "We are in the raw transfer socket client".
   
CREATE SOCKET hSocket.
hSocket:CONNECT ("-H localhost -S 3334").
IF hSocket:CONNECTED() THEN
  MESSAGE "Connected OK".
ELSE DO:
  MESSAGE "Could not connect".
  RETURN.
END.

/* Do a blocking READ until we get the count of how long the 
   record is to read.  
*/
SET-SIZE(mBuffer) = 4.
SET-BYTE-ORDER(mBuffer) = LITTLE-ENDIAN.   
hSocket:READ (mBuffer, 1, 4).
recLen = GET-LONG(mBuffer, 1).

/* READ again to get the full record. */
SET-SIZE(mBuffer) = 0.
SET-SIZE(mBuffer) = recLen.
hSocket:READ (mBuffer, 1, recLen).
MESSAGE hSocket:BYTES-READ "bytes read for customer record".

/* Copy the record to a RAW variable so we can put it into the DB */
r1 = mBuffer.
RAW-TRANSFER r1 TO customer.

MESSAGE "Customer is:" Cust-num Name Address City State Postal-Code.
RELEASE customer.
       
hSocket:DISCONNECT().
DELETE OBJECT hSocket.
SET-SIZE(mBuffer) = 0. 

The following example also involves a client and server.

The server, e-sktsv3.p, demonstrates how to set qsize, the length of the pending-connection queue, while enabling the server-socket for connections.

e-sktsv3.p
DEFINE VARIABLE sserver AS HANDLE. 
DEFINE VARIABLE ok AS LOGICAL. 
DEFINE VARIABLE m-string AS MEMPTR.  
CREATE SERVER-SOCKET sserver. 
ok = sserver:SET-CONNECT-PROCEDURE( "connProc"). 
MESSAGE "set connection procedure" ok. 
/* Enable for connections and set the qsize */ 
ok = sserver:ENABLE-CONNECTIONS( "-S 3000 -qsize 5"). 
MESSAGE "enable connections" OK. 
WAIT-FOR CONNECT OF sserver. 
PROCEDURE connProc. 
/* connection procedure for server socket */ 
DEFINE INPUT PARAMETER hsocket AS HANDLE.  /*implicitly created*/ 
MESSAGE "Inside connProc".  
SET-SIZE(m-string) = 64. 
PUT-STRING(m-string,1) = "SERVER - hello". 
ok = hsocket:WRITE (m-string,1,26). 
END. 

The client, e-sktcl3.p (1 of 2), shows how to set and retrieve socket options.

e-sktcl3.p
DEFINE VARIABLE hsocket AS HANDLE. 
DEFINE VARIABLE ok AS LOGICAL. 
DEFINE VARIABLE ret AS CHARACTER. 
DEFINE VARIABLE m-string AS MEMPTR. 
DEFINE VARIABLE c AS CHARACTER. 
CREATE SOCKET hsocket. 
hsocket:CONNECT ("-H localhost -S 3000 "). 
IF hsocket:CONNECTED() THEN 
    MESSAGE "Connected OK". 
ELSE DO: 
    MESSAGE "Could not connect".   
    RETURN. 
END. 
/* connect succeeded */ 
OK = hsocket:SET-SOCKET-OPTION("TCP-NODELAY", "TRUE"). 
ret = hsocket:GET-SOCKET-OPTION("TCP-NODELAY"). 
MESSAGE "TCP-NODELAY = " ret. 
OK = hsocket:SET-SOCKET-OPTION("SO-LINGER", "TRUE, 55"). 
ret = hsocket:GET-SOCKET-OPTION("SO-LINGER"). 
MESSAGE "SO-LINGER = " ret. 
OK = hsocket:SET-SOCKET-OPTION("SO-KEEPALIVE", "TRUE").  
ret = hsocket:GET-SOCKET-OPTION("SO-KEEPALIVE"). 
MESSAGE "SO-KEEPALIVE = " ret. 
OK = hsocket:SET-SOCKET-OPTION("SO-REUSEADDR", "TRUE"). 
ret = hsocket:GET-SOCKET-OPTION("SO-REUSEADDR"). 
MESSAGE "SO-REUSEADDR = " ret. 
OK = hsocket:SET-SOCKET-OPTION("SO-SNDBUF", "40"). 
ret = hsocket:GET-SOCKET-OPTION("SO-SNDBUF"). 
MESSAGE "SO-SNDBUF = " ret. 
OK = hsocket:SET-SOCKET-OPTION("SO-RCVBUF", "5000"). 
ret = hsocket:GET-SOCKET-OPTION("SO-RCVBUF"). 
MESSAGE "SO-RCVBUF = " ret. 
OK = hsocket:SET-SOCKET-OPTION("SO-RCVTIMEO", "60"). 
ret = hsocket:GET-SOCKET-OPTION("SO-RCVTIMEO"). 
MESSAGE "SO-RCVTIMEO = " ret. 
WAIT-FOR READ-RESPONSE OF hsocket. 
SET-SIZE(m-string) = 64. 
ok = hsocket:READ (m-string,1,26). 
c = GET-STRING(m-string,1). 
MESSAGE "the string = " c. 
hsocket:DISCONNECT(). 
DELETE OBJECT hsocket. 


Copyright © 2004 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095