Je vais te citer différents postesde Developpez.com :
"Les composants TClientSocket et TServerSocket ne sont plus installés par défaut avec Delphi7. Mais ils sont encore fournis. Il suffit d'installer le paquet /bin/dclsockets70.bpl via le menu Composants->Installer des paquets...
Il faut éviter de s'en servir pour de nouvelles applications, ils sont remplacés par les composants TCPClient/TTcpServer ou les composants Indy TIDTcpClient/TIDTcpServer. Cette méthode n'est donnée ici que pour assurer la compatibilité des applications Delphi5/Delphi6 avec Delphi7."
---------------------------------------------------------------
Transferer un fichier avec Indy
function TForm1.SendFile(const AFileName: string;
ATcpClient: TIdTCPClient) : Boolean;
var
Fs : TFileStream;
begin
Result := False;
ATcpClient.Connect(); //Connecte. Les propriétés Host et Port doivent être remplies.
try
Fs := TFileStream.Create(AFileName,fmOpenRead,fmShareDenyWrite); //Créer le flux
try
ATcpClient.WriteLn(Format('TRANS %s',[ExtractFileName(AFileName)])); //demander transfert
try
ATcpClient.WriteInteger(Fs.Size); //Ecrire la taille
ATcpClient.WriteStream(Fs); //Ecrit le flux
except
MessageDlg('Erreur pendant l''envoi du fichier.', mtError, [mbOK], 0);
end;
finally
FreeAndNil(Fs); //Libérer le flux
Result := ATcpClient.ReadLn()='OK'; //OK uniquement si le serveur a renvoyé "OK"
end;
finally
ATcpClient.Disconnect; //Déconnecter à la fin.
end;
end;
Code du serveur :
procedure TForm1.IdTCPServerExecute(AThread: TIdPeerThread);
var
Line, FileName : String;
i, FileSize : integer;
Fs : TFileStream;
begin
with AThread.Connection do
try
Line := ReadLn(); //Attends une commande de la forme TRANS suivi du nom de fichier
i := Pos(' ',Line);
if (i>0) and (LowerCase(Copy(Line,1,Pred(i)))='trans') then
begin
FileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName))
+ Copy(Line,Succ(i),Length(Line)); //Copier nom de fichier
Fs := TFileStream.Create(FileName,fmCreate); //Créer le flux
try
try
FileSize := ReadInteger(); //Lire la taille
ReadStream(Fs,FileSize,False); //Lire le flux
WriteLn('OK'); //Signaler succès
except
WriteLn('ERR'); //Signaler une erreur
end;
finally
FreeAndNil(Fs); //Libérer le flux dans tous les cas
end;
end
else
WriteLn('ERR'); //Commande incomprise
finally
Disconnect; //A la fin, on déconnecte
end;
end;
Exemple d'exploitation :
procedure TForm1.Button1Click(Sender: TObject);
begin
IdTCPClient.Host := 'localhost'; //Hôte
IdTCPClient.Port := 1985; //Le serveur doit aussi écouter sur le port 1985
if SendFile('c:\setup.exe',IdTCPClient) then
MessageDlg('Ok !', mtInformation, [mbOK], 0)
else
MessageDlg('Erreur...', mtInformation, [mbOK], 0)
end;
----------------------------------------------------------------------
Unit Unit1;
Interface
Uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp, ExtCtrls;
Type
TForm1 = Class(TForm)
Client: TClientSocket;
Serveur: TServerSocket;
GroupBox1: TGroupBox;
btnOpen: TButton;
btnClose: TButton;
MemoServeur: TMemo;
GroupBox2: TGroupBox;
btnEnvoyer: TButton;
btnStop: TButton;
Edit1: TEdit;
Ouvrir: TOpenDialog;
MemoClient: TMemo;
RetardDebut: TTimer;
Procedure btnOpenClick(Sender: TObject);
Procedure btnCloseClick(Sender: TObject);
Procedure ServeurClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
Procedure ServeurClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
Procedure ServeurClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; Var ErrorCode: Integer);
Procedure ServeurClientRead(Sender: TObject; Socket: TCustomWinSocket);
Procedure ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
Procedure ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
Procedure ClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; Var ErrorCode: Integer);
Procedure btnEnvoyerClick(Sender: TObject);
Procedure ClientRead(Sender: TObject; Socket: TCustomWinSocket);
Procedure btnStopClick(Sender: TObject);
Procedure RetardDebutTimer(Sender: TObject);
Private
{ Déclarations privées }
Public
{ Déclarations publiques }
End;
Var
Form1: TForm1;
Implementation
{$R *.dfm}
//**************************************************************************************
//
// Principe :
// le fichier envoyé en coupé en tranches de taille identiques et e transmises au serveur
// dans l'ordre du fichier.
//
// - Le client se connecte au serveur.
// - Si la connection est OK alors il envoie un trame du type ci-dessous avec ttType=1
// signalant qu'il s'agit du nom du fichier. ttNomFichier est alors le nom du fichier
// transféré.
// - Si le serveur accepte la demande, alors le fichier est envoyé par morceau avec le même
// type de trame. ttType = 2 pour signaler qu'il s'agit un morceau du fichier. ttDebut
// donne l'adresse du bloc dans le fichier et ttLong la taille du bloc.
// - Le client se deconnecte, ce qui ferme le fichier sur le serveur.
//
//**************************************************************************************
//**************************************************************************************
// Procédure d'affichage en clair des erreurs socket
// merci à djtexas
Function MessageErreurSocket( ErrorEvent: TErrorEvent; Var ErrorCode: Integer):String;
Var
ErrorMsg: String;
Begin
// définition du message d'erreur en fonction du code d'erreur
Case ErrorCode Of
10004 : ErrorMsg := 'Interrupted Function call.';
10013 : ErrorMsg := 'Permission Refusée.';
10014 : ErrorMsg := 'Mauvaise adresse.';
10022 : ErrorMsg := 'Arguments Invalides.';
10024 : ErrorMsg := 'Trop de fichiers ouverts.';
10035 : ErrorMsg := 'Resource temporarily unavailable.';
10036 : ErrorMsg := 'Operation en cours.';
10037 : ErrorMsg := 'Operation déjà en cours.';
10038 : ErrorMsg := 'Socket operation On non-socket.';
10039 : ErrorMsg := 'Destination address required.';
10040 : ErrorMsg := 'Message trop long.';
10041 : ErrorMsg := 'Protocol wrong Type For socket.';
10042 : ErrorMsg := 'Bad protocol option.';
10043 : ErrorMsg := 'Protocol Not supported.';
10044 : ErrorMsg := 'Socket Type Not supported.';
10045 : ErrorMsg := 'Operation Not supported.';
10046 : ErrorMsg := 'Protocol family Not supported.';
10047 : ErrorMsg := 'Address family Not supported by protocol family.';
10048 : ErrorMsg := 'Address already In use.';
10049 : ErrorMsg := 'Cannot assign requested address.';
10050 : ErrorMsg := 'Network Is down.';
10051 : ErrorMsg := 'Network Is unreachable.';
10052 : ErrorMsg := 'Network dropped connection On reset.';
10053 : ErrorMsg := 'Software caused connection abort.';
10054 : ErrorMsg := 'Connection reset by peer.';
10055 : ErrorMsg := 'No buffer space available.';
10056 : ErrorMsg := 'Socket Is already connected.';
10057 : ErrorMsg := 'Socket Is Not connected.';
10058 : ErrorMsg := 'Cannot send after socket shutdown.';
10060 : ErrorMsg := 'Connection timed Out.';
10061 : ErrorMsg := 'Connection refused.';
10064 : ErrorMsg := 'Host Is down.';
10065 : ErrorMsg := 'No route To host.';
10067 : ErrorMsg := 'Too many processes.';
10091 : ErrorMsg := 'Network subsystem Is unavailable.';
10092 : ErrorMsg := 'WINSOCK.DLL version Out Of range.';
10093 : ErrorMsg := 'Successful WSAStartup Not yet performed.';
10094 : ErrorMsg := 'Graceful shutdown In progress.';
11001 : ErrorMsg := 'Host Not found.';
11002 : ErrorMsg := 'Non-authoritative host Not found.';
11003 : ErrorMsg := 'This Is a non-recoverable error.';
11004 : ErrorMsg := 'Valid name, no data Record Of requested Type.';
Else
// erreur inconnue
ErrorMsg := 'Unknown socket error.';
End;
// mise en forme de la signification de l'erreur
ErrorMsg := 'Socket Error n°' + IntToStr(ErrorCode) + ' : ' + ErrorMsg;
// l'erreur est traitée
ErrorCode := 0;
// définition du type d'erreur
Case ErrorEvent Of
eeSend : ErrorMsg := 'Writing ' + ErrorMsg;
eeReceive : ErrorMsg := 'Reading ' + ErrorMsg;
eeConnect : ErrorMsg := 'Connecting ' + ErrorMsg;
eeDisconnect : ErrorMsg := 'Disconnecting ' + ErrorMsg;
eeAccept : ErrorMsg := 'Accepting ' + ErrorMsg;
Else
// erreur inconnue
ErrorMsg := 'Unknown ' + ErrorMsg;
End;
Result:=ErrorMsg;
End;
//
//**************************************************************************************
//**************************************************************************************
// Structure de la trame utilisée pour les échanges
//
Const
TailleBloc = 256;
Type
TIPTrame=Packed Record
Case ttType:Integer Of // ttType précise le type de la trame
// =1 quand c'est un nom de fichier
// =2 quand c'est un morceau du fichier
1:(
ttNomFichier:String[255]; // Nom du fichier
);
2:( ttDebut : Integer; // Adresse de début des données dans le fichier
ttLong : Integer; // Longueur des données envoyées
ttDatas : Array[0..TailleBloc-1] Of Byte ) // Données envoyées
End;
//
//**************************************************************************************
//**************************************************************************************
// SERVEUR
// Cette partie ne concerne que le serveur
Var
ServeurEnReception : Boolean False; //True quand une réception est en cours
ServeurAdresseClient : String = ''; // Adresse du client connecté
ServeurFichier : File; // Fichier utilisé pour la sauvegarde des morceaux
Procedure TForm1.btnOpenClick(Sender: TObject);
Begin
// Bouton de mise en écoute du serveur, c'est tout simple
Serveur.Open;
MemoServeur.Lines.Add('Serveur en écoute');
End;
Procedure TForm1.btnCloseClick(Sender: TObject);
Begin
// Bouton de fermeture du serveur, c'est aussi tout simple
If ServeurEnReception
And (MessageDlg('Un transfert est en cours, fermer le serveur ?',mtConfirmation,[mbYes,mbNo],0)=mrYes)
Then Begin
Serveur.Close;
MemoServeur.Lines.Add('Serveur desactivé');
End;
End;
//
// évènements du socket serveur
//
Procedure TForm1.ServeurClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
Begin
// C'est juste pour signaler
MemoServeur.Lines.Add('OnConnect:'+Socket.RemoteAddress);
End;
Procedure TForm1.ServeurClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
Begin
MemoServeur.Lines.Add('OnDisconnect:'+Socket.RemoteAddress);
// Le client se deconnecte => ou ferme le fichier en cours si besoin
If ServeurEnReception
Then Begin
CloseFile(ServeurFichier);
ServeurEnReception:=False;
End;
End;
Procedure TForm1.ServeurClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
Var ErrorCode: Integer);
Begin
// On ne fait que signaler l'erreur
MemoServeur.Lines.Add('OnClientError:'+Socket.RemoteAddress+#13+MessageErreurSocket(ErrorEvent,ErrorCode));
End;
Procedure TForm1.ServeurClientRead(Sender: TObject;
Socket: TCustomWinSocket);
Var Buffer:TIPTrame;
Erreur:Integer;
Recus :Integer;
Begin
// C'est ici la partie principale du serveur
// Cette procédure est appelée à chaque écriture d'un client
Erreur :=0;
Recus :=Socket.ReceiveLength; // Longueur reçue ( en octets )
MemoServeur.Lines.Add('OnRead'+Socket.RemoteAddress+' reçus '+IntToStr(Recus));
If Recus<= SizeOf(Buffer) // On vérifie que la longueur reçue tient dans la trame
// sinon attention au plantage !!!
Then With Buffer Do Begin
// Lecture de la trame reçue
Socket.ReceiveBuf(Buffer,Recus);
// En fonction du type de la trame on effectue les traitements
Case ttType Of
1:Begin
// C'est une nouvelle demande, on vérifie le nom du fichier
// La longueur de la trame doit être au minimumu de
// 4 ( taille de ttType ) + 1 ( longueur de la chaine ttNomFichier ) + Length(ttNomFichier)
If (Recus>=5)And(Recus>=(5+Length(ttNomFichier)))
Then Begin
// La longueur est bonne, on accepte la demande
MemoServeur.Lines.Add(ttNomFichier);
// On ferme le fichier précédent au cas ou
If ServeurEnReception Then CloseFile(ServeurFichier);
// On ouvre le fichier de réception en écriture
AssignFile(ServeurFichier,ExtractFilePath(ParamStr(0))+ttNomFichier);
Try
Rewrite(ServeurFichier,1);
ServeurEnReception:=True;
Erreur:=0;
Except
Erreur:=5; // Erreur de création du fichier
End;
End
Else Erreur:=2; // La longueur reçue est trop courte
End;
2:Begin
// On reçoit un morceau de fichier
// La longueur de la trame doit être au minimumu de
// 4 ( taille de ttType ) + 4 ( taille de ttDebut ) + 4 ( taille de ttLong )
// + ttLong ( nombre de données envoyées )
If (Recus>=12)And(Recus>=(12+ttLong))
Then Begin
// Le morceau n'est accepté que si une demande est en cours
If ServeurEnReception
Then Begin
// Le morceau n'est accepté que si le début du fichier à déjà été reçu
If (ttDebut>=0)And(ttDebut<=FileSize(ServeurFichier))
Then Begin
Try
// Si tout est bon on écrit le morceau dans le fichier
Seek(ServeurFichier,ttDebut);
BlockWrite(ServeurFichier,ttDatas,ttLong);
Erreur:=0; // C'est bon
Except
Erreur:=6; // Erreur d'écriture du fichier
End;
End
Else Erreur:=4; // La position 'début' n'est pas correcte
End
Else Erreur:=3; // On n'a pas reçue de demande
End
Else Erreur:=2; // La longueur reçue est trop courte
End;
End;// fin du case
End
Else Erreur:=1; // La longueur reçue est trop grande
// Dans tout les cas on envoie le code d'erreur au client
Socket.SendBuf(Erreur,4);
MemoServeur.Lines.Add(' Code de retour :'+IntToStr(Erreur));
End;
// fin de la partie serveur
//**************************************************************************************
//**************************************************************************************
// CLIENT
// Cette partie ne concerne que le client
Var
ClientFichier : File; // Fichier en cours d'envoi
ClientTrame : TIPTrame; // Copie de la dernière trame envoyée
Procedure TForm1.btnEnvoyerClick(Sender: TObject);
Begin
// Bouton envoyer, c'est le début
// On demande bien sur en premier le nom de fichier à transférer
If Not Ouvrir.Execute Then Exit;
AssignFile(ClientFichier,Ouvrir.FileName);
// On ouvre le fichier en lecture
Reset(ClientFichier,1);
// On essaye de se connecter au serveur
Client.Address:=Edit1.Text;
Client.Open;
End;
Procedure TForm1.ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
Begin
// La connection est réussie
MemoClient.Lines.Add('OnConnect:'+Socket.RemoteAddress);
// On demande alors l'envoi de la trame d'entête
ClientTrame.ttType:=1;
ClientTrame.ttNomFichier:=ExtractFileName(Ouvrir.FileName);
// La demande est différée par un petit timer car il est interdit
// d'écrire dans un OnConnect
btnEnvoyer.Enabled:=False;
RetardDebut.Enabled:=True;
End;
Procedure TForm1.RetardDebutTimer(Sender: TObject);
Begin
// Cette demande n'est faite qu'une fois par fichier
RetardDebut.Enabled:=False;
// On envoie au serveur la trame avec le nom du fichier
Client.Socket.SendBuf(ClientTrame,4+Length(ClientTrame.ttNomFichier)+1);
MemoClient.Lines.add('Envoi de l''entête');
End;
Procedure EnvoiBlocEnCours;
Begin
With ClientTrame Do
Begin
// Procédure d'envoi d'un morceau de fichier
// est appelée par ClientRead
// C'est une trame de type morceau de fichier
ttType:=2;
// Lecture dans le fichier
Seek(ClientFichier,ttDebut);
BlockRead(ClientFichier,ttDatas,TailleBloc,ttLong);
// Envoi du morceau
// La longueur envoyée est
// 4 ( taille de ttType ) + 4 ( taille de ttDebut ) + 4 ( taille de ttLong )
// + ttLong ( nombre de données envoyé )
Form1.Client.Socket.SendBuf(ClientTrame,ttLong+12);
Form1.MemoClient.Lines.add('Envoi du morceau '+IntToStr(ttDebut)+'['+IntToStr(ttLong)+']');
End;
End;
Procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
Var CodeRetour:Integer;
Begin
With ClientTrame Do
Begin
// On reçoit le code d'erreur du serveur, on le traite suivant les cas
MemoClient.Lines.Add('OnRead:'+Socket.RemoteAddress);
Socket.ReceiveBuf(CodeRetour,4);
Case ttType Of
1:Begin
// La dernière demande était un nom de fichier
// On teste le code de retour
If CodeRetour=0
Then Begin
// Par d'erreur, l'envoi réel peut donc commencer
ttDebut:=0;
EnvoiBlocEnCours;
End
Else Begin
// Le serveur refuse le fichier demandé => on arrète
MemoClient.Lines.Add('Erreur '+IntToStr(CodeRetour));
Client.Close;
End;
End;
2:Begin
// La dernière demande était un morceau de fichier
// On teste le code de retour
If CodeRetour=0
Then Begin
// Pas d'erreur on avance dans le fichier de la longueur envoyée précédemment
Inc(ttDebut,ttLong);
If ttDebut>=FileSize(ClientFichier)
Then Client.Close // C'est la fin du fichier, on ferme la connection
Else EnvoiBlocEnCours; // ce n'est pas la fin, on envoie le morceau
End
Else Begin
// Une erreur à eut lieu, on envoie le même morceau
MemoClient.Lines.Add('Erreur '+IntToStr(CodeRetour));
EnvoiBlocEnCours;
End;
End;
Else
// Normalement on ne doit pas passer ici.
// au cas ou, on ferme la connection
Client.Close;
End;
End;
End;
Procedure TForm1.ClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
Begin
// à la déconnection on ferme le fichier est cours de lecture
MemoClient.Lines.Add('OnDisonnect:'+Socket.RemoteAddress);
btnEnvoyer.Enabled:=True;
CloseFile(ClientFichier);
End;
Procedure TForm1.ClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; Var ErrorCode: Integer);
Begin
// On ne fait que signaler l'erreur
MemoServeur.Lines.Add('OnClientError:'+Socket.RemoteAddress+#13+MessageErreurSocket(ErrorEvent,ErrorCode));
End;
Procedure TForm1.btnStopClick(Sender: TObject);
Begin
// Arrêt du transfert en cours
RetardDebut.Enabled:=False;
Client.Close;
End;
// fin de la partie client
//**************************************************************************************
End.
///////////////////////////////////
Voila deux sources (Indy et Socket) qui font la meme chose : a ton avis c'est laquelle la source la plus simple ???
A+