Here is a buffered file transfer routine I have made. It is much more advance then Inf0Byt3 idea, and works on both internet and LAN. The code is made to ensure that all data is transfered and not corrupted.
You can specifie size of buffers and enable/disable threads in the inlcude file (for both client and server) to optimize for the code's purpose and best speed. Run the server first and then the client.
Edit: Some bugs removed
Edit2:
Added possible to resume downloading. Just choose the directory where the file to continue downloading to is. To test it, quit the client while uploading, and then restart server and client. Download the same file and choose the same directory.
Edit3:
User/password authentication added and some bugs removed.
Server:
Code: Select all
; File send/receive example with progress(bar). Fast and reliable, works on lan and over internet (both tested)
; The server, receives the file
; By bonne_den_kule - Aug,2006
EnableExplicit
IncludeFile "Include.pb"
EnableGraphicalConsole(1)
OpenConsole()
ConsoleTitle("Server")
Define Quit.b, DataReceived.l, Buffer.l=AllocateMemory(#BufferSize), FileSize.q, FileOffset.q, FileName.s, Reserved.l
Define Path.s, TotaltDataReceived.q, DataInBuffer.l, ClientID.l, StartTime.l, Timer.l, TempDataReceived.l, DataTimer.l
Define Username.s, Password.s
If #Threaded
Define Buffer2.l=AllocateMemory(#BufferSize), BufferData.DataInfo, WriteThread.l
EndIf
If InitNetwork()
PrintN("Init network done")
If CreateNetworkServer(#Server, #Server_Port)
PrintN("Server created on "+Hostname())
Repeat
Select NetworkServerEvent()
Case #PB_NetworkEvent_Connect
PrintN("Client connected, ip/hostname: "+IPString(GetClientIP(EventClient())))
Case #PB_NetworkEvent_Disconnect
PrintN("Client disconnected")
Quit=1
Case #PB_NetworkEvent_Data
ClientID=EventClient()
DataReceived=ReceiveNetworkDataEx(ClientID, Buffer)
If DataReceived
PrintN("Valid data received")
Select PeekB(Buffer+4) ;Reading network message
Case #SendFile ;A message sent from the client
FileSize=PeekQ(Buffer+5) ;Length of file
Reserved=PeekL(Buffer+13) ;Reserved space in the packet for other info (to be used in the future maybe)
FileName=PeekS(Buffer+17) ;File name
Username=PeekS(Buffer+18+Len(FileName))
Password=PeekS(Buffer+19+Len(FileName)+Len(Username), DataReceived-19)
If CheckFilename(FileName) And FileSize<>0 And ValidateUser(Username, Password)
PrintN("Name: "+FileName+", size of file: "+Str(Int(FileSize/1024))+"kB")
Path.s=PathRequester("Where to save "+FileName, "")
If Path
If OpenFile(#File, Path+FileName)
FileBuffersSize(#File, #FileBufferSize)
;Making a packet to send to the client to say that the request is accepted
PokeL(Buffer, 13) ;The size of the packet
PokeB(Buffer+4, #RequestAccepted) ;The message
PokeQ(Buffer+5, Lof(#File)) ;Message to client to read from this offset
FileSize-Lof(#File) ;Bytes of data to download
FileOffset=Lof(#File) ;Continue to download from last time if filesize<>0
If SendNetworkDataEx(ClientID, Buffer) And FileSize>Lof(#File)
PrintN("File transfer request accepted")
DataTimer=ElapsedMilliseconds()
Repeat ;Wait for data
Delay(1)
If ElapsedMilliseconds()-DataTimer>#TimeOut Or NetworkServerEvent()=#PB_NetworkEvent_Disconnect
Quit=1
Break
EndIf
Until NetworkServerEvent()=#PB_NetworkEvent_Data
If Not Quit
PrintN("Receiving the file")
If FileOffset
PrintN("Will resume downloading from last time")
EndIf
FileSeek(#File, FileOffset)
StartTime=ElapsedMilliseconds()
Timer.l=ElapsedMilliseconds()
;The file receive loop
Repeat
DataTimer=ElapsedMilliseconds()
Repeat ;Wait for data
Delay(1)
If ElapsedMilliseconds()-DataTimer>#TimeOut Or NetworkServerEvent()=#PB_NetworkEvent_Disconnect
Quit=1
Break
EndIf
Until NetworkServerEvent()=#PB_NetworkEvent_Data
If Not Quit
DataReceived=ReceiveNetworkData(ClientID, Buffer+DataInBuffer, Min(#BufferSize-DataInBuffer, FileSize-TotaltDataReceived))
If DataReceived<=-1
PrintN("Connection error")
If #Threaded
If IsThread(WriteThread)
WaitThread(WriteThread)
EndIf
BufferData\Buffer=Buffer
BufferData\DataInBuffer=DataInBuffer
WriteThread=CreateThread(@WriteThread(), @BufferData)
Else
WriteData(#File, Buffer, DataInBuffer)
EndIf
DataInBuffer=0
Break
EndIf
TempDataReceived+DataReceived
TotaltDataReceived+DataReceived
DataInBuffer+DataReceived
If DataInBuffer>Int(#BufferSize*0.9) Or TotaltDataReceived=FileSize
If #Threaded
If IsThread(WriteThread)
WaitThread(WriteThread)
EndIf
BufferData\Buffer=Buffer
BufferData\DataInBuffer=DataInBuffer
WriteThread=CreateThread(@WriteThread(), @BufferData)
Swap Buffer, Buffer2
Else
WriteData(#File, Buffer, DataInBuffer)
EndIf
DataInBuffer=0
EndIf
If ElapsedMilliseconds()-Timer>1000
PrintN("Progress: "+StrD(((TotaltDataReceived+FileOffset)/(FileSize+FileOffset))*100, 1)+"%, speed: "+StrD((TempDataReceived/1024)/((ElapsedMilliseconds()-Timer)/1000), 2)+"kB/s")
Timer=ElapsedMilliseconds()
TempDataReceived=0
EndIf
Else
PrintN("Network timeout or client lost.")
;CloseNetworkConnection(ClientID) Crashes the server if the client has disconnected from before
Quit=1
DataReceived=-1
Break
EndIf
Until TotaltDataReceived=FileSize
If #Threaded
If IsThread(WriteThread)
WaitThread(WriteThread)
EndIf
EndIf
If DataReceived>-1
PrintN("File received")
PrintN("Average speed: "+StrD((FileSize/1024)/((ElapsedMilliseconds()-StartTime)/1000), 2)+"kB/s, time used: "+FormatTime(Int(ElapsedMilliseconds()-StartTime)/1000))
EndIf
Else
PrintN("Network timeout or client lost.")
;CloseNetworkConnection(ClientID) Crashes the server if the client has disconnected from before
Quit=1
EndIf
Else
If Not (FileSize>Lof(#File))
PrintN("The file is already finished downloaded")
CloseNetworkConnection(ClientID)
Else
PrintN("Could not send request to client")
EndIf
Quit=1
EndIf
Else
PrintN("Cant create or open file")
PokeL(Buffer, 5) ;The size of the packet
PokeB(Buffer+4, #RequestDenied)
SendNetworkDataEx(ClientID, Buffer)
CloseNetworkConnection(ClientID)
Quit=1
EndIf
Else
PrintN("Wrong or no specified path")
PokeL(Buffer, 5) ;The size of the packet
PokeB(Buffer+4, #RequestDenied)
SendNetworkDataEx(ClientID, Buffer)
CloseNetworkConnection(ClientID)
Quit=1
EndIf
Else
If Not ValidateUser(Username, Password)
PrintN("Wrong username and/or password")
Else
PrintN("Wrong file name or size")
EndIf
PokeL(Buffer, 5) ;The size of the packet
PokeB(Buffer+4, #RequestDenied)
SendNetworkDataEx(ClientID, Buffer)
CloseNetworkConnection(ClientID)
Quit=1
EndIf
EndSelect
Else
PrintN("Garbarge received from client")
CloseNetworkConnection(ClientID)
Quit=1
EndIf
EndSelect
Delay(1)
Until Quit=1
Else
PrintN("Error creating server")
EndIf
Else
PrintN("Error initing network")
EndIf
If #Threaded
If IsThread(WriteThread)
WaitThread(WriteThread)
EndIf
EndIf
If IsFile(#File)
CloseFile(#File)
EndIf
CloseNetworkServer(#Server)
;FreeMemory(-1)
PrintN("Server closed")
Input()
Code: Select all
; File send/receive example with progress(bar). Fast and reliable, works on lan and over internet (both tested)
; The client, sends the file
; By bonne_den_kule - Aug,2006
EnableExplicit
IncludeFile "Include.pb"
EnableGraphicalConsole(1)
OpenConsole()
ConsoleTitle("Client")
Define ConnectionID, Buffer.l=AllocateMemory(#BufferSize), File.s, DataRead.l, DataSent.l, Quit.l, FileSize.l
Define StartTime.l, Timer.l, TempDataSent.l, TotaltDataSent.l, TotalBufferSent.q, FileOffset.q, DataTimer.l
If #Threaded
Define ReadThread.l, Buffer2.l=AllocateMemory(#BufferSize)
EndIf
If InitNetwork()
PrintN("Init network done")
ConnectionID=OpenNetworkConnection(#HostName, #Server_Port)
If ConnectionID
PrintN("Connected to server")
File=OpenFileRequester("File to send", "", "", 0)
If FileSize(File)>0 And ReadFile(#File, File)
FileBuffersSize(#File, #FileBufferSize)
;A packet to send to server:
FileSize=FileSize(File)
PokeL(Buffer, Len(GetFilePart(File))+19+Len(#Username)+Len(#Password))
PokeB(Buffer+4, #SendFile)
PokeQ(Buffer+5, FileSize) ;Length of file to send
PokeL(Buffer+13, 0) ;Reserved for use in the future
PokeS(Buffer+17, GetFilePart(File))
PokeS(Buffer+18+Len(GetFilePart(File)), #Username)
PokeS(Buffer+19+Len(GetFilePart(File))+Len(#Username), #Password)
SendNetworkDataEx(ConnectionID, Buffer)
DataTimer=ElapsedMilliseconds()
Repeat
Delay(1)
If NetworkClientEvent(ConnectionID)=#PB_NetworkEvent_Disconnect; Or ElapsedMilliseconds()-DataTimer>#TimeOut*3
Quit=1
Break
EndIf
Until NetworkClientEvent(ConnectionID)=#PB_NetworkEvent_Data
If Not Quit
If ReceiveNetworkDataEx(ConnectionID, Buffer)=13 And PeekB(Buffer+4)=#RequestAccepted And PeekQ(Buffer+5)<FileSize
PrintN("Server accepted the request")
FileOffset=PeekQ(Buffer+5)
FileSize-FileOffset
PrintN("Starting sending the file, name: "+GetFilePart(File)+", file size: "+Str(Int((FileSize+FileOffset)/1024))+"kB")
If FileOffset
PrintN("Will resume sending from last time")
EndIf
StartTime=ElapsedMilliseconds()
Timer=ElapsedMilliseconds()
FileSeek(#File, FileOffset)
If #Threaded
ReadThread=CreateThread(@ReadThread(), Buffer)
EndIf
;The file send loop
Repeat
If #Threaded
If IsThread(ReadThread)
WaitThread(ReadThread)
EndIf
DataRead=GlobalDataRead
ReadThread=CreateThread(@ReadThread(), Buffer2)
Else
DataRead=ReadData(#File, Buffer, #BufferSize)
EndIf
If DataRead=0 And TotaltDataSent<>FileSize
PrintN("Can't read file")
DataSent=-1
Break
EndIf
TotalBufferSent=0
Repeat
DataSent=SendNetworkData(ConnectionID, Buffer, DataRead-TotalBufferSent)
If DataSent<=-1
PrintN("Connection error")
Break 2
EndIf
TotalBufferSent+DataSent
TempDataSent+DataSent
If ElapsedMilliseconds()-Timer>1000
PrintN("Progress: "+StrD(((TotaltDataSent+FileOffset)/(FileSize+FileOffset))*100, 1)+"%, speed: "+StrD((TempDataSent/1024)/((ElapsedMilliseconds()-Timer)/1000), 2)+"kB/s")
Timer=ElapsedMilliseconds()
TempDataSent=0
EndIf
Until DataRead=TotalBufferSent
TotaltDataSent+TotalBufferSent
If #Threaded
Swap Buffer, Buffer2
EndIf
Until TotaltDataSent=FileSize
If #Threaded
If IsThread(ReadThread)
WaitThread(ReadThread)
EndIf
EndIf
If DataSent>-1
PrintN("File sent to server")
PrintN("Average speed: "+StrD((FileSize/1024)/((ElapsedMilliseconds()-StartTime)/1000), 2)+"kB/s, time used: "+FormatTime(Int(ElapsedMilliseconds()-StartTime)/1000))
EndIf
Else
If PeekQ(Buffer+5)>=FileSize
PrintN("The file is already finished uploaded to the server")
EndIf
PrintN("The client could not upload the file")
EndIf
Else
PrintN("Connection timeout or server lost")
EndIf
Else
PrintN("Wrong file, empty file or no file specified")
EndIf
Else
PrintN("Error connecting to server")
EndIf
Else
PrintN("Error initing network")
EndIf
If ConnectionID
CloseNetworkConnection(ConnectionID)
EndIf
If IsFile(#File)
CloseFile(#File)
EndIf
;FreeMemory(-1)
PrintN("Client closed")
Input()
Code: Select all
; File send/receive example with progress(bar). Fast and reliable, works on lan and over internet (both tested)
; The include file for both server and client
; By bonne_den_kule - Aug,2006
; The constants can be different on the client and server (expect the server port)
#Server_Port=2586
#HostName="localhost"
#BufferSize=1024*100 ;The main buffer for reading/sending and writing/receiving, Higher buffer, better speed, but more memory usage.
#FileBufferSize=#BufferSize ;The PureBasic internal file buffer, can be turned off by setting it to 0.
#TimeOut=7000 ; The connection time out period.
#Threaded=#True ; The code send and read or receive and write datas at the same time if turned on. Effective if #filebuffersize is set to 0
#Username="Fred" ;Username for the client (edit ValidateUser() for customize login for server)
#Password="Berikco" ;Password for the client (edit ValidateUser() for customize login for server)
Enumeration
#Server
#File
#SendFile
#RequestAccepted
#RequestDenied
EndEnumeration
If #Threaded
Global GlobalDataRead.l
EndIf
Structure DataInfo
Buffer.l
DataInBuffer.l
EndStructure
Procedure ReceiveNetworkDataEx(ConnectionID.l, Buffer.l) ;Function to prevent some kinds of buffer overflow's and ensure that all data is received
Define DataRead.l, TotaltDataRead.l, Length.l=4, StartTime.l=ElapsedMilliseconds()
Repeat
DataRead=ReceiveNetworkData(ConnectionID, Buffer+TotaltDataRead, Length-TotaltDataRead)
TotaltDataRead+DataRead
If TotaltDataRead>=4 ;Get the length of the packet as fast as possible
Length=PeekL(Buffer)
EndIf
If DataRead<=-1 Or Length>#BufferSize Or (ElapsedMilliseconds()-StartTime>#TimeOut And TotaltDataRead<=4)
ProcedureReturn 0
EndIf
Delay(1)
Until TotaltDataRead=Length And Length<>0
ProcedureReturn TotaltDataRead
EndProcedure
Procedure SendNetworkDataEx(ConnectionID.l, Buffer.l) ;Function to ensure that all data is sent
Define DataSent.l, TotaltDataSent.l, StartTime.l=ElapsedMilliseconds(), Length=PeekL(Buffer) ;Number of bytes to send
Repeat
DataSent.l=SendNetworkData(ConnectionID, Buffer+TotaltDataSent, Length-TotaltDataSent)
TotaltDataSent+DataSent
If DataSent<=-1 Or (ElapsedMilliseconds()-StartTime>#TimeOut And TotaltDataSent=0) Or Length=0
ProcedureReturn 0
EndIf
Delay(1)
Until TotaltDataSent=Length And TotaltDataSent>0
ProcedureReturn 1
EndProcedure
Procedure.q Min(val1.q, val2.q) ;Returns the lowest value
If val1<val2
ProcedureReturn val1
Else
ProcedureReturn val2
EndIf
EndProcedure
Procedure.s FormatTime(seconds.l)
Define Date.s=FormatDate("%hhh %iim %sss", AddDate(0, #PB_Date_Second, seconds))
Date=RemoveString(Date, "00h")
Date=RemoveString(Date, "00m")
Date=RemoveString(Date, "00s")
Date=Trim(Date)
If Left(Date, 1)="0"
Date=Right(Date, Len(Date)-1)
EndIf
If Trim(Date)=""
Date="less than 1s"
EndIf
ProcedureReturn Date
EndProcedure
Procedure ReadThread(Buffer)
Define DataRead.l
If Not Eof(#File)
DataRead.l=ReadData(#File, Buffer, #BufferSize)
GlobalDataRead=DataRead
EndIf
EndProcedure
Procedure WriteThread(*strct.DataInfo)
WriteData(#File, *strct\Buffer, *strct\DataInBuffer)
EndProcedure
Procedure ValidateUser(Username.s, Password.s)
If Username=#Username And Password=#Password
ProcedureReturn 1
Else
ProcedureReturn 0
EndIf
EndProcedure