Les sockets

.: [ By Progzman ] :.


Le socket est un technique à savoir absolument ( si vous ne savez pas le faire ne dites pas que vous êtes programmeur=).
On va travailler sous Linux en C et des notions d'entrées/sorties haut niveau seront très utiles.
Le plus dur à comprendre ce sera toutes les structures que l'on exploitera mais ne vous en faites pas.

Pour ceux qui débarquent, on va expliquer: un socket est un moyen de faire communiquer deux ordinateurs sur un réseau ( en gros ), et peut importe le système.
Les sockets ça marche sous Linux ou sur UNIX mais les stakhanovistes de Microdaube ont repris ça dans WinSock evidemment.
On étudiera seulement le coté client.
Une fois le socket ouvert et la connexion établie, on écrira et lira sur le socket avec les appels read et write.
Et oui lorsqu'on ouvre un socket la fonction à appeller retourne un numéro de descripteur de fichier donc les habitués ne seront pas trop perdus.
Enfin bon on ouvre pas un socket avec open , mais avec ça :

#include <sys/types.h>
#include <sys/socket.h>
 
int socket (int domain, int type, int protocol);

Cette fonction retourne donc le numéro du descripteur de fichier correspondant au socket ouvert, donc de type int. domain définit la famille d'adresses, type le type de communication à utiliser et protocol ben ça parle de lui-même. La valeur de domain ( pas besoin de mettre une variable ) peut être une des suivantes :

AF_UNIX domaine interne à UNIX ( socket de système de fichiers )
AF_INET domaine pour les protocoles d'un réseau de type ARPA ( donc du genre Internet, TCP/IP )
AF_ISO domaine pour les protocoles standard ISO
AF_NS domaine pour les protocoles Xerox Network Systems
AF_IPX domaine pour les protocoles Novell IPX
AF_APPLETALK domaine pour Appletalk DDS

On utilisera AF_INET mais pour ceux que ça intéresse je parlerais un peu de AF_UNIX. type définit la technique employée pour la communication, là il y a deux possibilités:

SOCK_STREAM : le plus utilisé ( encore ) . Le socket utilisera un flux bidirectionnel, séquencé et fiable puisqu'une connexion sera obligatoirement établie.

SOCK_DGRAM : le socket fonctionnera avec des datagrammes. Le socket sera alors utilisé pour envoyer des petits messages d'une taille bien déterminée ( généralement pas très grande ). Si ce socket est utilisé sur un domaien AF_INET, on enverra des datagrammes UDP.

protocol bah c'est simple à comprendre sauf que là on met un nombre, je vous recommande 0 ( protocole par défaut ). Maintenant que le socket est ouvert mais il n'y a rien au bout.
On va donc initialiser une structure définie dans netinet/in.h, qui contient des infos.
Voici la structure :

struct sockaddr_in {
    short int sin_family; /* ici on mettera AF_INET */
    unsigned short int sin_port; /* le port du socket */
    struct in_addr sin_addr; /*l'adresse IP de notre cher serveur =) */
};

Les plus observateurs auront remarqué que l'adresse du serveur est contenue dans une structure in_addr, que voici :

struct in_addr {
    unsigned long int s_addr;
};

Et oui une adresse IP ça fait 32 bits donc on aura le droit à un beau unsigned long. Voila maintenant vous pouvez communiquer avec le serveur ( ne lui envoyez pas n'importe quoi =) .

Pour fermer le socket on fait comme un fichier :
close (socket_fd)- où socket_fd est le numéro du descritpeur de notre socket.

Seulement le serveur est pas encore au courant que vous voulez vous connecter. Donc on va le faire :

#include <sys/socket.h>
 
int connect(int socket, const struct sockaddr *adresse, size_t long_adresse);

Socket c'est le numéro du descipteur d'entrées/sorties du socket, adresse c'est la structure remplie plus haut, long_adresse correspond à la longueur de la structure ( faites un sizeof ).
connect revoie 0 si tout va bien ou -1 en cas d'erreur.
Pour ceux qui connaissent errno ( c pour les erreurs ) peut prendre ces valeurs:

EBADF le descripteur d'entrées/sorties du socket n'est pas correct
EALREADY il y a deja une connexion sur ce socket
ETIMEDOUT le délai pour la connexion à été dépassé
ECONNREFUSED la demande de connexion à été refusée par le serveur

Si vous voulez des infos sur la bécane à laquelle vous êtes connecté, on peut arranger ça avec l'une de ces deux

#include <netdb.h>
 
struct hostent *gethostbyaddr ( const void *addr, size_t long, int type);
struct hostent *gethostbyname ( const char *nom );

La structure retournée ressemble à ça :

struct hostent {
    char *h_name; /* nom de l'hote */
    char **h_aliases; /* liste des alias */
    char h_addrtype; /* type d'adresse */
    int h_lengh; /* longueur de l'adresse en octets */
    char **h_addr_list; /* liste d'adesses */
};

Si il n'y a rien comme info, le pointeur retourné est nul. Si maintenant vous voulez des infos sur un service sur l'hote, utilisez l'un de ces deux appels :

#include <netdb.h>
 
struct servent *getservbyname ( const char *nom, const char *proto);
struct servent *getservbyport (int port, const char *proto);

proto c'est le protocole à utiliser ( tcp pour SOCK_STREAM ou udp pour SOCK_DGRAM ).
La structure servent se présente comme ceci :

struct servent {
    char *s_name; /* nom du service */
    char **s_aliases; /* liste des alias */
    int s_port; /* numéro de port */
    char *s_proto; /* protocole : tcp ou udp */
};

Si vous voulez une adresse de type 123.123.123.123 à partir de la structure in_addr, utilisez cette fonction :

#include <arpa/inet.h>
 
char *inet_ntoa (struct in_addr in);

Je vais quand même faire un petit retour sur la strucure qui sera exploitée dans le cas d'un domaine de type AF_UNIX. on utilise dans ce domaine la structure sockaddr_un :

struct sockaddr_un {
    sa_family_t sun_family; /* on met AF_UNIX */
    char sun_path[]; /* pathname */
}

Voila maintenant vous pouvez sans problème écrire un prog qui exploite les sockets.




  
     .: index :.