Re: Remplacer ReceiveNetworkFile
Publié : jeu. 17/déc./2009 19:11
Désolé, pas eu le temps de retoucher au code. C'est pour bientôt 

Forums PureBasic - Français
http://forums.purebasic.com/french/
Code : Tout sélectionner
; ________________________________________________________
;
; Fonctions d'envoi et de réception d'un fichier
;
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Auteur : Cls
; Version : Décembre 2009
; Compatibilité : PB 4.40
; Structure de l'entête
Structure NETFILE_HEADER
type.l
no_packet.l
offset_file.q
flags.l
data_size.l
EndStructure
; Structure de transfert d'un fichier
Structure FILE_TRANSFERT
Client_id.l
Fichier_Destination.s
Fichier_Taille.q
Fichier_Transfere.q
EndStructure
; Structure client
; Extension d'un FILE_TRANSFERT (cela signifie qu'un client ne peut avoir qu'un seul transfert simultané)
Structure CLIENT Extends FILE_TRANSFERT
id.l
ip.l
port.l
last_packet_date.l
last_packet_type.l
connexion_date.l
EndStructure
; Tableau contenant les clients connectés
Global NewMap Client.CLIENT()
; Liste chainée contenant les transferts en cours
;Global NewList Transfert.FILE_TRANSFERT()
; Configuration
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
#Destination_Directory = "C:\Downloads\"
#SEND_DELAY = 1
#RECEPTION_DELAY = 3
#READ_BUFFER_SIZE = 51200 ; Taille du buffer de lecture
#HEADER_SIZE = SizeOf(NETFILE_HEADER) ; Taille de l'entête
#PACKET_SIZE = #HEADER_SIZE + #READ_BUFFER_SIZE ; Taille totale d'un packet de données
#PACKET_RECEIVE_ATTEMPTS = 10
; Type de packet de données
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
#PACKET_TYPE_FILE_NAME = $10110001
#PACKET_TYPE_FILE_CONFIRM = $10110011
#PACKET_TYPE_TRANSFERT_FILE = $10110111
#PACKET_TYPE_MD5 = $10111111
; Code d'erreurs
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
#NETFILE_ERROR_BAD_CONNEXION = $00000010
#NETFILE_ERROR_FILE_NOT_FOUND = $00000100
#NETFILE_ERROR_FILE_NOT_READABLE = $00001000
#NETFILE_ERROR_FILE_NOT_WRITABLE = $00010000
#INTERNAL_ERROR = $FFFF
; Création d'un packet de données
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure PacketCreate()
*packet = AllocateMemory(#PACKET_SIZE)
If *packet = 0
ProcedureReturn #INTERNAL_ERROR
EndIf
ProcedureReturn *packet
EndProcedure
; Libère un packet de données
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure PacketFree(*packet)
FreeMemory(*packet)
EndProcedure
; Création de l'entête d'un packet de données
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure PacketCreateHeader(*packet, type.l, numero.q = 0, place_fichier.q = 0, taille_donnees.l = 0, drapeaux.l = 0)
*p_packet = *packet
PokeL(*p_packet, type) : *p_packet + SizeOf(Long)
PokeL(*p_packet, numero) : *p_packet + SizeOf(Long)
PokeQ(*p_packet, place_fichier) : *p_packet + SizeOf(Quad)
PokeL(*p_packet, drapeaux) : *p_packet + SizeOf(Long)
PokeL(*p_packet, taille_donnees) : *p_packet + SizeOf(Long)
ProcedureReturn *packet
EndProcedure
; Ajout des données à un packet
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure PacketAddData(*packet, *donnees.l, taille.l)
*p_packet = *packet
*p_packet + SizeOf(NETFILE_HEADER)
CopyMemory(*donnees, *p_packet, taille)
ProcedureReturn *packet
EndProcedure
; Ajout de données de type chaine
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure PacketAddString(*packet, String.s, taille.l)
*p_packet = *packet
*p_packet + SizeOf(NETFILE_HEADER)
PokeL(*p_packet, taille) : *p_packet + SizeOf(Long)
PokeS(*p_packet, String, taille) : *p_packet + taille
ProcedureReturn *packet
EndProcedure
; Procédure de DEBUG d'un paquet
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure PacketDebug(*packet, taille.l)
*header.NETFILE_HEADER = *packet
Debug "No Packet : " + Str(*header\no_packet)
Debug "Offset : " + Str(*header\offset_file)
Debug "Flags : " + Str(*header\flags)
Debug "DATA size : " + Str(*header\data_size)
*packet + SizeOf(NETFILE_HEADER)
str.s
For x = 0 To taille - SizeOf(NETFILE_HEADER)
str + Chr(PeekC(*packet + x))
Next
Debug str
EndProcedure
; Message d'erreur
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure Error(msg.s, type.l = 1)
Select type
Case 1
MessageRequester("Erreur", msg, #MB_ICONINFORMATION)
Default
Debug msg
EndSelect
EndProcedure
; Affichage sur l'IHM
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure Message(msg.s)
Global log
msg = FormatDate("%dd/%mm %hh:%ii:%ss", Date()) + " - " + msg
AddGadgetItem(log, -1, msg)
EndProcedure
; Procédure d'envoi d'un fichier
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; @param Connexion l'identifiant de la connexion
; @param Fichier le fichier à envoyer
; @param Fichier_destination le nom du fichier qui sera cré en local
; @param Loopback une fonction permettant de connaitre l'état du transfert (optionel)
; @return 1 si le fichier est envoyé correctement
Procedure SendNetworkFileEx(Connexion.l, Fichier.s, Fichier_destination.s = "")
; Contrôles
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Vérifie la connexion
If Connexion = 0
ProcedureReturn #NETFILE_ERROR_BAD_CONNEXION
EndIf
; Vérifie le fichier
total_filesize.q = FileSize(Fichier)
If total_filesize < 0
ProcedureReturn #NETFILE_ERROR_FILE_NOT_FOUND
EndIf
; Création d'un buffer de lecture
*read_buffer = AllocateMemory(#READ_BUFFER_SIZE)
If *read_buffer = 0
ProcedureReturn #INTERNAL_ERROR
EndIf
; Transfert
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Handle vers le fichier
hFile.l = ReadFile(#PB_Any, Fichier)
If hFile = 0
ProcedureReturn #NETFILE_ERROR_FILE_NOT_READABLE
EndIf
; Création d'un buffer pour l'envoi des données (packet)
*packet = PacketCreate()
; Lit le fichier par bloc
p_locate_file.q = 0
numero_packet.l = 0
data_send.q = 0
While Eof(hFile) = 0
; Lit un bloc de READ_BUFFER
read_length = ReadData(hFile, *read_buffer, #READ_BUFFER_SIZE)
;Debug Str(read_length) + " " + Str(p_locate_file)
; Création d'un paquet de données
If read_length = #READ_BUFFER_SIZE
; Packet "normal"
*packet = PacketCreateHeader(*packet, #PACKET_TYPE_TRANSFERT_FILE, numero_packet, p_locate_file, read_length)
Else
; Dernier packet de données
*packet = PacketCreateHeader(*packet, #PACKET_TYPE_TRANSFERT_FILE, numero_packet, p_locate_file, read_length, 1)
EndIf
; Ajout des données à envoyer
*packet = PacketAddData(*packet, *read_buffer, read_length)
; Envoi du packet sur le réseau
SendNetworkData(Connexion, *packet, #PACKET_SIZE)
;PacketDebug(*packet, read_length + #HEADER_SIZE)
data_send + read_length
; Déplace le pointeur de lecture
p_locate_file + read_length
FileSeek(hFile, p_locate_file)
; Packet suivant
numero_packet + 1
; Delay
Delay(#SEND_DELAY)
Wend
; Libération des mémoires
CloseFile(hFile)
FreeMemory(*read_buffer)
PacketFree(*packet)
ProcedureReturn #True
EndProcedure
; Procédure de reception d'un fichier
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; @param Connexion l'identifiant de la connexion Client
; @param AutoriserNomPersonnalise A true, permet au client de spécifier le nom du fichier
; @param Loopback une fonction permettant de connaitre l'état du transfert (optionel)
; @return 1 si le fichier est recu correctement
Procedure ReceiveNetworkFileEx(Connexion, Fichier.s, *packet.l, length.l)
; Contrôles
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; Vérifie la connexion
If Connexion = 0
ProcedureReturn #NETFILE_ERROR_BAD_CONNEXION
EndIf
; Définition du nom du fichier
Fichier_destination.s = Fichier
; Création d'un buffer d'écriture réception
*write_buffer = AllocateMemory(#PACKET_SIZE)
If *write_buffer = 0
ProcedureReturn #INTERNAL_ERROR
EndIf
; Création d'un buffer contenant le packet entier
*packet_received = PacketCreate()
; Recompose le packet de données
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
CopyMemory(*packet, *packet_received, length)
; Ecriture sur le disque
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
; A ce moment *packet contient le packet de données entier
;PacketDebug(*packet_received, #PACKET_SIZE)
; On le réaassemble sur notre disque
hWriteFile = OpenFile(#PB_Any, #Destination_Directory + Fichier_destination)
If hWriteFile = 0
ProcedureReturn #NETFILE_ERROR_FILE_NOT_WRITABLE
EndIf
; Entête
*header.NETFILE_HEADER = *packet_received
; Données
*p_packet = *packet_received
*p_packet + SizeOf(NETFILE_HEADER)
; Déplace le pointeur d'écriture et écrit les données
; Si le pointeur dépasse la fin du fichier, on rempli de vide
If *header\offset_file > Lof(hWriteFile)
FileSeek(hWriteFile, Lof(hWriteFile))
taille_to_fill.l = *header\offset_file - Lof(hWriteFile)
*filler = AllocateMemory(taille_to_fill)
FillMemory(*filler, taille_to_fill)
WriteData(hWriteFile, *filler, taille_to_fill)
EndIf
FileSeek(hWriteFile, *header\offset_file)
WriteData(hWriteFile, *p_packet, *header\data_size)
; Libération des mémoires
CloseFile(hWriteFile)
FreeMemory(*write_buffer)
FreeMemory(*packet_received)
EndProcedure
; Envoi d'un fichier
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure SendFile(pFile.l)
file.s = PeekS(pFile)
Global client
; Modification du nom du fichier
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
FileName.s = GetFilePart(file)
*packet = PacketCreate()
*packet = PacketCreateHeader(*packet, #PACKET_TYPE_FILE_NAME, 0, FileSize(file), Len(FileName))
*packet = PacketAddString(*packet, FileName, Len(FileName))
SendNetworkData(client, *packet, #PACKET_SIZE)
; Envoi des données au serveur
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
ret = SendNetworkFileEx(client, file)
If ret <> 1
Error("Code " + Str(ret) + " : Erreur lors de l'envoi du fichier.")
Else
; Confirmation
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*packet = PacketCreate()
*packet = PacketCreateHeader(*packet, #PACKET_TYPE_FILE_CONFIRM)
SendNetworkData(client, *packet, #PACKET_SIZE)
; Vérification de l'intégrité du fichier
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
md5.s = MD5FileFingerprint(file)
*packet = PacketCreate()
*packet = PacketCreateHeader(*packet, #PACKET_TYPE_MD5)
*packet = PacketAddString(*packet, md5, Len(md5))
SendNetworkData(client, *packet, #PACKET_SIZE)
EndIf
; Libère la mémoire
PacketFree(*packet)
EndProcedure
; Sélection et envoi d'un fichier
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure SelectAndSendFile(value.l)
Global client
file.s = OpenFileRequester("", "", "", 1)
If file
SendFile(@file)
EndIf
EndProcedure
; Met à jour la barre de progression
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure RefreshProgressBar(Transfert.q, Total.q)
Global progress, progress_text
pourcentage.f = Transfert / Total * 100
SetGadgetText(progress_text, Str(pourcentage) + " %")
SetGadgetState(progress, pourcentage)
EndProcedure
; Comparaison du MD5 local avec le fichier distant
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure CheckMd5FileFingerPrint(md5.s, file.s)
If MD5FileFingerprint(file) = md5
ProcedureReturn #True
Else
ProcedureReturn #False
EndIf
EndProcedure
; Gère les évènements du serveur
; ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Procedure ManageNetworkServerEvent(clientId)
; Identifie le client dans la MAP
Protected nocli.s = Str(clientId)
; Réception du paquet
*packet = PacketCreate()
*header.NETFILE_HEADER = *packet
; On tamporise la réception sinon le programme va trop vite !
; On oublie pas de mettre un timout pour prévenir les boucles infinies malignes et la fin du fichier ;)
Protected attemptsReception.l = #PACKET_RECEIVE_ATTEMPTS
Protected length.l = 0
Protected offsetReception.l = 0
Repeat
length = ReceiveNetworkData(clientId, *packet + offsetReception, #PACKET_SIZE - offsetReception)
Debug "Length: " + Str(length)
If length > -1
offsetReception + length
EndIf
attemptsReception - 1
Delay(#RECEPTION_DELAY) ; Ce delay est important car il permet de ne pas lire trop vite le buffer
; Test d'arrêt
If attemptsReception <= 0 : Break : EndIf
If offsetReception >= #PACKET_SIZE : Break : EndIf
ForEver
Debug "attempts : " + Str(attemptsReception) + " - " + Str(offsetReception)
; Traitement du packet
Select *header\type
Case #PACKET_TYPE_FILE_NAME
taille_nom_fichier.l = PeekL(*packet + SizeOf(NETFILE_HEADER))
Client(nocli)\Fichier_Destination = PeekS(*packet + SizeOf(NETFILE_HEADER) + SizeOf(Long), taille_nom_fichier)
Client(nocli)\Fichier_Taille = *header\offset_file ; On utilise ce champ pour envoyer la taille du fichier
Client(nocli)\Fichier_Transfere = 0
Message("Changement du nom du fichier : " + Client(nocli)\Fichier_Destination + " (" + Str(Client(nocli)\Fichier_Taille) + " octets)")
Case #PACKET_TYPE_TRANSFERT_FILE
Message("Données reçues : " + Str(*header\data_size) + " octets")
ReceiveNetworkFileEx(clientId, Client(nocli)\Fichier_Destination, *packet, length)
Client(nocli)\Fichier_Transfere + *header\data_size
RefreshProgressBar(Client(nocli)\Fichier_Transfere, Client(nocli)\Fichier_Taille)
Case #PACKET_TYPE_FILE_CONFIRM
Message("Transfert du fichier terminé !")
Case #PACKET_TYPE_MD5
taille_md5.l = PeekL(*packet + SizeOf(NETFILE_HEADER))
MD5.s = PeekS(*packet + SizeOf(NETFILE_HEADER) + SizeOf(Long), taille_md5)
Debug MD5
If CheckMd5FileFingerPrint(MD5, #Destination_Directory + Client(nocli)\Fichier_Destination)
Message("L'empreinte MD5 du fichier est correcte. Le fichier a été transféré correctement.")
Else
Message("Le fichier est corrompu ! :(")
Error("Le transfert du fichier a échoué. Veuillez recommencer.")
EndIf
Default
Debug "Packet inconnu"
EndSelect
; Met à jour la liste Client
Client(nocli)\last_packet_date = Date()
Client(nocli)\last_packet_type = *header\type
; Libère la mémoire
PacketFree(*packet)
EndProcedure
;- Début du programe de test
If InitNetwork() = 0
Error("Pas de réseau")
End
EndIf
; Ouverture d'un écran
#height = 350 : #width = 600
Global window = OpenWindow(#PB_Any, 0, 0, #width, #height, "Test SendNetworkFile / ReceiveNetworkFile par Cls", #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget)
Global log = ListViewGadget(#PB_Any, 10, 10, #width - 20, #height - 75)
Global progress = ProgressBarGadget(#PB_Any, 10, #height - 60, #width - 20, 10, 0, 100, #PB_ProgressBar_Smooth)
Global progress_text = TextGadget(#PB_Any, #width - 40, #height - 40, 30, 20, "0 %")
Global send = ButtonGadget(#PB_Any, 10, #height - 40, 150, 30, "Envoyer un fichier...")
EnableWindowDrop(window, #PB_Drop_Files, #PB_Drag_Copy)
; Création d'un serveur
#port = 5000
Global server = CreateNetworkServer(#PB_Any, #port)
If server : Message("Serveur créé sur le port " + Str(#port)) : EndIf
; Création d'un client
Global client = OpenNetworkConnection("127.0.0.1", #port)
; Boucle des évènements
Quit.l
Repeat
;- Evènement fenêtre
wEvent = WaitWindowEvent(1)
Select wEvent
Case #PB_Event_CloseWindow
End
Case #PB_Event_Gadget
Select EventGadget()
Case send
CreateThread(@SelectAndSendFile(), 1)
EndSelect
Case #PB_Event_WindowDrop
Files.s = EventDropFiles()
fFile.s = StringField(Files, 1, Chr(10))
CreateThread(@SendFile(), @fFile)
EndSelect
;- Evènement réseau Serveur
sEvent = NetworkServerEvent()
clientId = EventClient() : nocli.s = Str(clientId)
Select sEvent
Case #PB_NetworkEvent_Connect
; Récupère les infos du client
cliip.l = GetClientIP(clientId)
cliport.l = GetClientPort(clientId)
; Ajoute un client
Client(nocli)\id = clientId
Client(nocli)\ip = cliip
Client(nocli)\port = cliport
Client(nocli)\connexion_date = Date()
Message("Client connecté : " + IPString(GetClientIP(clientId)) + ":" + Str(GetClientPort(clientId)))
Case #PB_NetworkEvent_Data
ManageNetworkServerEvent(clientId)
Case #PB_NetworkEvent_Disconnect
Message("Client déconnecté : " + IPString(GetClientIP(clientId)) + ":" + Str(GetClientPort(clientId)))
; Supprimer un client
DeleteMapElement(Client(), nocli)
EndSelect
;- Evènement réseau Client
tEvent = NetworkClientEvent(server)
Select tEvent
Case #PB_NetworkEvent_Data
EndSelect
Until Quit = 1