BASIC

Author Topic: gfp - GLBasic File Protocol (very simple file transfers)  (Read 5284 times)

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10665
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
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.


« Last Edit: 2011-Nov-29 by Kitty Hello »

Offline trucidare

  • Administrator
  • Prof. Inline
  • *******
  • Posts: 1377
  • Bachelor of Fail
    • View Profile
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

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10665
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
It's almost the same. The sock commands just wrap the sockety api

Offline bigsofty

  • Community Developer
  • Prof. Inline
  • ******
  • Posts: 2556
    • View Profile
Very nice, thanks Gernot!
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)

Offline Crivens

  • Prof. Inline
  • *****
  • Posts: 913
    • View Profile
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.

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10665
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
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.

Offline Wampus

  • Prof. Inline
  • *****
  • Posts: 1004
    • View Profile
There are all manner of uses for this. Thanks Gernot.

Offline mentalthink

  • Prof. Inline
  • *****
  • Posts: 3338
  • Integrated Brain
    • View Profile
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...

Offline Kitty Hello

  • code monkey
  • Administrator
  • Prof. Inline
  • *******
  • Posts: 10665
  • here on my island the sea says 'hello'
    • View Profile
    • http://www.glbasic.com
@exitout: is a label that you can use with GOTO. And the "@" hides it in the jump-bar on the right side.

Offline mentalthink

  • Prof. Inline
  • *****
  • Posts: 3338
  • Integrated Brain
    • View Profile
thanks Gernnot, I have to do my homework  :-[ :-[