Author | Topic: 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, |