Les Types de Sockets
Il y a deux types de socket : les 'sockets stream (flot)' et les 'sockets datagram'. Un socket stream se sert du TCP, ainsi sa transmission exige que la connexion soit fiable. Vous employez un socket stream quand vous voulez envoyer de gros fichiers à travers le net ou bien que vous souhaitez envoyer vos données par paquets séquencés (c'est-à-dire que l'ordre des paquets de données est important). Un socket datagram fournit un transfert simple de données. On ne garantit pas qu'il soit fiable (les données peuvent ne pas atteindre leur destination), ni séquencés (les paquets de données peuvent arriver dans un ordre différent que celui qui est attendu), ou non-dupliqués (le socket de destination peut recevoir deux ou même plusieurs copies identiques des mêmes paquets de données). Les protocoles HTTP et FTP et quelques autres emploient les sockets stream tandis que certains autres protocoles d'émission utilisent les sockets datagram.
Vous pouvez vous représenter la connexion par socket stream comme un simple appel téléphonique. Vous devez d'abord faire un appel et si l'appel est couronné de succès, une connexion est établie. Alors vous pouvez échanger des informations avec l'autre ordinateur d'une façon fiable. Si un quelconque problème de communication se produit vous le saurez immédiatement et pourrez prendre des mesures pour le rectifier. La connexion par socket datagram c'est un peu comme l'envoi de plusieurs courriers à quelqu'un. Vous ne pouvez pas savoir à l'avance s'ils atteindront la personne destinée. Même s'il les a reçus, il n'y a aucune garantie que ces courriers lui parviendront dans le même ordre que celui où vous les lui avez envoyés. Et les courriers peuvent très bien ne pas atteindre la cible du tout.
Le modèle d'un socket emploie parfois le concept client-serveur c'est-à-dire qu'on se représente le socket en tant que serveur puisque il propose ses services à d'autre sockets, de l'autre côté c'est le client, lui il demande des services au serveur. Les utilisations HTTP et FTP sont des concepts client-serveur.
Ordre d'Octet
Puisque nous devons traiter avec des adresses IP dans la programmation d'un winsock, nous devons d'abord nous familiariser avec les différents types d'ordonnancement de bytes. Il y a deux types de rangement de bytes : le 'Grand Endian' et le 'Petit Endian'. Dans l'arrangement 'Grand Endian', l'octet extrême gauche est stocké dans l'octet le plus significatif (byte de poids fort). Pour l'arrangement 'Petit Endian' c'est le contraire. Par exemple, si votre adresse IP est 205.34.67.24, alors l'arrangement 'Grand Endian' sera représenté en tant que 205 34 67 24. Mais dans l'arrangement 'Petit Endian', ce sera 24 67 34 205.
Le CPU Intel utlise le 'Petit Endian' tandis que d'autres CPU comme Motorola travaillent en 'Grand Endian'. Le mot de la fin c'est qu': Internet emploie l'arrangement 'Grand Endian', donc si vous utilisez un CPU qui classe en 'Petit Endian', vous devez d'abord convertir les adresses IP avant de les utiliser avec le Net.
Modes : Bloqué et Débloqué
Les fonctions des sockets peuvent fonctionner sur deux modes : bloqué et débloqué. À l'origine, dans l'exécution Berkeley d'Unix, les sockets fonctionnaient en mode bloqué, c'est-à-dire qu'une fonction d'un socket ne retournait pas avant que l'opération ne soit achevée. Le "mode bloqué " est une opération synchrone alors que le "mode débloqué" est une opération asynchrone. L'opération de blocage peut prendre une longue période de temps non définie pour s'achever et attente que les données atteignent le socket éloigné. Pendant ce temps-là, le programme semblera gelé. C'est en général plutôt inacceptable dans l'environnement de Windows. Donc Windows inclut des versions d'API de sockets (débloqués) asynchrones, ce sont des évolutions des APIs originaux bloqués. Vous devez utiliser les versions débloquées chaque fois que c'est possible puisqu'ils se conforment au paradigme de Windows et fournissent de meilleurs résultats que ceux bloqués.
Les Ports de Communication
Quand vous créez un socket et le connectez à un autre socket éloigné, vous devez spécifier le port que les sockets prendront pour communiquer entre eux. Les ports dans ce cas ne sont pas des ports matériel comme COM1 ou COM2. Les ports dans la programmation d'un winsock sont virtuels dans l'unique but d'établir une communication. Peut-être qu'un exemple rendra ça plus clair. Sachez que, le serveur crée le socket et le charge d'écouter la connexion entrante sur le port 21, bien qu'il puisse y avoir beaucoup de paquets entrants vers le serveur, seuls ces paquets qui sont destinés au port numéro 21 seront routés vers le socket. Plusieurs protocoles Internet ont leurs propres ports par défaut. Le port du protocole HTTP est le port 80 (décimale) et le FTP emploie le port 21 (décimale). Ceux sont deux ports par défaut. Ça signifie que si le client et le serveur consentent tous deux à communiquer via d'autres ports, ils peuvent le faire sans que ça pose problème.
Vue d'ensemble sur la programmation d'un Winsock
La programmation d'un winsock inclut normalement ces étapes:
Valeur de Retour
WSAStartup renvoie un NULL si l'appel est couronné de succès. Autrement il renvoie le code d'erreur. Vous pouvez chercher les codes d'erreur dans la doc winsock.hlp. C'est la seule fonction winsock qui renvoie le code d'erreur réel parce que vous ne pouvez pas appeler WSAGetLastError si la Dll socket n'est pas initialisée auparavent.
Commentaires
Si cette fonction n'est pas couronnée de succès, vous ne pourrez employer aucune des fonctions winsock. En fait, cette fonction sert aussi de routine de négociation pour les meilleures versions d'API winsock, pour votre application. Après que l'appel soit couronné de succès, la structure WSADATA sera remplie avec les capacités du winsock. Un des membres, est la plus haute version du winsock que la bibliothèque doit soutienir.
Si votre application requière au minimum la version 1.1 et que la bibliothèque winsock courante peut soutenir la version 2, alors l'appel de WSAStartup sera couronné de succès et ainsi la bibliothèque winsock retournera la valeur 200h dans le membre concerné de WSADATA. Si l'application peut se servir de l'appui de la version winsock 2, on pourra appeler WSACleanup pour refermer l'initialisation précédente et appeler WSAStartup à nouveau, cette fois-ci avec 200h dans son paramètre wVersionRequired. Si votre application ne supporte que winsock 1.1, vous ne devez pas examiner la structure WSADATA.
Petit bout de Code :
Valeur de Retour
Si cet appel échoue, la valeur en retour est INVALID_SOCKET et vous pouvez appeler la fonction WSAGetLastError pour retrouver le code de l'erreur réelle. Si l'appel est couronné de succès, il renvoie le descripteur du socket, lequel vous devrez utiliser pour vos fonctions winsock suivantes.
Commentaire:
Vous devez créer au moins un socket pour l'employer en tant que dispositif de communication de part. Après l'appel couronné de succès, vous devez sauvegarder les retours du descripteur de socket pour l'utiliser avec les fonctions winsock suivantes.
Petit bout de Code:
Commentaires
Cette fonction est l'instrument du paradigme 'débloqué'. Elle permet de spécifier quels événements winsock sont intéressants pour notre application, et lorsque un de ces événements winsock pré-enregistré se produit, la bibliothèque winsock enverra le message indiqué à la fenêtre. Cette fonction change aussi le mode du socket pour le mettre à 'débloqué'. Je recommande que vous employiez WSAAsyncSelect chaque fois que c'est possible puisque il se conforme au paradigme de Windows.
Si vous voulez que la bibliothèque winsock arrête d'envoyer des informations à votre fenêtre, vous devez appeler WSAAsyncSelect avec la valeur 0 dans le paramètre lEvent. Cependant, prenez garde qu'il peut rester des messages winsock dans la file d'attente avant que vous n'ayez annulez la réception de ces messages d'informations winsock. Vous devez être préparés à les traiter. Normalement vous devez les extraire de la file d'attente des messages grâce à l'appel PeekMessage avec le flag PM_REMOVE.
Quand l'application est intéressée par l'arrivée d'un événement winsock, le message indiqué par le paramètre msg sera envoyé à la procédure de la fenêtre retrouvée par le paramètre hwnd.
Notre message 'msg' construit sur mesure par nous-même fonctionne ainsi : wParam contient le descripteur du socket, le mot de poids fort de lParam contient le code d'erreur (s'il y en a une) et le mot de poids faible de lParam contient l'événement.
Petit Bout de Code
WndProc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.......
.if uMsg==WM_SOCKET
; C'est le message que nous avons spécifié dans WSAAsyncSelect
mov eax,lParam
.if ax==FD_CONNECT
; Le mot poids faible de lParam contient le code de l'événement.
shr eax,16
; Le code d'erreur (s'il y en a un) est stocké dans le mot de poids fort d'lParam
.if ax==NULL
<Aucune erreur ne s'est produite donc on continue>
.else
<Une erreur s'est produite. Mettez votre routine de traitement d'erreurs ici>
.endif
.elseif
ax==FD_READ
shr eax,16
.if ax==NULL
<Aucune erreur ne s'est produite donc on continue>
.else
<Une erreur s'est produite. Mettez votre routine de traitement d'erreurs ici>
.endif
.elseif
ax==FD_CLOSE
shr eax,16
.if ax==NULL
<Aucune erreur ne s'est produite donc on continue>
.else
<Une erreur s'est produite. Mettez votre routine de traitement d'erreurs ici>
.endif
.endif
.else
..........
.endif
WndProc ENDP
Vous devez remplir cette structure et passer son adresse à connect.
Valeurs de Retour
Ça dépend du mode de fonctionnement du socket.
.data?
socket dd ?
.code
........
mov sin.sin_family, AF_INET
invoke htons, Port
; On converti d'abord le numéro du port dans l'organisation d'octets
; pour réseau
mov sin.sin_port,ax
; Notez que ce membre est un paramètre de taille 'Word'.
invoke inet_addr, addr IPAddress
; On converti l'adresse IP dans l'organisation d'octets du réseau
mov sin_addr,eax
invoke connect,socket,addr sin,sizeof sin
.if eax==SOCKET_ERROR
; En supposant qu'on utilise le mode débloqué, la connexion
; retournera toujours le flag SOCKET_ERROR.
invoke WSAGetLastError
; <-- Cette fonction retrouve le code de l'erreur réel,
.if eax!=WSAEWOULDBLOCK
; si ce n'est pas WSAEWOULDBLOCK
<Mettez votre code de traitement d'erreurs ici>
; ça signifie qu'une vraie erreur s'est produite.
.endif
.endif
Valeur de retour
Si le retour est couronné de succès, il renvoie le pointer qui est sur la structure hostent laquelle est réservée par Windows pendant l'exécution du socket. Si l'appel échoue, il renvoye un NULL et vous pouvez appeler WSAGetLastError pour retrouver le code d'erreur.
Petit Bout de Code
.data
sin SOCKADDR_IN <>
hostname db "members.xoom.com",0
Port dd 80
; On utilise le port 80, le port HTTP pour notre démonstration
.data?
socket dd ?
.code
........
mov sin.sin_family, AF_INET
invoke htons, Port
; On converti d'abord le numéro du port dans l'organisation d'octets
; pour réseau
mov sin.sin_port,ax
; Notez que ce membre est un paramètre de taille 'Word'.
invoke gethostbyname, addr hostname
mov eax,[eax+12]
; Transfère le membre h_list dans eax
mov eax,[eax]
; Copie le pointer qui est sur l'addresse IP actuelle dans eax
mov eax,[eax]
; Copie l'addresse de l'IP dans eax
mov sin.sin_addr,eax
invoke connect,socket,addr sin,sizeof sin
.if eax==SOCKET_ERROR
; En supposant qu'on utilise le mode débloqué, la connexion
; retournera toujours le flag SOCKET_ERROR.
invoke WSAGetLastError
; <-- Cette fonction retrouve le code de l'erreur réel,
.if eax!=WSAEWOULDBLOCK
; si ce n'est pas WSAEWOULDBLOCK
<Mettez votre code de traitement d'erreurs ici>
; ça signifie qu'une vraie erreur s'est produite.
.endif
.endif
Normalement nous n'employons aucun de ces deux flags, dans ce cas le paramètre flags doit être mis à 0.
Commentaires
Cette fonction lance des données vers un socket éloigné et connecté. Il ne se soucie pas, ni ne connaît les données qu'il envoie.
Petit Bout de Code
Commentaires
Cette fonction lit les données du socket et les stocke dans le buffer indiqué. Une question surgit : comment savoir combien d'octets valides ont été lus dans le socket ?
La réponse vient de la fonction ioctlsocket. Elle possède la syntaxe suivante :
Valeur de Retour
S'il est couronné de succès, la valeur de retour dans eax est NULL. Sinon la valeur SOCKET_ERROR est retournée et vous pouvez appeler WSAGetLastError pour retrouver le code d'erreur.
Commentaires
Cette fonction est employée pour contrôler le mode du socket. Avec la commande FIONBIO, on peut commuter le socket du mode bloqué au mode débloqué et inversement.
La commande FIONREAD renvoie la quantité de données valides dans le socket.
Grâce à la commande SIOCATMARK, on vérifie vraiment si toutes les données hors-norme ont été lues.
Petit Bout de Code
Valeur de Retour
Si elle est couronnée de succès, elle retourne un NULL. Sinon SOCKET_ERROR est retourné et vous pouvez appeler WSAGetLastError pour retrouver le code d'erreur.
Commentaires
Cette fonction referme le socket. Chaque ressource du socket sera également terminé.
Petit Bout de Code
Valeur de Retour
Si elle est couronnée de succès, elle retourne un NULL. Autrement elle retourne SOCKET_ERROR.
Commentaires
Il doit exister dans votre programme autant d'appels de fonctions WSAStartup que de fonctions WSACleanup.
Si vous appelez WSAStartup trois fois, vous devez également appeler WSACLEANUP trois fois autrement la bibliothèque winsock sera incapable de libérer les ressources inutilisées quelles qu'elles soient. C'est la dernière fonction que vous appelez lorsque vous en avez terminé avec la bibliothèque winsock.
Petit Bout de Code