Alaska Software Inc. - SocketGetMessage/SocketPutMessage
Username: Password:
AuthorTopic: SocketGetMessage/SocketPutMessage
Jose Adriano Baltieri SocketGetMessage/SocketPutMessage
on Thu, 12 Jul 2007 16:05:53 -0300
Hello !

I'm trying to implement a client/server architecture using SocketGetMessage 
and SocketPutMessage.

I already have other client/server programs, written in XPP , that are working 
OK.

However, they use send/receive only.

I really would like to use Get/Put Message.

So, what's the point ?

The problem is that SocketGetMessage, which is typically used on server side 
(I guess) has no timeout parameter.

I'd like it to block UNTIL a message arrives or some time has elapsed.

Also, WHILE it waits, it MUST SLEEP, that is, it must not consume CPU.

More than that, it SHOULD talk underneath with Garbage Collector.

Any ideas ?
Phil Ide
Re: SocketGetMessage/SocketPutMessage
on Mon, 23 Jul 2007 14:20:12 +0100
Adriano,

> I'm trying to implement a client/server architecture using SocketGetMessage 
> and SocketPutMessage.
> 
> I already have other client/server programs, written in XPP , that are working 
> OK.
> 
> However, they use send/receive only.
> 
> I really would like to use Get/Put Message.
> 
> So, what's the point ?

These functions are convenience functions.  Basically, it is a wrapper for
what you have already done in previous apps with SocketSend/SocketRecv.

SocketGetMessage should go to an idle state until data arrives on the
socket.

If you want to maintain a timeout, then you need to either set the socket
with a timeout period, or manually monitor the period yourself.

Regards,

Phil Ide

---------------------
www.xbhcl.com
www.pbih.eu
www.idep.org.uk/xbase
---------------------
This tagline not sponsored by Pepsi in any way.
Jose Adriano Baltieri Re: SocketGetMessage/SocketPutMessage
on Tue, 24 Jul 2007 08:17:37 -0300
Phil Ide wrote:
> Adriano,
> 
>> I'm trying to implement a client/server architecture using SocketGetMessage 
>> and SocketPutMessage.
>>
>> I already have other client/server programs, written in XPP , that are working 
>> OK.
>>
>> However, they use send/receive only.
>>
>> I really would like to use Get/Put Message.
>>
>> So, what's the point ?
> 
> These functions are convenience functions.  Basically, it is a wrapper for
> what you have already done in previous apps with SocketSend/SocketRecv.
> 
> SocketGetMessage should go to an idle state until data arrives on the
> socket.
> 
> If you want to maintain a timeout, then you need to either set the socket
> with a timeout period, or manually monitor the period yourself.
> 
OK. But I gave up doing it like that.

Since I had already a set of functions to work upon the FASTCGI protocol, I 
used these functions to this other particular implementation.

FASTCGI protocol works basically by exchanging records with this layout :

First byte : Record Type
Next two bytes : Request Id (kind of a sequence # of the record within the dialog)
Two bytes : record length
Next "N" bytes : record data itself

So, I put my data on those kind of records.

My implementation of the protocol uses SocketSelect with a timeout period.

So, I guess it is pretty much efficient (I guess).

Just in case anyone else is curious about this implementation or would like to 
use it, I inserted my set of functions below :

Thank you so much Phil for your attention !

Follows my implementation :

# DEFINE ERRO_TCPIP(TCPIPERROR,SOCKETNO) "ERRO NA COMUNICACAO TCP/IP : " + 
STRTRIM(TCPIPERROR) + " " + STRTRIM(SOCKETNO) + " (DESCRICAO NO ASINET.CH)"

# DEFINE REC_TYPE                1
# DEFINE REC_REQ_ID              2
# DEFINE REC_DATA                3
# DEFINE REC_PADDING             4

FUNCTION SEND(LNSOCKET,LNTYPE,LCREQID,LCMENS)

LOCAL LNERROR, ;
       LNCHARS, ;
       LNSTARTSECS:=CSECONDS(), ;
       LCRECORD, ;
       LNLEN:=LEN(LCMENS), ;
       LNMIN, ;
       LCLEN

IF  LNLEN == 0
     ABORT : "TAMANHO DA MENSAGEM NAO PODE SER ZERO"
END IF

WHILE LNLEN > 0

   LCRECORD:=LEFT(LCMENS,LNMIN:=MIN(LNLEN,FCGI_MAX_LENGTH-100))
   LCLEN:=W2BIN(LNMIN)

   LCRECORD:=CHR(1) + CHR(LNTYPE) + LCREQID + LCLEN[2] + LCLEN[1] + CHR(0) + 
CHR(0) + LCRECORD

   WHILE TRUE
     LNCHARS:=SOCKETSEND(LNSOCKET,LCRECORD,LEN(LCRECORD),MSG_NORMAL,@LNERROR)
     IF  (CSECONDS() - LNSTARTSECS) >= 180
         EXIT
     END IF
     IF  LNERROR == 0
         IF  NOT LNCHARS == LEN(LCRECORD)
             ABORT : "CARACTERES NAO BATEM AO EFETUAR SOCKET SEND" + CRLF + ;
                     STRTRIM(LNCHARS)                              + CRLF + ;
                     STRTRIM(LEN(LCRECORD))
         END IF
         EXIT
     END IF
     IF  LNERROR == WSAEWOULDBLOCK
         LOOP
     END IF
     IF  LNERROR == WSAECONNABORTED ;
     OR  LNERROR == WSAECONNRESET
         EXIT
     ELSE
         RETURN LNERROR
     END IF
   END WHILE

   LCMENS:=SUBSTR(LCMENS,LNMIN+1)
   LNLEN:=LEN(LCMENS)

END WHILE

#  IFDEF LOG_SERVER
          LOGRECORDS("E",LCRECORD,LNSOCKET)
#  ENDIF

RETURN 0


FUNCTION RECEIVE(LNSOCKET,LNSD,LNTIMEOUT)

LOCAL LCMENS:=SPACE(FCGI_MAX_LENGTH), ;
       LNRECVCHARS, ;
       LNSTART:=CSECONDS(), ;
       LNMSGLEN, ;
       LNERROR:=0, ;
       LNVERSION, ;
       LNTYPE, ;
       LCREQID, ;
       LNCLEN, ;
       LNPLEN, ;
       LCDATA, ;
       LCPADD, ;
       LCBLOCK, ;
       LNBLEN, ;
       LCHEADER, ;
       LCSAIDA, ;
       LLTIMEOUT, ;
       LNCOUNT

IF  NOT SOCKETFD_ISSET(LNSOCKET,LNSD)
     IF  NOT SOCKETFD_ZERO(LNSD,@LNERROR)
         ABORT : ERRO_TCPIP(LNERROR,LNSOCKET)
     END IF
     IF  NOT SOCKETFD_SET(LNSOCKET,LNSD)
         ABORT : ERRO_TCPIP(LNERROR,LNSOCKET)
     END IF
END IF

IF  SOCKETSELECT(LNSD,,,{LNTIMEOUT,0},@LNERROR) == 0
     LCSAIDA:="TIMEOUT NO RECEIVE 0 " + STRTRIM(LNTIMEOUT) + " " + 
STRTRIM(CSECONDS() - LNSTART) + CRLF
     RETURN {-1,LCSAIDA}
END IF

LNSTART:=CSECONDS()
LCHEADER:=""
LNRECVCHARS:=0
LLTIMEOUT:=FALSE
LNCOUNT:=0
REPEAT
   LCBLOCK:=SPACE(FCGI_MAX_LENGTH)
   LNRECVCHARS+=(LNBLEN:=SOCKETRECV(LNSOCKET,@LCBLOCK,FCGI_HEADER_LEN - 
LNRECVCHARS,MSG_NORMAL,@LNERROR))
   IF  LNERROR == WSAEWOULDBLOCK
       IF  ++LNCOUNT >= 100
           SLEEP(1)
       END IF
       LOOP
   END IF
   IF  LNERROR <> 0
       ABORT : ERRO_TCPIP(LNERROR,LNSOCKET)
   END IF
   LCHEADER+=LEFT(LCBLOCK,LNBLEN)
UNTIL LNRECVCHARS >= FCGI_HEADER_LEN OR (LLTIMEOUT:=(CSECONDS() - LNSTART) >= 
LNTIMEOUT)

IF  LLTIMEOUT
     LCSAIDA:="TIMEOUT NO RECEIVE 1 " + STRTRIM(LNTIMEOUT) + " " + 
STRTRIM(CSECONDS() - LNSTART) + CRLF + ;
              "LNRECVCHARS   :      " + ANYTOCED(LNRECVCHARS)   + CRLF + ;
              "LCHEADER      :      " + "'" + LCHEADER + "'"    + CRLF + ;
              "LEN(LCHEADER) :      " + ANYTOCED(LEN(LCHEADER)) + CRLF + ;
              "CHRS(LCHEADER):      " + CHRS(LCHEADER) + CRLF
     RETURN {-1,LCSAIDA}
END IF

IF  LNRECVCHARS > FCGI_HEADER_LEN ;
OR  (LEN(LCHEADER) <> FCGI_HEADER_LEN)
     ABORT : "TAMANHO DO HEADER NAO CONFERE : " + LCHEADER   + CRLF + ;
             "TAMANHO RECEBIDO  : " + STRTRIM(LNRECVCHARS)   + CRLF + ;
             "TAMANHO LCHEADER  : " + STRTRIM(LEN(LCHEADER)) + CRLF + ;
             "LCHEADER          : " +             LCHEADER   + CRLF
END IF

IF  NOT (LNVERSION:=ASC(LCHEADER[1])) == FCGI_VERSION_1
     ABORT : "VERSAO INVALIDA : " + STRTRIM(LNVERSION)
END IF

LNTYPE:=ASC(LCHEADER[2])

IF  LNTYPE == FCGI_BEGIN_REQUEST           ;
OR  LNTYPE == FCGI_ABORT_REQUEST           ;
OR  LNTYPE == FCGI_END_REQUEST             ;
OR  LNTYPE == FCGI_PARAMS                  ;
OR  LNTYPE == FCGI_STDIN                   ;
OR  LNTYPE == FCGI_STDOUT                  ;
OR  LNTYPE == FCGI_STDERR                  ;
OR  LNTYPE == FCGI_DATA                    ;
OR  LNTYPE == FCGI_GET_VALUES              ;
OR  LNTYPE == FCGI_GET_VALUES_RESULT       ;
OR  LNTYPE == FCGI_PR_ARE_YOU_READY        ;
OR  LNTYPE == FCGI_PR_YES_IM_READY         ;
OR  LNTYPE == FCGI_PR_BEGIN_REQUEST_DATA   ;
OR  LNTYPE == FCGI_PR_REQUEST_DATA         ;
OR  LNTYPE == FCGI_PR_END_OF_REQUEST_DATA  ;
OR  LNTYPE == FCGI_PR_BEGIN_RESPONSE_DATA  ;
OR  LNTYPE == FCGI_PR_RESPONSE_DATA        ;
OR  LNTYPE == FCGI_PR_END_OF_RESPONSE_DATA
      OK
ELSE
     ABORT : "TIPO INVALIDO : " + STRTRIM(LNTYPE)
END IF

LCREQID:=SUBSTR(LCHEADER,3,2)

IF  (LNCLEN:=BIN2W(LCHEADER[6] + LCHEADER[5])) > FCGI_MAX_LENGTH
     ABORT : "TAMANHO INVALIDO : " + STRTRIM(LNCLEN)
END IF

IF  (LNPLEN:=ASC(LCHEADER[7])) > 255
     ABORT : "TAMANHO INVALIDO : " + STRTRIM(LNPLEN)
END IF

LNMSGLEN:=LNCLEN + LNPLEN

IF  LNMSGLEN > 0
     LNSTART:=CSECONDS()
     LCMENS:=""
     LNRECVCHARS:=0
     LLTIMEOUT:=FALSE
     LNCOUNT:=0
     REPEAT
       LCBLOCK:=SPACE(FCGI_MAX_LENGTH)
       LNRECVCHARS+=(LNBLEN:=SOCKETRECV(LNSOCKET,@LCBLOCK,LNMSGLEN - 
LNRECVCHARS,MSG_NORMAL,@LNERROR))
       IF  LNERROR == WSAEWOULDBLOCK
           IF  ++LNCOUNT >= 100
               SLEEP(1)
           END IF
           LOOP
       END IF
       IF  LNERROR <> 0
           ABORT : ERRO_TCPIP(LNERROR,LNSOCKET)
       END IF
       LCMENS+=LEFT(LCBLOCK,LNBLEN)
     UNTIL LNRECVCHARS >= LNMSGLEN OR (LLTIMEOUT:=(CSECONDS() - LNSTART) >= 300)
     IF  LLTIMEOUT
         LCSAIDA:="TIMEOUT NO RECEIVE 2 " + STRTRIM(LNTIMEOUT) + " " + 
STRTRIM(CSECONDS() - LNSTART) + CRLF + ;
                  "LNRECVCHARS   :      " + ANYTOCED(LNRECVCHARS)   + CRLF + ;
                  "LCHEADER      :      " + "'" + LCHEADER + "'"    + CRLF + ;
                  "LEN(LCHEADER) :      " + ANYTOCED(LEN(LCHEADER)) + CRLF + ;
                  "CHRS(LCHEADER):      " + CHRS(LCHEADER) + CRLF
         RETURN {-1,LCSAIDA}
     END IF
     IF  (LNRECVCHARS <> LNMSGLEN)
         ABORT : "TAMANHO DA MENSAGEM NAO CONFERE : " + LCMENS + CRLF + ;
                 "TAMANHO DECLARADO : " + STRTRIM(LNMSGLEN)    + CRLF + ;
                 "TAMANHO RECEBIDO  : " + STRTRIM(LNRECVCHARS) + CRLF + ;
                 "TAMANHO LCMENS    : " + STRTRIM(LEN(LCMENS)) + CRLF + ;
                 "LCMENS            : " +             LCMENS   + CRLF
     END IF
ELSE
     LCMENS:=""
END IF

#  IFDEF LOG_SERVER
          LOGRECORDS("R",LCMENS,LNSOCKET)
#  ENDIF

LCDATA:=LCMENS
LCPADD:=RIGHT(LCMENS,LNPLEN)

RETURN {LNTYPE,LCREQID,LCDATA,LCPADD}


FUNCTION CHRS(LCMENS)

LOCAL LCCHAR, ;
       LNCHAR, ;
       LNI, ;
       LNLEN:=LEN(LCMENS), ;
       LCSAIDA:=""

FOR LNI:= 1 TO LNLEN
   LCCHAR:=LCMENS[LNI]
   IF  (LNCHAR:=ASC(LCCHAR)) > 31
       LCSAIDA+=LCCHAR
   ELSE
       LCSAIDA+="<" + {"NUL"                        ;
                      ,"SOH Start of Header"        ;
                      ,"STX Start of Text"          ;
                      ,"ETX End of Text"            ;
                      ,"EOT End of Transmission"    ;
                      ,"ENQ Enquiry"                ;
                      ,"ACK Acknowledge"            ;
                      ,"BEL Bell"                   ;
                      ,"BS  BackSpace"              ;
                      ,"HT  Horizontal Tab"         ;
                      ,"LF  Line Feed"              ;
                      ,"VT  Vertical Tab"           ;
                      ,"FF  Form Feed"              ;
                      ,"CR  Carriage Return"        ;
                      ,"SO  Shift Out"              ;
                      ,"SI  Shift In"               ;
                      ,"DLE Data Link Escape"       ;
                      ,"DC1 Device Control 1"       ;
                      ,"DC2 Device Control 2"       ;
                      ,"DC3 Device Control 3"       ;
                      ,"DC4 Device Control 4"       ;
                      ,"NAK Negative Acknowledge"   ;
                      ,"SYN Synchronous Idle"       ;
                      ,"ETB End Transmission Block" ;
                      ,"CAN Cancel"                 ;
                      ,"EM  End of Medium"          ;
                      ,"SUB Substitute"             ;
                      ,"ESC Escape"                 ;
                      ,"FS  File Separator"         ;
                      ,"GS  Group Separator"        ;
                      ,"RS  Record Separator"       ;
                      ,"US  Unit Separator"         ;
                      }[LNCHAR+1] + ">"
   END IF
NEXT

RETURN LCSAIDA	
> Regards,