Parler de l'application du "flow" dans la programmation Delphi
Chen Jingtao
Qu'est-ce qu'un flux ? Stream, en termes simples, est un outil de traitement de données abstrait basé sur l'orientation objet. Dans le flux, certaines opérations de base pour le traitement des données sont définies, telles que la lecture des données, l'écriture des données, etc. Le programmeur effectue toutes les opérations sur le flux sans se soucier de la direction réelle du flux des données à l'autre extrémité du flux. Les flux peuvent non seulement traiter des fichiers, mais également de la mémoire dynamique, des données réseau et d'autres formes de données. Si vous maîtrisez très bien les opérations de flux et utilisez la commodité des flux dans votre programme, l'efficacité de l'écriture de programmes sera grandement améliorée.
Ci-dessous, l'auteur utilise quatre exemples : chiffreur de fichiers EXE, carte de vœux électronique, OICQ fait maison et transmission d'écran réseau pour illustrer l'utilisation du « flux » dans la programmation Delphi. Certaines des techniques présentées dans ces exemples étaient autrefois secrètes pour de nombreux logiciels et non accessibles au public. Désormais, tout le monde peut citer directement le code, gratuitement.
"De grands bâtiments sortent du sol." Avant d'analyser les exemples, comprenons d'abord les concepts et les fonctions de base du flux. Ce n'est qu'après avoir compris ces éléments de base que nous pourrons passer à l'étape suivante. Assurez-vous de bien comprendre ces méthodes de base. Bien entendu, si vous les connaissez déjà, vous pouvez sauter cette étape.
1. Concepts de base et déclarations de fonctions des flux dans Delphi
Dans Delphi, la classe de base de tous les objets flux est la classe TStream, qui définit les propriétés et méthodes communes de tous les flux.
Les propriétés définies dans la classe TStream sont présentées comme suit :
1. Taille : Cette propriété renvoie la taille des données dans le flux en octets.
2. Position : cet attribut contrôle la position du pointeur d'accès dans le flux.
Il existe quatre méthodes virtuelles définies dans Tstream :
1. Lire : Cette méthode lit les données du flux. Le prototype de fonction est :
Fonction Read(var Buffer;Count:Longint):Longint;virtuel;abstrait;
Le paramètre Buffer est le tampon placé lors de la lecture des données. Count est le nombre d'octets de données à lire. La valeur de retour de cette méthode est le nombre réel d'octets lus, qui peut être inférieur ou égal à la valeur spécifiée dans. Compter.
2. Write : Cette méthode écrit les données dans le flux. Le prototype de fonction est :
Fonction Write(var Buffer;Count:Longint):Longint;virtuel;abstrait;
Le paramètre Buffer est le tampon des données à écrire dans le flux, Count est la longueur des données en octets et la valeur de retour de cette méthode est le nombre d'octets réellement écrits dans le flux.
3. Recherche : Cette méthode implémente le mouvement du pointeur de lecture dans le flux. Le prototype de fonction est :
Recherche de fonction (Offset:Longint;Origint:Word):Longint;virtuel;abstrait;
Le paramètre Offset est le nombre d'octets de décalage, et le paramètre Origin indique la signification réelle de Offset. Ses valeurs possibles sont les suivantes :
soFromBeginning:Offset est la position du pointeur depuis le début des données après le mouvement. À ce moment, le décalage doit être supérieur ou égal à zéro.
soFromCurrent:Offset est la position relative du pointeur après le mouvement et du pointeur actuel.
soFromEnd:Offset est la position du pointeur à partir de la fin des données après le mouvement. À ce moment, le décalage doit être inférieur ou égal à zéro. La valeur de retour de cette méthode est la position du pointeur après le mouvement.
4. Setsize : Cette méthode modifie la taille des données. Le prototype de fonction est :
Fonction Setsize(NewSize:Longint);virtuel;
De plus, plusieurs méthodes statiques sont également définies dans la classe TStream :
1. ReadBuffer : La fonction de cette méthode est de lire les données à partir de la position actuelle dans le flux. Le prototype de fonction est :
PRocédure ReadBuffer(var Buffer;Count:Longint);
La définition des paramètres est la même que celle indiquée ci-dessus. Remarque : Lorsque le nombre d'octets de données lus n'est pas le même que le nombre d'octets à lire, une exception EReadError est générée.
2. WriteBuffer : La fonction de cette méthode est d'écrire des données dans le flux à la position actuelle. Le prototype de fonction est :
Procédure WriteBuffer(var Buffer;Count:Longint);
La définition des paramètres est la même que celle de Write ci-dessus. Remarque : Lorsque le nombre d'octets de données écrits n'est pas le même que le nombre d'octets à écrire, une exception EWriteError sera générée.
3. CopyFrom : Cette méthode est utilisée pour copier des flux de données à partir d’autres flux. Le prototype de fonction est :
Fonction CopyFrom(Source:TStream;Count:Longint):Longint;
Le paramètre Source est le flux qui fournit les données et Count est le nombre d'octets de données copiés. Lorsque Count est supérieur à 0, CopyFrom copie Count octets de données à partir de la position actuelle du paramètre Source ; lorsque Count est égal à 0, CopyFrom définit la propriété Position du paramètre Source sur 0, puis copie toutes les données de la Source. ;
TStream possède d'autres classes dérivées, dont la plus couramment utilisée est la classe TFileStream. Pour utiliser la classe TFileStream pour accéder aux fichiers, vous devez d'abord créer une instance. La déclaration est la suivante :
constructeur Create(const Filename:string;Mode:Word);
Le nom de fichier est le nom du fichier (y compris le chemin) et le paramètre Mode est le moyen d'ouvrir le fichier, qui inclut le mode d'ouverture du fichier et le mode de partage. Ses valeurs et significations possibles sont les suivantes :
Mode ouvert :
fmCreate : créez un fichier avec le nom de fichier spécifié ou ouvrez le fichier s'il existe déjà.
fmOpenRead : ouvre le fichier spécifié en mode lecture seule
fmOpenWrite : ouvre le fichier spécifié en mode écriture seule
fmOpenReadWrite : ouvre le fichier spécifié en écriture
Mode de partage :
fmShareCompat : le mode de partage est compatible avec les FCB
fmShareExclusive : n'autorisez en aucun cas d'autres programmes à ouvrir le fichier
fmShareDenyWrite : ne pas autoriser d'autres programmes à ouvrir le fichier en écriture
fmShareDenyRead : ne pas autoriser d'autres programmes à ouvrir le fichier en mode lecture
fmShareDenyNone : d'autres programmes peuvent ouvrir le fichier de n'importe quelle manière
TStream possède également une classe dérivée, TMemoryStream, qui est utilisée très fréquemment dans les applications réelles. C'est ce qu'on appelle un flux mémoire, ce qui signifie créer un objet flux en mémoire. Ses méthodes et fonctions de base sont les mêmes que ci-dessus.
Eh bien, avec les bases ci-dessus en place, nous pouvons commencer notre parcours de programmation.
-------------------------------------------------- --------------------------
2. Première application pratique : utiliser des flux pour créer des chiffreurs de fichiers EXE, des bundles, des fichiers auto-extractibles et des programmes d'installation.
Parlons d'abord de la façon de créer un chiffreur de fichiers EXE.
Le principe du chiffreur de fichiers EXE : Créez deux fichiers, l'un sert à ajouter des ressources à l'autre fichier EXE, appelé programme complémentaire. Un autre fichier EXE ajouté est appelé fichier d'en-tête. La fonction de ce programme est de lire les fichiers ajoutés à lui-même. La structure des fichiers EXE sous Windows est relativement complexe et certains programmes ont également des sommes de contrôle. Lorsqu'ils découvrent qu'ils ont été modifiés, ils penseront qu'ils sont infectés par un virus et refuseront de s'exécuter. Nous ajoutons donc le fichier à notre propre programme afin que la structure du fichier d'origine ne soit pas modifiée. Écrivons d'abord une fonction d'ajout. La fonction de cette fonction est d'ajouter un fichier sous forme de flux à la fin d'un autre fichier. La fonction est la suivante :
Fonction Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Cible, Source : TFileStream ;
MyFileSize:entier;
commencer
essayer
Source:=TFileStream.Create(SourceFile,fmOpenRead ou fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite ou fmShareExclusive);
essayer
Target.Seek(0,soFromEnd);//Ajouter des ressources à la fin
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//Calculez la taille de la ressource et écrivez-la à la fin du processus auxiliaire
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
enfin
Cible.Gratuit ;
Source.Gratuit ;
fin;
sauf
Résultat :=Faux ;
Sortie;
fin;
Résultat :=Vrai ;
fin;
Avec les bases ci-dessus, nous devrions facilement comprendre cette fonction. Le paramètre SourceFile est le fichier à ajouter et le paramètre TargetFile est le fichier cible à ajouter. Par exemple, pour ajouter a.exe à b.exe : Cjt_AddtoFile('a.exe',b.exe'); Si l'ajout réussit, il renverra True, sinon il renverra False.
Sur la base de la fonction ci-dessus, nous pouvons écrire la fonction de lecture opposée :
Fonction Cjt_LoadFromFile(SourceFile,TargetFile:string):Boolean;
var
Source : TFileStream ;
Cible : TMemoryStream ;
MyFileSize:entier;
commencer
essayer
Cible :=TMemoryStream.Create ;
Source:=TFileStream.Create(SourceFile,fmOpenRead ou fmShareDenyNone);
essayer
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//Lire la taille de la ressource
Source.Seek(-MyFileSize,soFromEnd);//Localiser l'emplacement de la ressource
Target.CopyFrom(Source,MyFileSize-sizeof(MyFileSize));//Supprimer les ressources
Target.SaveToFile(TargetFile);//Enregistrer dans un fichier
enfin
Cible.Gratuit ;
Source.Gratuit ;
fin;
sauf
Résultat :=faux ;
Sortie;
fin;
Résultat :=vrai ;
fin;
Le paramètre SourceFile est le nom du fichier auquel le fichier a été ajouté et le paramètre TargetFile est le nom du fichier cible enregistré après la suppression du fichier. Par exemple, Cjt_LoadFromFile('b.exe','a.txt'); extrait le fichier dans b.exe et l'enregistre sous a.txt. Si l’extraction réussit, elle renvoie True sinon elle renvoie False.
Ouvrez Delphi, créez un nouveau projet et placez un contrôle d'édition Edit1 et deux boutons sur la fenêtre : Button1 et Button2. La propriété Caption du bouton est définie respectivement sur « OK » et « Annuler ». Écrivez du code dans l'événement Click de Button1 :
var S : chaîne ;
commencer
S:=ChangeFileExt(application.ExeName,'.Cjt');
si Edit1.Text='790617' alors
commencer
Cjt_LoadFromFile(Application.ExeName,S);
{Sortez le fichier et enregistrez-le dans le chemin actuel et nommez-le "fichier original.Cjt"}
Winexec(pchar(S),SW_Show);{Exécuter "fichier original.Cjt"}
Application.Terminate ; {Quitter le programme}
fin
autre
Application.MessageBox('Le mot de passe est incorrect, veuillez le saisir à nouveau !', 'Le mot de passe est incorrect', MB_ICONERROR+MB_OK);
Compilez ce programme et renommez le fichier EXE en head.exe. Créez un nouveau fichier texte head.rc avec le contenu : head exefile head.exe, puis copiez-le dans le répertoire BIN de Delphi, exécutez la commande Dos Brcc32.exe head.rc, et un fichier head.res sera généré. file est de conserver les fichiers de ressources que nous voulons en premier.
Notre fichier d'en-tête a été créé, créons le programme complémentaire.
Créez un nouveau projet et placez les contrôles suivants : un Edit, un Opendialog et les propriétés Caption des deux Button1 sont définis respectivement sur "Sélectionner un fichier" et "Encryption". Ajoutez une phrase dans le programme source : {$R head.res} et copiez le fichier head.res dans le répertoire courant du programme. De cette façon, le head.exe vient d'être compilé avec le programme.
Écrivez le code dans l'événement Cilck de Button1 :
si OpenDialog1.Execute alors Edit1.Text:=OpenDialog1.FileName ;
Écrivez le code dans l'événement Cilck de Button2 :
var S:Chaîne;
commencer
S:=ExtractFilePath(Edit1.Text);
si ExtractRes('exefile','head',S+'head.exe') alors
si Cjt_AddtoFile(Edit1.Text,S+'head.exe') alors
si SupprimerFichier(Edit1.Text) alors
si RenameFile(S+'head.exe',Edit1.Text) alors
Application.MessageBox('Cryptage du fichier réussi !','Message',MB_ICONINFORMATION+MB_OK)
autre
commencer
si FileExists(S+'head.exe') alors DeleteFile(S+'head.exe');
Application.MessageBox('Échec du cryptage du fichier !','Message',MB_ICONINFORMATION+MB_OK)
fin;
fin;
Parmi eux, ExtractRes est une fonction personnalisée, utilisée pour extraire head.exe du fichier de ressources.
Fonction ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
commencer
essayer
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
essayer
Res.SavetoFile(ResNewName);
Résultat :=vrai ;
enfin
Res.Gratuit ;
fin;
sauf
Résultat :=faux ;
fin;
fin;
Remarque : Notre fonction ci-dessus ajoute simplement un fichier à la fin d'un autre fichier. Dans les applications réelles, elle peut être modifiée pour ajouter plusieurs fichiers, à condition que l'adresse de décalage soit définie en fonction de la taille et du nombre réels. Par exemple, un regroupeur de fichiers ajoute deux programmes ou plus à un fichier d'en-tête. Les principes des programmes auto-extractibles et des installateurs sont les mêmes, mais avec plus de compression. Par exemple, nous pouvons référencer une unité LAH, compresser le flux puis l'ajouter, afin que le fichier devienne plus petit. Décompressez-le simplement avant de le lire. De plus, l'exemple du chiffreur EXE dans l'article présente encore de nombreuses imperfections. Par exemple, le mot de passe est fixé sur "790617", et après avoir retiré le fichier EXE et l'avoir exécuté, il doit être supprimé une fois son exécution terminée, etc. . Les lecteurs peuvent le modifier eux-mêmes.
-------------------------------------------------- -------------------
3. Application pratique 2 : Utiliser des flux pour créer des cartes de vœux électroniques exécutables
Nous voyons souvent des logiciels de production de cartes de vœux électroniques qui vous permettent de sélectionner vous-même des images, puis de générer un fichier exécutable EXE pour vous. Lorsque vous ouvrez la carte de vœux, l'image s'affichera pendant la lecture de musique. Maintenant que nous avons appris les opérations de flux, nous pouvons également en créer une.
Dans le processus d'ajout d'images, nous pouvons utiliser directement le précédent Cjt_AddtoFile, et ce que nous devons faire maintenant, c'est comment lire et afficher les images. Nous pouvons utiliser le Cjt_LoadFromFile précédent pour lire d'abord l'image, l'enregistrer en tant que fichier, puis la charger. Cependant, il existe un moyen plus simple, qui consiste à lire directement le flux de fichiers et à l'afficher avec le puissant outil de stream. , tout devient simple.
Les images les plus populaires de nos jours sont le format BMP et le format JPG. Nous allons maintenant écrire des fonctions de lecture et d'affichage pour ces deux types d'images.
Fonction Cjt_BmpLoad(ImgBmp:TImage;SourceFile:String):Boolean;
var
Source : TFileStream ;
MyFileSize:entier;
commencer
Source:=TFileStream.Create(SourceFile,fmOpenRead ou fmShareDenyNone);
essayer
essayer
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));//Lire les ressources
Source.Seek(-MyFileSize,soFromEnd);//Localiser la position de départ de la ressource
ImgBmp.Picture.Bitmap.LoadFromStream(Source);
enfin
Source.Gratuit ;
fin;
sauf
Résultat :=Faux ;
Sortie;
fin;
Résultat :=Vrai ;
fin;
Ce qui précède est une fonction de lecture d'images BMP, et ce qui suit est une fonction de lecture d'images JPG. Étant donné que l'unité JPG est utilisée, une phrase : utilise jpeg doit être ajoutée au programme.
Fonction Cjt_JpgLoad(JpgImg:Timage;SourceFile:String):Boolean;
var
Source : TFileStream ;
MyFileSize:entier;
Monjpg : TJpegImage ;
commencer
essayer
Monjpg := TJpegImage.Create ;
Source:=TFileStream.Create(SourceFile,fmOpenRead ou fmShareDenyNone);
essayer
Source.Seek(-sizeof(MyFileSize),soFromEnd);
Source.ReadBuffer(MyFileSize,sizeof(MyFileSize));
Source.Seek(-MyFileSize,soFromEnd);
Monjpg.LoadFromStream(Source);
JpgImg.Picture.Bitmap.Assign(Monjpg);
enfin
Source.Gratuit ;
Monjpg.free;
fin;
sauf
Résultat :=faux ;
Sortie;
fin;
Résultat :=vrai ;
fin;
Avec ces deux fonctions, nous pouvons réaliser un programme de lecture. Prenons l'exemple des photos BMP :
Exécutez Delphi, créez un nouveau projet et placez un contrôle d'affichage d'image Image1. Écrivez simplement la phrase suivante dans l'événement Create de la fenêtre :
Cjt_BmpLoad(Image1,Application.ExeName);
Il s'agit du fichier d'en-tête, puis nous utilisons la méthode précédente pour générer un fichier de ressources head.res.
Nous pouvons maintenant commencer à créer notre programme complémentaire. Le code complet est le suivant :
unité Unité1 ;
interface
utilise
Windows, messages, SysUtils, classes, graphiques, contrôles, formulaires, boîtes de dialogue,
ExtCtrls, StdCtrls, ExtDlgs ;
taper
TForm1 = classe(TForm)
Edit1 : TEdit ;
Bouton1 : TButton ;
Bouton2 : TButton ;
OpenPictureDialog1 : TOpenPictureDialog ;
procédure FormCreate(Expéditeur : TObject);
procédure Button1Click (Expéditeur : TObject);
procédure Button2Click (Expéditeur : TObject);
privé
Fonction ExtractRes(ResType, ResName, ResNewName : String):boolean;
Fonction Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
{Déclarations privées}
publique
{Déclarations publiques}
fin;
var
Formulaire1 : TForm1 ;
mise en œuvre
{$R *.DFM}
Fonction TForm1.ExtractRes(ResType, ResName, ResNewName : String):boolean;
var
Res : TResourceStream;
commencer
essayer
Res := TResourceStream.Create(Hinstance, Resname, Pchar(ResType));
essayer
Res.SavetoFile(ResNewName);
Résultat :=vrai ;
enfin
Res.Gratuit ;
fin;
sauf
Résultat :=faux ;
fin;
fin;
Fonction TForm1.Cjt_AddtoFile(SourceFile,TargetFile:string):Boolean;
var
Cible, Source : TFileStream ;
MyFileSize:entier;
commencer
essayer
Source:=TFileStream.Create(SourceFile,fmOpenRead ou fmShareExclusive);
Target:=TFileStream.Create(TargetFile,fmOpenWrite ou fmShareExclusive);
essayer
Target.Seek(0,soFromEnd);//Ajouter des ressources à la fin
Target.CopyFrom(Source,0);
MyFileSize:=Source.Size+Sizeof(MyFileSize);//Calculez la taille de la ressource et écrivez-la à la fin du processus auxiliaire
Target.WriteBuffer(MyFileSize,sizeof(MyFileSize));
enfin
Cible.Gratuit ;
Source.Gratuit ;
fin;
sauf
Résultat :=Faux ;
Sortie;
fin;
Résultat :=Vrai ;
fin;
procédure TForm1.FormCreate(Expéditeur : TObject);
commencer
Caption:='Programme de démonstration Bmp2Exe Auteur : Chen Jingtao';
Edit1.Text:='';
OpenPictureDialog1.DefaultExt := GraphicExtension(TBitmap);
OpenPictureDialog1.Filter := GraphicFilter(TBitmap);
Button1.Caption:='Sélectionner une image BMP';
Button2.Caption:='Générer EXE';
fin;
procédure TForm1.Button1Click(Expéditeur : TObject);
commencer
si OpenPictureDialog1.Execute alors
Edit1.Text:=OpenPictureDialog1.FileName;
fin;
procédure TForm1.Button2Click(Expéditeur : TObject);
var
HeadTemp : Chaîne ;
commencer
si ce n'est pas FileExists (Edit1.Text), alors
commencer
Application.MessageBox('Le fichier image BMP n'existe pas, veuillez sélectionner à nouveau !','Message',MB_ICONINFORMATION+MB_OK)
Sortie;
fin;
HeadTemp:=ChangeFileExt(Edit1.Text,'.exe');
si ExtractRes('exefile','head',HeadTemp) alors
si Cjt_AddtoFile(Edit1.Text,HeadTemp) alors
Application.MessageBox('Fichier EXE généré avec succès !','Message',MB_ICONINFORMATION+MB_OK)
autre
commencer
si FileExists (HeadTemp) alors DeleteFile (HeadTemp);
Application.MessageBox('La génération du fichier EXE a échoué !','Message',MB_ICONINFORMATION+MB_OK)
fin;
fin;
fin.
Et ça ? C'est incroyable :) Rendez l'interface du programme plus belle et ajoutez quelques fonctions, et vous constaterez qu'il n'est pas très inférieur aux logiciels qui nécessitent un enregistrement.
-------------------------------------------------- --------------------------
Troisième application pratique : Utilisez des flux pour créer votre propre OICQ
OICQ est un logiciel de communication en ligne en temps réel développé par la société Shenzhen Tencent et compte une large base d'utilisateurs en Chine. Cependant, l'OICQ doit être connecté à Internet et au serveur de Tencent avant de pouvoir être utilisé. Nous pouvons donc en écrire un nous-mêmes et l'utiliser sur le réseau local.
L'OICQ utilise le protocole UDP, qui est un protocole sans connexion, c'est-à-dire que les parties communicantes peuvent envoyer des informations sans établir de connexion, l'efficacité est donc relativement élevée. Le contrôle NMUDP de FastNEt fourni avec Delphi lui-même est un contrôle de datagramme utilisateur du protocole UDP. Cependant, il convient de noter que si vous utilisez ce contrôle, vous devez quitter le programme avant d'éteindre l'ordinateur, car le contrôle TNMXXX présente un BUG. Le ThreadTimer utilisé par PowerSocket, base de tous les contrôles nm, utilise une fenêtre cachée (classe TmrWindowClass) pour gérer les failles.
Ce qui n'a pas fonctionné :
Psock :: TThreadTimer :: WndProc (var msg: TMessage)
si msg.message=WM_TIMER alors
Il s'en charge lui-même
msg.résultat :=0
autre
msg.result:=DefWindowProc(0,....)
fin
Le problème est que lors de l'appel de DefWindowProc, le paramètre HWND transmis est en fait une constante 0, donc en fait DefWindowProc ne peut pas fonctionner. L'appel à n'importe quel message d'entrée renvoie 0, y compris WM_QUERYENDsession, donc Windows ne peut pas se fermer. En raison de l'appel anormal de DefWindowProc, en effet, à l'exception de WM_TIMER, les autres messages traités par DefWindowProc ne sont pas valides.
La solution est dans PSock.pas
Dans TThreadTimer.Wndproc
Résultat := DefWindowProc( 0, Msg, WPARAM, LPARAM );
Remplacer par :
Résultat := DefWindowProc( FWindowHandle, Msg, WPARAM, LPARAM );
Les premières versions de bas niveau d'OICQ présentaient également ce problème. Si OICQ n'était pas éteint, l'écran clignotait pendant un moment, puis revenait lorsque l'ordinateur était éteint.
Bon, sans plus tarder, écrivons notre OICQ. Il s'agit en fait d'un exemple fourni avec Delphi :)
Créez un nouveau projet, faites glisser un contrôle NMUDP du panneau FASTNET vers la fenêtre, puis placez trois EDIT nommés Editip, EditPort, EditMyTxt, trois boutons BtSend, BtClear, BtSave, un MEMOMemoReceive, un SaveDialog et une barre d'état. Lorsque l'utilisateur clique sur BtSend, un objet de flux mémoire est créé, les informations textuelles à envoyer sont écrites dans le flux mémoire, puis NMUDP envoie le flux. Lorsque NMUDP reçoit des données, son événement DataReceived est déclenché Ici, nous convertissons le flux reçu en informations de caractères, puis l'affichons.
Remarque : N'oubliez pas de libérer (Free) tous les objets de flux après leur création et leur utilisation. En fait, son destructeur doit être Destroy. Cependant, si la création du flux échoue, l'utilisation de Destroy générera une exception. vérifiera d'abord si si le flux n'est pas établi avec succès, il ne sera publié que s'il est établi, il est donc plus sûr d'utiliser Free.
Dans ce programme, nous utilisons le contrôle NMUDP, qui possède plusieurs propriétés importantes. RemoteHost représente l'adresse IP ou le nom de l'ordinateur distant, et LocalPort est le port local, qui surveille principalement s'il y a des données entrantes. Le RemotePort est un port distant et les données sont envoyées via ce port lors de l'envoi de données. Les comprendre peut déjà comprendre notre programme.
Le code complet est le suivant :
unité Unité1 ;
interface
utilise
Windows, messages, SysUtils, classes, graphiques, contrôles, formulaires, boîtes de dialogue, StdCtrls, ComCtrls, NMUDP ;
taper
TForm1 = classe(TForm)
NMUDP1 : TNMUDP ;
EditIP : TEdit ;
EditPort : TEdit ;
EditMyTxt : TEdit ;
MémoReceive : TMemo ;
BtEnvoyer : TButton ;
BtClear : TButton ;
BtSave : TButton ;
StatusBar1 : TStatusBar ;
SaveDialog1 : TSaveDialog ;
procédure BtSendClick(Expéditeur : TObject);
procédure NMUDP1DataReceived(Expéditeur : TComponent ; NumberBytes : Integer ;
FromIP : chaîne ; port : entier );
procédure NMUDP1InvalidHost (var gérée : booléenne);
procédure NMUDP1DataSend(Expéditeur : TObject);
procédure FormCreate(Expéditeur : TObject);
procédure BtClearClick(Expéditeur : TObject);
procédure BtSaveClick(Expéditeur : TObject);
procédure EditMyTxtKeyPress (Expéditeur : TObject ; var Key : Char);
privé
{Déclarations privées}
publique
{Déclarations publiques}
fin;
var
Formulaire1 : TForm1 ;
mise en œuvre
{$R *.DFM}
procédure TForm1.BtSendClick(Expéditeur : TObject);
var
Mon flux : TMemoryStream ;
MonEnvoyerTxt : chaîne ;
Iport,icode:entier;
Commencer
Val(EditPort.Text,Iport,icode);
si icode<>0 alors
commencer
Application.MessageBox('Le port doit être un numéro, veuillez le saisir à nouveau !','Message',MB_ICONINFORMATION+MB_OK);
Sortie;
fin;
NMUDP1.RemoteHost := EditIP.Text ; {hôte distant}
NMUDP1.LocalPort:=Iport ; {port local}
NMUDP1.RemotePort := Iport ; {port distant}
MonEnvoyerTxt := EditMonTxt.Text;
MyStream := TMemoryStream.Create; {Créer un flux}
essayer
MyStream.Write(MySendTxt[1], length(EditMyTxt.Text));{Écrire des données}
NMUDP1.SendStream(MyStream); {Envoyer le flux}
enfin
MyStream.Free ; {flux de version}
fin;
fin;
procédure TForm1.NMUDP1DataReceived(Expéditeur : TComponent;
NombreOctets : Entier ; FromIP : Chaîne ; Port : Entier);
var
Mon flux : TMemoryStream ;
MyReciveTxt : chaîne ;
commencer
MyStream := TMemoryStream.Create; {Créer un flux}
essayer
NMUDP1.ReadStream(MyStream);{Recevoir le flux}
SetLength(MyReciveTxt,NumberBytes);{NumberBytes est le nombre d'octets reçus}
MyStream.Read(MyReciveTxt[1],NumberBytes);{lire les données}
MemoReceive.Lines.Add('Informations reçues de l'hôte '+FromIP+':'+MyReciveTxt);
enfin
MyStream.Free ; {flux de version}
fin;
fin;
procédure TForm1.NMUDP1InvalidHost(var gérée : booléenne);
commencer
Application.MessageBox('L'adresse IP de l'autre partie est incorrecte, veuillez la saisir à nouveau !','Message',MB_ICONINFORMATION+MB_OK);
fin;
procédure TForm1.NMUDP1DataSend(Expéditeur : TObject);
commencer
StatusBar1.SimpleText:='Message envoyé avec succès !';
fin;
procédure TForm1.FormCreate(Expéditeur : TObject);
commencer
EditIP.Text:='127.0.0.1';
EditPort.Text:='8868';
BtSend.Caption:='Envoyer';
BtClear.Caption:='Effacer l'historique des discussions';
BtSave.Caption:='Enregistrer l'historique des discussions';
MemoReceive.ScrollBars:=ssBoth;
MemoReceive.Clear;
EditMyTxt.Text:='Entrez les informations ici et cliquez sur Envoyer.';
StatusBar1.SimplePanel:=true;
fin;
procédure TForm1.BtClearClick(Expéditeur : TObject);
commencer
MemoReceive.Clear;
fin;
procédure TForm1.BtSaveClick(Expéditeur : TObject);
commencer
si SaveDialog1.Execute alors MemoReceive.Lines.SaveToFile(SaveDialog1.FileName);
fin;
procédure TForm1.EditMyTxtKeyPress(Sender : TObject ; var Key : Char) ;
commencer
si Key=#13 alors BtSend.Click ;
fin;
fin.
Le programme ci-dessus est certainement loin derrière l'OICQ, car l'OICQ utilise la méthode de communication Socket5. Lorsqu'il se connecte, il récupère d'abord les informations sur les amis et l'état en ligne du serveur. Lors de l'envoi, il enregistre d'abord les informations sur le serveur, attend que l'autre partie se connecte la prochaine fois, puis l'envoie, puis supprime le. sauvegarde du serveur. Vous pouvez améliorer ce programme en fonction des concepts que vous avez appris précédemment. Par exemple, ajoutez un contrôle NMUDP pour gérer l'état en ligne. Les informations envoyées sont d'abord converties en code ASCII pour l'opération AND et ajoutent une information d'en-tête. réception des informations. Que l'en-tête des informations soit correct ou non, les informations ne seront déchiffrées et affichées que si elles sont correctes, améliorant ainsi la sécurité et la confidentialité.
De plus, un autre grand avantage du protocole UDP est qu'il peut diffuser, ce qui signifie que toute personne se trouvant dans le même segment de réseau peut recevoir des informations sans spécifier d'adresse IP spécifique. Les segments de réseau sont généralement divisés en trois catégories : A, B et C.
1~126.XXX.XXX.XXX (réseau de classe A) : l'adresse de diffusion est XXX.255.255.255
128~191.XXX.XXX.XXX (réseau de classe B) : l'adresse de diffusion est XXX.XXX.255.255
192~254.XXX.XXX.XXX (réseau de catégorie C) : l'adresse de diffusion est XXX.XXX.XXX.255
Par exemple, si trois ordinateurs sont 192.168.0.1, 192.168.0.10 et 192.168.0.18, lors de l'envoi d'informations, il vous suffit de spécifier l'adresse IP 192.168.0.255 pour réaliser la diffusion. Vous trouverez ci-dessous une fonction qui convertit l'IP en IP de diffusion. Utilisez-la pour améliorer votre propre OICQ^-^.
Fonction Trun_ip(S:string):string;
var s1,s2,s3,ss,sss,Tête : chaîne ;
n,m :entier ;
commencer
sss:=S;
n:=pos('.',s);
s1:=copie(s,1,n);
m:=longueur(s1);
supprimer(s,1,m);
Tête :=copie(s1,1,(longueur(s1)-1));
n:=pos('.',s);
s2:=copie(s,1,n);
m:=longueur(s2);
supprimer(s,1,m);
n:=pos('.',s);
s3:=copie(s,1,n);
m:=longueur(s3);
supprimer(s,1,m);
ss:=sss;
if strtoint(Head) in [1..126] then ss:=s1+'255.255.255'; //1~126.255.255.255 (réseau de classe A)
si strtoint(Head) dans [128..191] alors ss:=s1+s2+'255.255';//128~191.XXX.255.255 (réseau de classe B)
if strtoint(Head) dans [192..254] then ss:=s1+s2+s3+'255'; //192~254.XXX.XXX.255 (Réseau de catégorie)
Résultat :=ss ;
fin;
-------------------------------------------------- --------------------------
5. Application pratique 4 : Utiliser des flux pour transmettre des images d'écran sur le réseau
Vous devriez avoir vu de nombreux programmes de gestion de réseau. L'une des fonctions de ces programmes est de surveiller l'écran d'un ordinateur distant. En fait, cela est également réalisé à l’aide d’opérations de flux. Ci-dessous, nous donnons un exemple. Cet exemple est divisé en deux programmes, l'un est le serveur et l'autre est le client. Une fois le programme compilé, il peut être utilisé directement sur une seule machine, un réseau local ou Internet. Des commentaires correspondants ont été donnés dans le programme. Nous ferons une analyse détaillée plus tard.
Créez un nouveau projet et faites glisser un contrôle ServerSocket vers la fenêtre du panneau Internet. Ce contrôle est principalement utilisé pour surveiller le client et établir des connexions et des communications avec le client. Après avoir défini le port d'écoute, appelez la méthode Open ou Active:=True pour commencer à travailler. Remarque : Contrairement au NMUDP précédent, une fois que le Socket commence à écouter, son port ne peut pas être modifié. Si vous souhaitez le modifier, vous devez d'abord appeler Close ou définir Active sur False, sinon une exception se produira. De plus, si le port est déjà ouvert, ce port ne pourra plus être utilisé. Par conséquent, vous ne pouvez pas réexécuter le programme avant sa fermeture, sinon une exception se produira, c'est-à-dire qu'une fenêtre d'erreur apparaîtra. Dans les applications pratiques, les erreurs peuvent être évitées en déterminant si le programme a été exécuté et en le quittant s'il est déjà en cours d'exécution.
Lorsque les données sont transmises par le client, l'événement ServerSocket1ClientRead sera déclenché et nous pouvons traiter les données reçues ici. Dans ce programme, il reçoit principalement les informations de caractère envoyées par le client et effectue les opérations correspondantes selon l'accord préalable.
Le code complet du programme est le suivant :
unité Unit1 ; {programme serveur}
interface
utilise
Windows, messages, SysUtils, classes, graphiques, contrôles, formulaires, boîtes de dialogue, JPEG, ExtCtrls, ScktComp ;
taper
TForm1 = classe(TForm)
ServerSocket1 : TServerSocket ;
procédure ServerSocket1ClientRead(Expéditeur : TObject;Socket : TCustomWinSocket) ;
procédure FormCreate(Expéditeur : TObject);
procédure FormClose (Expéditeur : TObject ; var Action : TCloseAction);
privé
procédure Cjt_GetScreen(var Mybmp : TBitmap ; DrawCur : Boolean) ;
{Fonction de capture d'écran personnalisée, DrawCur indique s'il faut capturer l'image de la souris}
{Déclarations privées}
publique
{Déclarations publiques}
fin;
var
Formulaire1 : TForm1 ;
MyStream : TMemorystream ; {objet flux mémoire}
mise en œuvre
{$R *.DFM}
procédure TForm1.Cjt_GetScreen(var Mybmp : TBitmap ; DrawCur : Boolean) ;
var
Cursorx, Cursory : entier ;
cc:hdc;
Mycan : Tcanvas ;
R : TRect ;
DrawPos : TPoint ;
MonCurseur : TIcon ;
tenue : hwnd ;
Sujet : dword ;
mp : tpoint ;
pIconInfo : TIconInfo ;
commencer
Monbmp := Tbitmap.Create ; {Créer BMPMAP}
Mycan := TCanvas.Create ; {capture d'écran}
dc := GetWindowDC(0);
essayer
Mycan.Handle := dc;
R := Rect(0, 0, écran.Largeur, écran.Hauteur);
Mybmp.Width := R.Right;
Mybmp.Height := R.Bottom;
Mybmp.Canvas.CopyRect(R, Mycan, R);
enfin
releaseDC(0, DC);
fin;
Mycan.Handle := 0;
Mycan.Gratuit ;
si DrawCur alors {Dessine l'image de la souris}
commencer
GetCursorPos(DrawPos);
MonCurseur := TIcon.Create;
getcursorpos(mp);
hld := WindowFromPoint(mp);
Threadld := GetWindowThreadProcessId(hld, néant);
AttachThreadInput(GetCurrentThreadId, Threadld, True);
MonCursor.Handle := Getcursor();
AttachThreadInput(GetCurrentThreadId, threadld, False);
GetIconInfo(Moncurseur.Handle, pIconInfo);
sliderx := DrawPos.x - round(pIconInfo.xHotspot);
superficiel := DrawPos.y - round(pIconInfo.yHotspot);
Mybmp.Canvas.Draw(cursorx, Cursy, MyCursor); {Dessiner avec la souris}
DeleteObject(pIconInfo.hbmColor);{GetIconInfo crée deux objets bitmap lorsqu'ils sont utilisés. Ces deux objets doivent être libérés manuellement}.
DeleteObject(pIconInfo.hbmMask); {Sinon, après l'avoir appelé, il créera un bitmap et plusieurs appels en généreront plusieurs jusqu'à ce que les ressources soient épuisées}
Mycursor.ReleaseHandle ; {Libérer la mémoire du tableau}
MyCursor.Free ; {libérer le pointeur de la souris}
fin;
fin;
procédure TForm1.FormCreate(Expéditeur : TObject);
commencer
ServeurSocket1.Port := 3000 ;
ServerSocket1.Open ; {Socket commence à écouter}
fin;
procédure TForm1.FormClose(Expéditeur : TObject ; var Action : TCloseAction) ;
commencer
si ServerSocket1.Active alors ServerSocket1.Close ; {Fermer Socket}
fin;
procédure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket : TCustomWinSocket );
var
S, S1 : chaîne ;
MonBmp : TBitmap ;
Monjpg : TJpegimage ;
commencer
S := Socket.ReceiveText;
si S = 'cap' alors {Le client émet une commande de capture d'écran}
commencer
essayer
MyStream := TMemorystream.Create;{Créer un flux mémoire}
MonBmp := TBitmap.Create;
Monjpg := TJpegimage.Create;
Cjt_GetScreen(MyBmp, True); {True signifie saisir l'image de la souris}
Myjpg.Assign(MyBmp); {Convertir les images BMP au format JPG pour une transmission facile sur Internet}
Myjpg.CompressionQuality := 10 ; {paramètre de pourcentage de compression du fichier JPG, plus le nombre est grand, plus l'image est claire, mais plus les données sont volumineuses}
Myjpg.SaveToStream(MyStream); {Écrire une image JPG à diffuser}
Monjpg.free;
MyStream.Position := 0;{Remarque : cette phrase doit être ajoutée}
s1 := inttostr(MyStream.size);{taille du flux}
Socket.sendtext(s1); {taille du flux d'envoi}
enfin
MonBmp.free;
fin;
fin;
si s = 'prêt' alors {Le client est prêt à recevoir des images}
commencer
MonStream.Position := 0;
Socket.SendStream(MyStream); {Envoyer le flux}
fin;
fin;
fin.
Ce qui précède est le serveur, écrivons le programme client ci-dessous. Créez un nouveau projet et ajoutez le contrôle Socket ClientSocket, le contrôle d'affichage d'image Image, un panneau, une édition, deux boutons et un contrôle de barre d'état StatusBar1. Remarque : placez Edit1 et deux boutons au-dessus de Panel1. Les attributs de ClientSocket sont similaires à ceux de ServerSocket, mais il existe un attribut d'adresse supplémentaire, indiquant l'adresse IP du serveur à connecter. Après avoir renseigné l'adresse IP, cliquez sur « Connecter » pour établir une connexion avec le programme serveur. En cas de succès, la communication peut commencer. En cliquant sur "Capture d'écran", vous enverrez des caractères au serveur. Étant donné que le programme utilise des unités d'image JPEG, Jpeg doit être ajouté aux utilisations.
Le code complet est le suivant :
unité Unité2{client} ;
interface
utilise
Windows, messages, SysUtils, classes, graphiques, contrôles, formulaires, boîtes de dialogue, StdCtrls, ScktComp, ExtCtrls, Jpeg, ComCtrls ;
taper
TForm1 = classe(TForm)
ClientSocket1 : TClientSocket ;
Image1 : TImage ;
StatusBar1 : TStatusBar ;
Panneau 1 : TPanel ;
Edit1 : TEdit ;
Bouton1 : TButton ;
Bouton2 : TButton ;
procédure Button1Click (Expéditeur : TObject);
procédure ClientSocket1Connect(Expéditeur : TObject ;
Socket : TCustomWinSocket );
procédure Button2Click (Expéditeur : TObject);
procédure ClientSocket1Error(Expéditeur : TObject ; Socket : TCustomWinSocket ;
ErrorEvent : TErrorEvent ; var ErrorCode : Integer );
procédure ClientSocket1Read (Expéditeur : TObject ; Socket : TCustomWinSocket) ;
procédure FormCreate(Expéditeur : TObject);
procédure FormClose (Expéditeur : TObject ; var Action : TCloseAction);
procédure ClientSocket1Disconnect(Expéditeur : TObject ;
Socket : TCustomWinSocket );
privé
{Déclarations privées}
publique
{Déclarations publiques}
fin;
var
Formulaire1 : TForm1 ;
MaTaille : Entier Long ;
MyStream : TMemorystream ; {objet flux mémoire}
mise en œuvre
{$R *.DFM}
procédure TForm1.FormCreate(Expéditeur : TObject);
commencer
{-------- Ce qui suit consiste à définir les propriétés d'apparence du contrôle de fenêtre------------- }
{Remarque : placez Button1, Button2 et Edit1 au-dessus de Panel1}
Edit1.Text := '127.0.0.1';
Button1.Caption := 'Se connecter à l'hôte';
Button2.Caption := 'Capture d'écran';
Button2.Enabled := faux;
Panel1.Align := alTop;
Image1.Align := alClient;
Image1.Stretch := Vrai ;
StatusBar1.Align:=alBottom;
StatusBar1.SimplePanel := True ;
{---------------------------------------------------------------- ---------}
MyStream := TMemorystream.Create; {Créer un objet flux mémoire}
MaTaille := 0 ; {initialisation}
fin;
procédure TForm1.Button1Click(Expéditeur : TObject);
commencer
sinon ClientSocket1.Active alors
commencer
ClientSocket1.Address := Edit1.Text; {adresse IP distante}
ClientSocket1.Port := 3000 ; {Port de prise}
ClientSocket1.Open ; {Établir la connexion}
fin;
fin;
procédure TForm1.Button2Click(Expéditeur : TObject);
commencer
Clientsocket1.Socket.SendText('cap'); {Envoyer une commande pour avertir le serveur de capturer l'image d'écran}
Button2.Enabled := Faux;
fin;
procédure TForm1.ClientSocket1Connect(Expéditeur : TObject;
Socket : TCustomWinSocket );
commencer
StatusBar1.SimpleText := 'Avec l'hôte' + ClientSocket1.Address + 'Connexion établie avec succès !';
Button2.Enabled := Vrai ;
fin;
procédure TForm1.ClientSocket1Error(Expéditeur : TObject ;
Socket : TCustomWinSocket ; ErrorEvent : TErrorEvent ;
varErrorCode : entier );
commencer
Code d'erreur := 0 ; {Ne pas afficher la fenêtre d'erreur}
Statusbar1.simpletext: = 'Impossible de se connecter à l'hôte' + clientSocket1.address + 'connexion établie!';
fin;
procédure TForm1.ClientSocket1Disconnect(Expéditeur : TObject;
Socket : TCustomWinSocket );
commencer
Statusbar1.simpletext: = 'avec host' + clientSocket1.address + 'Disconnect!';
Button2.Enabled: = false;
fin;
procédure TForm1.ClientSocket1Read(Expéditeur : TObject;
Socket : TCustomWinSocket );
var
MyBuffer: Array [0..10000] de Byte;
MyReceviceLength: entier;
S : chaîne ;
Mybmp: tbitmap;
Myjpg: tjpegimage;
commencer
Statusbar1.simpletext: = 'recevoir des données ...';
Si mySize = 0 alors {MySize est le nombre d'octets envoyés par le serveur.
commencer
S: = socket.receiveText;
MySize: = strToint (s);
Clientsocket1.Socket.SendText ('Ready');
fin
autre
Commencer {Voici la partie recevant les données d'image}
MyReceviceLength: = socket.receivelthing; {Lire la longueur du paquet}
Statusbar1.simpletext: = 'recevoir des données, la taille des données est:' + intToStr (mysize);
Socket.receiveBuf (MyBuffer, MyReceviceLength);
Mystream.write (mybuffer, myReceviceLength);
Si mystream.size> = mysize alors {Si la longueur du flux est supérieure au nombre d'octets à recevoir, la réception est terminée}
commencer
Mystream.position: = 0;
Mybmp: = tbitmap.create;
Myjpg: = tjpegimage.create;
essayer
Myjpg.loadFromStream (mystream);
Mybmp.assign (myjpg);
Statusbar1.simpletext: = 'Affichage de l'image';
Image1.picture.bitmap.assign (mybmp);
Enfin {ce qui suit est le travail de nettoyage}
Mybmp.free;
Myjpg.free;
Button2.Enabled: = true;
{Socket.sendText ('cap'); ajouter cette phrase pour capturer l'écran en continu}
Mystream.clear;
MySize: = 0;
fin;
fin;
fin;
fin;
procédure TForm1.FormClose(Expéditeur : TObject ; var Action : TCloseAction) ;
commencer
MyStream.free; {relâchez l'objet de flux de mémoire}
Si clientSocket1.active alors clientSocket1.close;
fin;
fin.
Principe du programme: exécutez le serveur pour commencer à écouter, puis exécutez le client, entrez l'adresse IP du serveur pour établir une connexion, puis envoyez un caractère pour informer le serveur pour capturer l'écran. Le serveur appelle la fonction personnalisée CJT_GETSCREEN pour capturer l'écran et l'enregistrer en BMP, convertir le BMP en JPG, écrire le JPG dans le flux de mémoire, puis envoyer le flux au client. Après avoir reçu le flux, le client effectue l'opération opposée, convertissant le flux en JPG puis en BMP, puis l'affichage.
Remarque: En raison des limites de la prise, des données trop importantes ne peuvent pas être envoyées en même temps, mais ne peuvent être envoyées que plusieurs fois. Par conséquent, dans le programme, après avoir converti la capture d'écran en flux, le serveur envoie d'abord la taille du flux pour informer le client de la taille du flux. Il est, il sera converti et affiché.
Ce programme et l'OICQ auto-fabriqué précédent utilisent tous deux l'objet Stream Memory TmeMoryStream. En fait, cet objet de flux est le plus couramment utilisé dans la programmation. Un "intermédiaire" c'est le meilleur. Par exemple, si vous compressez ou décompressez un flux, vous créez d'abord un objet TMemoryStream, puis copiez-y d'autres données, puis effectuez les opérations correspondantes. Parce qu'il fonctionne directement dans la mémoire, l'efficacité est très élevée. Parfois, vous ne remarquerez même aucun retard.
Zones à améliorer le programme: Bien sûr, vous pouvez ajouter une unité de compression pour comprimer avant d'envoyer puis envoyer. Remarque: il y a aussi une astuce ici, qui consiste à compresser BMP directement au lieu de la convertir en JPG, puis de la compresser. Les expériences ont montré que la taille d'une image dans le programme ci-dessus est d'environ 40 à 50 Ko. Si vous souhaitez aller plus vite, vous pouvez utiliser cette méthode: capturez d'abord la première image et envoyez-la, puis commencez à partir de la deuxième image et n'envoyez que des images dans différents domaines de la précédente. Il existe un programme étranger appelé administrateur distant qui utilise cette méthode. Les données qu'ils ont testées sont les suivantes: 100 à 500 images par seconde sur le réseau local et 5 à 10 images par seconde sur Internet lorsque la vitesse du réseau est extrêmement faible. Ces digressions veulent simplement illustrer une vérité: lorsque vous pensez à un problème, surtout lors de l'écriture d'un programme, en particulier un programme qui a l'air très compliqué, ne vous y prendre pas trop. . Le programme est mort, le talent est vivant. Bien sûr, ceux-ci ne peuvent s'appuyer que sur l'accumulation d'expérience. Mais former de bonnes habitudes depuis le début versera des dividendes tout au long de votre vie!