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:
// --------------------------------- //
// 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.
// --------------------------------- //
// 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.