gfp - GLBasic File Protocol (very simple file transfers)

Previous topic - Next topic

Kitty Hello

Hi,

for my GACK project I needed a way to get very easy file transfers with a remote computer (Touchpad) that is somewhere in the LAN.
The great stuff about this functions is, that you just use them as if you were on the local drive. It's like:

GetFileList("Some Directory", "MyAppName", files$[])
CopyFile("remote path", "local path")

No need to worry about ip addresses or other stuff.

Here's the library:
Code (glbasic) Select

// --------------------------------- //
// Project: glbFileProtocol
// Start: Saturday, November 26, 2011
// IDE Version: 10.179


GLOBAL g_gfpDefaultPort% = 12311

// ----------------------------------------------------
// find the remote PC (or send a broadcast to be found)
// ----------------------------------------------------
FUNCTION gfp_GetRemoteIP$: app$, port% = 0, bBroadcast% = FALSE
LOCAL sock%
STATIC bctime% = 0

IF bBroadcast%
LOCAL t = GETTIMERALL()
IF GETTIMERALL() < bctime% THEN RETURN
bctime% = t + 500
ENDIF

IF port<=0 THEN port = g_gfpDefaultPort%
app$ = REPLACE$(app$, "#", ":")

// find my own IP
LOCAL ip$ = SOCK_GETIP$(SOCK_GETIP(""))
LOCAL ip_broadcast% = SOCK_GETIP("255.255.255.255")


IF bBroadcast%
// STDOUT "broadcasting my ip: "+ip$+"\n"
sock% = SOCK_UDPOPEN(port%)
LOCAL rv% = SOCK_UDPSEND(sock%, app$+"#"+ip$, ip_broadcast%, port%)
IF rv% = -1
STDOUT "SOCK_UDPSEND -1\n"
STDOUT NETGETLASTERROR$()+"\n"
RETURN ""
ENDIF
SOCK_CLOSE(sock%)
RETURN ""
ELSE // find the other end
sock% = SOCK_UDPOPEN(port%)
LOCAL rv%, msg$

// wait for a response
FOR reread% = 1 TO 10
rv% = SOCK_RECV(sock%, msg$, 512)
IF rv%>0
msg$ = MID$(msg$, INSTR(msg$, "#")+1)
IF msg$ <> ip$
STDOUT "other PC at: "+msg$+"\n"

SOCK_CLOSE(sock%)
RETURN msg$ // return the remote IP
ENDIF
ENDIF

IF rv% = -1
STDOUT "SOCK_RECV -1\n"
GOTO exitout
ENDIF

IF rv% = -2 // would block, try again later
STDOUT ".\n"
SLEEP 100
ENDIF
NEXT
ENDIF

IF NOT bBroadcast% THEN STDOUT "timed out\n"
@exitout:

SOCK_CLOSE(sock%)
RETURN ""
ENDFUNCTION


// ----------------------------------------------------
// connect to server in LAN - will try to find it.
// port - port to use (will add 1 for TCP, and use port% for udp server find)
// returns socket
// ----------------------------------------------------
FUNCTION gfp_Connect%: app$, port%
STATIC gfpLastIp$ // last successive connect


IF port<=0 THEN port = g_gfpDefaultPort%

LOCAL sock% = -1
IF LEN(gfpLastIp$)
sock% = SOCK_TCPCONNECT(gfpLastIp$, port% + 1)
ENDIF

// quick reconnect worked
IF sock%>=0 THEN RETURN sock%

gfpLastIp$ = ""
FOR retry% = 0 TO 10
gfpLastIp$ = gfp_GetRemoteIP$(app$, port%, FALSE) // find
IF LEN(gfpLastIp$) THEN BREAK
NEXT
IF NOT LEN(gfpLastIp$) THEN RETURN -1


sock% = SOCK_TCPCONNECT(gfpLastIp$, port% + 1)
// connect did not work
IF sock%<0
STDOUT "connect failed\n"
gfpLastIp$ = ""
ENDIF

RETURN sock%
ENDFUNCTION




// ----------------------------------------------------
// Get list of files in a remote directory ( from server )
// ----------------------------------------------------
FUNCTION gfp_GetFileList%: dir_remote$, files$[], app$, port%=0
REDIM files$[0]

LOCAL sock% = gfp_Connect(app$, port%)
IF sock% < 0 THEN RETURN FALSE

STDOUT "conencted OK\n"

LOCAL buf$, msg$
IF SOCK_TCPSEND(sock%, "LSFL"+dir_remote$) > 0
WHILE TRUE
buf$=""
LOCAL rv% = SOCK_RECV(sock%, buf$, 1024*100)
IF rv% = 0 THEN BREAK
IF rv%>0 THEN INC msg$, buf$
WEND
ENDIF
SOCK_CLOSE( sock% )

msg$ = MID$(msg$, 4) // LSFL
IF LEN(msg$)
SPLITSTR(msg$, files$[], "|")
RETURN TRUE
ENDIF

RETURN FALSE
ENDFUNCTION

// ----------------------------------------------------
// Get a file from the server
// ----------------------------------------------------
FUNCTION gfp_GetFile%: file_remote$, file_local$, app$, port%=0
LOCAL sock% = gfp_Connect(app$, port%)
IF sock% < 0 THEN RETURN FALSE

LOCAL buf$, msg$
IF SOCK_TCPSEND(sock%, "FILE"+file_remote$) > 0
WHILE TRUE
buf$=""
LOCAL rv% = SOCK_RECV(sock%, buf$, 1024*100)
STDOUT "Get: "+rv%+"\n"
IF rv%>0
INC msg$, buf$
CONTINUE
ENDIF

IF rv% = -2
SLEEP 5
CONTINUE
ENDIF
BREAK
WEND
ENDIF
SOCK_CLOSE( sock% )

msg$ = MID$(msg$, 4) // FILE
IF LEN(msg$)
LOCAL fi% = GENFILE()
IF OPENFILE(fi%, file_local$, 0)
WRITESTR fi%, msg$
CLOSEFILE fi%
RETURN TRUE
ENDIF

STDOUT "Can't write file data to :"+file_local$+"\n"
ENDIF

RETURN FALSE
ENDFUNCTION



// ----------------------------------------------------
// Get a file from the server
// ----------------------------------------------------
FUNCTION gfp_PutFile%: file_local$, file_remote$, app$, port%=0
LOCAL sock% = gfp_Connect(app$, port%)
IF sock% < 0 THEN RETURN FALSE


LOCAL filedata$
LOCAL n% = GETFILESIZE(file_local$)
LOCAL fi% = GENFILE()
IF OPENFILE(fi%, file_local$, 1)
READSTR fi%, filedata$, n%
CLOSEFILE fi%
ELSE
STDOUT "can't read "+file_local$+"\n"
RETURN FALSE
ENDIF


LOCAL buf$, msg$
LOCAL rv%
WHILE TRUE
IF LEN(filedata$)=0 THEN BREAK
STDOUT "put "+LEN(filedata$)+"\n"
rv% = SOCK_TCPSEND(sock%, "PILE"+file_remote$ + "\n" + filedata$)

IF rv% = 0 THEN BREAK
IF rv%>0
filedata$ = MID$(filedata$, rv%)
CONTINUE
ENDIF

IF rv% = -2
SLEEP 5
CONTINUE
ENDIF

WEND
SOCK_CLOSE( sock% )

IF rv% > 0 THEN RETURN TRUE

RETURN FALSE
ENDFUNCTION



FUNCTION gfp_ReadToTheEnd%: sock%, BYREF msg$

LOCAL more$, rv%
WHILE TRUE
rv% = SOCK_RECV(sock%, more$, 1024*100)
STDOUT "recv: "+rv%+"\n"
IF rv>0
INC msg$, more$
CONTINUE
ENDIF

IF rv = -2
SLEEP 5
CONTINUE
ENDIF
IF rv% = 0
RETURN LEN(msg$)
ENDIF

BREAK
WEND
RETURN rv
ENDFUNCTION



PROTOTYPE gfp_QuitOption:

// ----------------------------------------------------
// Start/run the fileserver
// ----------------------------------------------------
FUNCTION gfp_FileServer%: app$, port%, qfoo AS gfp_QuitOption
LOCAL ip$
LOCAL sock_remote%[]
LOCAL msg$
LOCAL sock_listen%
IF port<=0 THEN port = g_gfpDefaultPort%

sock_listen% = SOCK_TCPLISTEN(port%+1)

// wait for orders
WHILE TRUE
gfp_GetRemoteIP$(app$, port%, TRUE) // announce
LOCAL ip% // no care
LOCAL client% = SOCK_TCPACCEPT(sock_listen%, ip%)
IF client% <> -1
DIMPUSH sock_remote%[], client%
ENDIF

LOCAL bSleep% = TRUE

LOCAL inp$

FOREACH sock% IN sock_remote%[]
LOCAL rv% = SOCK_RECV(sock%, msg$, 1024*100)

IF rv% = 0 THEN DELETE sock% // disconnected
IF rv% > 0
bSleep% = FALSE
LOCAL cmd$  = LEFT$(msg$, 4)
LOCAL para$ = MID$(msg$, 4)

msg$ = "" // return string

SELECT cmd$
CASE "LSFL" // ListFiles
IF DOESDIREXIST(para$)
LOCAL cd$ = GETCURRENTDIR$()
SETCURRENTDIR(para$)
LOCAL files$[]
LOCAL n% = GETFILELIST("*.*", files$[])
SETCURRENTDIR(cd$)
FOR i% = 0 TO INTEGER(n% / 0x10000)-1
DIMDEL files$[], 0
NEXT
FOREACH f$ IN files$[]
INC msg$, f$+"|"
NEXT
ELSE
msg$ = "NO_DIR" + para$
ENDIF
SOCK_TCPSEND(sock%, "LSFL"+msg$)
SOCK_CLOSE( sock% )
DELETE sock%
CASE "FILE" // Get File Contents
IF DOESFILEEXIST(para$)
LOCAL n% = GETFILESIZE(para$)
LOCAL fi% = GENFILE()
IF OPENFILE(fi%, para$, 1)
READSTR fi%, msg$, n%
CLOSEFILE fi%
ENDIF
ENDIF
SOCK_TCPSEND(sock%, "FILE"+msg$)
SOCK_CLOSE( sock% )
DELETE sock%
CASE "PILE" // receive and save file contents
msg$ = ""
gfp_ReadToTheEnd(sock%, msg$)
INC para$, msg$

LOCAL pos% = INSTR(para$, "\n")
IF pos>0
LOCAL file$ = LEFT$(para$, pos)
para$ = MID$(para$, pos+1)
LOCAL fi% = GENFILE()
STDOUT "writing to file:"+file$+":"
IF OPENFILE(fi%, file$, 0)
STDOUT " - ok\n"
WRITESTR fi%, para$
CLOSEFILE fi%
ELSE
STDOUT " - fail\n"
ENDIF

ENDIF
SOCK_CLOSE( sock% )
DELETE sock%

ENDSELECT

ENDIF
NEXT

IF bSleep%
SLEEP 10
ENDIF

// allow to quit
IF qfoo()
SOCK_CLOSE(sock_listen%)
FOREACH sock% IN sock_remote%[]
SOCK_CLOSE(sock%)
NEXT
RETURN
ENDIF
WEND
ENDFUNCTION



and an example project that either starts a server (with a quit option) or gets the files in the remote's "Media", as well as transfer the first file over.
Code (glbasic) Select

// --------------------------------- //
// Project: glbFileProtocol
// Start: Saturday, November 26, 2011
// IDE Version: 10.179



LOCAL app$="gfpTest"

SOCK_INIT()

IF PLATFORMINFO$("") = "WIN32"
LOCAL pFoo AS gfp_QuitOption
pFoo = MyQuit
gfp_FileServer(app$, 0, pFoo )
ELSE
LOCAL files$[], ok%
IF gfp_GetFileList("Media", files$[], app$)
FOREACH f$ IN files$[]
STDOUT "file: "+f$+"\n"
NEXT

IF LEN(files$[])
STDOUT "Get remote file\n"
ok% = gfp_GetFile("Media/"+files$[0], "Media/remote-"+files$[0], app$)
STDOUT "Get remote file was :"+ok%+"\n"
ENDIF

ELSE
STDOUT "File list failed\n"
ENDIF


STDOUT "Put local file\n"
ok% = gfp_PutFile("Media/"+files$[0], "Media/I_send_you.txt", app$)
STDOUT "Put local file was :"+ok%+"\n"

ENDIF




FUNCTION MyQuit:
PRINT "Press to quit",0,0
IF MOUSEAXIS(3) THEN RETURN TRUE

RETURN FALSE
ENDFUNCTION




** Update
-Faster session detection, reuse of known ip address
-larger buffers, read lops for large files (copied a bible in both directions)
-gfp_PutFile to push a file to the server app side.

It works pretty great now.



trucidare

Do you have a C++ version of this for me?
i need a simple way to exchange files from device to pc
MacBook Pro 2,2 GHz Core 2 Duo, 4 GB RAM, 160 GB HDD, 8600M GT
Core i3 - 3,07 GHz, 8 GB Ram, 2.5 TB HDD, Geforce GTX 260+ OC

Kitty Hello

It's almost the same. The sock commands just wrap the sockety api

bigsofty

Cheers,

Ian.

"It is practically impossible to teach good programming style to students that have had prior exposure to BASIC.  As potential programmers, they are mentally mutilated beyond hope of regeneration."
(E. W. Dijkstra)

Crivens

What about download a file from the Web? I'm thinking of maybe a news app. I'm sure the net commands currently will download a file maybe, but how do you go about putting a download in the background?

So if you are downloading a big picture for example, and the user decides to cancel and look at another news item, how would you do that? Or even have a progress bar to show how much has downloaded. Ideally I'm thinking of being able to have a command where you can say bob=NETGETDATA("http://www.bob.com/picture.jpg","news.jpg"). Then the image is downloaded to "news.jpg". Then while it is downloading you can say NETGETPROGRESS(bob) to get the percentage downloaded (-1 if errored?), or NETSTOPDATA(bob) to stop it.

I created a program years ago with VB6 that did something similar (basically an integrated forum in my program that contacted my ASP pages) but I can't see how I can do that in GLB. And would be well nice. I'm thinking for use with an online catalogue of my games (stupid WebOS...), but also for integrated forums and I still have some love to convert my MUD to iOS...

Cheers
Current fave quote: Cause you like musicians and I like people with boobs.

Kitty Hello

Search for http post. Theres already code for that. This library is to transfer in-app files. I need this for the GACK project to exchange files from the pad to the pc to make a new app from the games. Also, I'm using the device as a sort of copy protection for the pc version.

Wampus

There are all manner of uses for this. Thanks Gernot.

mentalthink

Thanks for this usefull LIB... only a question becuase I don´t look before:

This :
@exitout:

What´s do... It´s a SUB it´s something in C++, but I don´t look any SUB or InlineCommand...

Thanks in advance...

Kitty Hello

@exitout: is a label that you can use with GOTO. And the "@" hides it in the jump-bar on the right side.

mentalthink

thanks Gernnot, I have to do my homework  :-[ :-[