C socket
Introduction
Les sockets permettent de connecter deux programmes qui s'exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l'utilisation d'un port (TCP ou UDP) et d'une adresse IP. Il y a deux types de sockets, une qui est démarrée par la partie serveur en écoute et l'autre démarrée par la partie cliente qui se connecte à la première. Ci-contre une image résumant les différentes étapes pour arriver à l'envoie de données. |
Utilisation
Côté serveur
Tout d'abord il faut créer l'objet socket:
#include <sys/socket.h>
#include <netinet/in.h>
int socket(int domain, int type, int protocol)
- domain → integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)
- type → type de communication
- SOCK_STREAM: TCP
- SOCK_DGRAM: UDP
- SOCK_RAW: socket à l'état brut (bas niveau)
- protocol → valeur du champ protocol de l'entête de niveau 3 (généralement 0)
- la valeur de retour est le fichier descripteur de la socket ou -1 en cas d'erreur (errno est positionné)
Une fois le descripteur de socket créé, il est possible de le configurer:
#include <sys/socket.h>
#include <netinet/in.h>
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
- sockfd → le fichier descripteur de la socket;
- level → niveau d'application de l'option (SOL_SOCKET, TCP, UDP, ...);
- optname → le nom de l'option (SO_REUSEADDR, SO_REUSEPORT, SO_KEEPALIVE, ...)
- optval, optlen → utilisé pour accéder aux options de la socket;
- le code retour varie entre 0 et -1 en cas d'erreur (errno est positionné)
Il existe plusieurs options de socket, les plus utilisées étant :
- SO_REUSEADDR → permet de réutiliser l'adresse de la socket tout de suite même si cette dernière est dans l'état wait;
- SO_REUSEPORT → permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l'état wait;
- SO_KEEPALIVE → envoie des informations périodiquement pour tester si l'extrémité du tunnel est toujours présente;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
- sockfd → le fichier descripteur de la socket;
- addr → une structure symbolisant l'adresse
struct sockaddr_in {
short sin_family; // e.g: AF_INET
unsigned short sin_port; // e.g: htons(3490)
struct in_addr sin_addr; // détaillé ci-dessous
char sin_zero[8]; // '0' habituellement
};
struct in_addr {
unsigned long s_addr; // initialiser avec inet_aton()
};
- addrlen → la taille de l'objet addr;
- le code retour varie entre 0 et -1 en cas d'erreur (errno est positionné)
On peut maintenant passer la socket en état d'écoute, elle est prête à recevoir des connexions:
int listen(int sockfd, int backlog)
- sockfd → le fichier descripteur de la socket;
- backlog → taille maximum de la file d'attente de la socket après laquelle le système répond avec ECONNREFUSED;
- le code retour varie entre 0 et -1 en cas d'erreur (errno est positionné)
On peut maintenant attendre une connexion:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockfd → le fichier descripteur de la socket;
- addr → l'adresse du client;
- addrlen → la taille de la structure addr;
- le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et -1 en cas d'erreur (errno est positionné)
Côté client
Après avoir créé la socket avec socket on peut se connecter à la partie serveur:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- sockfd → le fichier descripteur de la socket;
- addr → une structure symbolisant l'adresse (cf. ci-dessus)
- addrlen → la taille de l'objet addr;
- le code retour varie entre 0 et -1 en cas d'erreur (errno est positionné)