Try OpenEdge Now
skip to main content
Programming Interfaces
External Program Interfaces : Sockets : Examples using ABL sockets
 

Examples using ABL sockets

The following sample procedures show a simple interaction between a socket server (i-sktsv1.p) and a socket client (i-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.
i-sktsv1.p
DEFINE VARIABLE aOk           AS LOGICAL   NO-UNDO.
DEFINE VARIABLE cString       AS CHARACTER NO-UNDO.
DEFINE VARIABLE greeting      AS CHARACTER NO-UNDO.
DEFINE VARIABLE hServerSocket AS HANDLE    NO-UNDO.
DEFINE VARIABLE hSocket       AS HANDLE    NO-UNDO.
DEFINE VARIABLE mBuffer       AS MEMPTR    NO-UNDO.

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

/* Marshal data to write */
ASSIGN
  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*/

IF VALID-HANDLE(hSocket) THEN DO: /* standard validation */
MESSAGE "We are in CONNECT event procedure, connProc".
hSocket:WRITE(mBuffer,1,LENGTH(greeting)).
MESSAGE hSocket:BYTES-WRITTEN "bytes written".
hSocket:DISCONNECT().
DELETE OBJECT hSocket.
END.

ELSE MESSAGE "Unable to obtain socket.".
END PROCEDURE.

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 NO-UNDO.

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.
i-sktcl1.p
DEFINE VARIABLE aOk     AS LOGICAL   NO-UNDO.
DEFINE VARIABLE cString AS CHARACTER NO-UNDO.
DEFINE VARIABLE hSocket AS HANDLE    NO-UNDO.
DEFINE VARIABLE mBuffer AS MEMPTR    NO-UNDO.

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 NO-UNDO.

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 (i-sktsv2.p) and a socket client (i-sktcl2.p) using a raw transfer. It uses the sports2000 database as the source (server) and a copy of the sports2000 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.
i-sktsv2.p
/* Sends new customer record from the sports2000 database through a socket. */
DEFINE VARIABLE aOk           AS LOGICAL NO-UNDO.
DEFINE VARIABLE custNum       AS INTEGER NO-UNDO.
DEFINE VARIABLE hServerSocket AS HANDLE  NO-UNDO.
DEFINE VARIABLE len           AS INTEGER NO-UNDO.
DEFINE VARIABLE mBuffer       AS MEMPTR  NO-UNDO.
DEFINE VARIABLE r1            AS RAW     NO-UNDO.

MESSAGE "We are in the raw transfer socket server".

/* Create a new customer record with a unique custnum */
FIND LAST Customer NO-LOCK.
custNum = Customer.CustNum + 1.

CREATE Customer.
ASSIGN
  Customer.CustNum    = custNum
  Customer.Name       = "Jack Sprat"
  Customer.Address    = "222 Ferdinand St"
  Customer.City       = "Woburn"
  Customer.State      = "MA"
  Customer.PostalCode = "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. */
ASSIGN
  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.

DELETE OBJECT hServerSocket.

PROCEDURE connProc:
/* Connection procedure for server socket */
DEFINE INPUT PARAMETER hSocket AS HANDLE. /*Socket implicitly created*/
IF VALID-HANDLE(hSocket) THEN DO: /* standard validation */
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".
hSocket:DISCONNECT().
DELETE OBJECT hSocket.
END.
ELSE MESSAGE "Unable to obtain socket.".
END PROCEDURE.
In this example, the client (i-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.
i-sktcl2.p
/* Receives a new customer record through a socket and puts it into the
sports2000 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:" Customer.CustNum Customer.Name Customer.Address
  Customer.City Customer.State Customer.PostalCode.
RELEASE Customer.

hSocket:DISCONNECT().
DELETE OBJECT hSocket.
SET-SIZE(mBuffer) = 0.
The next example also involves a client and server. The server, i-sktsv3.p, demonstrates how to set qsize, the length of the pending-connection queue, while enabling the server-socket for connections.
i-sktsv3.p
DEFINE VARIABLE m-string AS MEMPTR  NO-UNDO.
DEFINE VARIABLE ok       AS LOGICAL NO-UNDO.
DEFINE VARIABLE sserver  AS HANDLE  NO-UNDO.

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 */

IF VALID-HANDLE(hSocket) THEN DO: /* standard validation */
MESSAGE "Inside connProc".
SET-SIZE(m-string)     = 64.
PUT-STRING(m-string,1) = "SERVER - hello".
ok = hsocket:WRITE (m-string,1,26).
hSocket:DISCONNECT().
DELETE OBJECT hSocket.
END.
ELSE MESSAGE "Unable to obtain socket.".
END.
The client, i-sktcl3.p, shows how to set and retrieve socket options.
i-sktcl3.p
DEFINE VARIABLE hsocket  AS HANDLE    NO-UNDO.
DEFINE VARIABLE ok       AS LOGICAL   NO-UNDO.
DEFINE VARIABLE ret      AS CHARACTER NO-UNDO.
DEFINE VARIABLE m-string AS MEMPTR    NO-UNDO.
DEFINE VARIABLE c        AS CHARACTER NO-UNDO.

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.