Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Pour pouvoir réaliser une transmission de données, le programme serveur va devoir envoyer des données, et le programme client les recevoir.
Pour cela, nous allons utiliser trois fonctions :
- La fonction send, qui va envoyer les données (sous forme de tableau de char).
- La fonction recv, qui va recevoir ce qu'a envoyé la fonction send (sous forme de tableau de char).
- La fonction shutdown, qui va désactiver les envois et les réceptions sur la socket.
Premièrement, nous allons revenir sur le code source du programme serveur, pour étudier le fonctionnement de la fonction
send.
La fonction send
Voici son prototype :
Code : C1 | int send(int socket,char *buffer,int len,int flags);
|
- La fonction retourne SOCKET_ERROR en cas d'erreur.
- Le premier paramètre représente la socket destiné à recevoir le message.
- Le deuxième représente un pointeur (un tableau) dans lequel figureront nos informations à transmettre.
- Le paramètre len indique le nombre d'octets à lire.
- Le dernier correspond au type d'envoi ; il nous est inutile, nous le mettrons donc à 0 pour avoir un envoi normal.
La fonction est très simple :
Code : C1 | send(sock, buffer, sizeof(buffer), 0);
|
sizeof(buffer) n'est pas toujours la bonne valeur à mettre pour le troisième paramètre. Par exemple, pour les chaines de caractères, il faut utiliser la fonction strlen pour connaitre la taille de la chaine. De même, pour un tableau, il faut passer en paramètre la taille totale du que prend le tableau (nombre de cases * taille d'une case).
La fonction recv
Maintenant, nous allons nous pencher sur l'application client. Pour pouvoir étudier maintenant la fonction
recv.
Cette fonction est aussi simple que la fonction
send, et son fonctionnement le même :
Code : C1 | int recv(int socket,char * buffer,int len,int flags)
|
- La fonction retourne SOCKET_ERROR en cas d'erreur.
- Le premier paramètre représente la socket destiné à attendre un message.
- Le deuxième représente un pointeur (un tableau) dans lequel résideront les informations à recevoir.
- Le paramètre len indique le nombre d'octets à lire.
- De même, le dernier correspond au type d'envoi : il nous est également inutile, nous le mettrons donc aussi à 0.
Nous recevrons les données envoyées comme cela :
Code : C1 | recv(sock, buffer, sizeof(buffer), 0);
|
Tout comme la fonction send, sizeof(buffer) n'est pas toujours la bonne taille e mettre pour le troisième paramètre. Pour cette fonction il ne faut pas mettre une valeur plus grande que la taille du tableau elle-même. Sinon, on risque de voir le programme planter

.
La fonction shutdown
Voici le prototype de la dernière fonction : elle servira à fermer la transmission de données entre le serveur et le client.
Code : C1 | int shutdown(int socket,int how);
|
- la fonction retourne un int.
- Le premier paramètre désigne sur quel socket on doit fermer la connection.
- Le deuxième paramètre définit où va se fermer la transition. Il peut prendre trois valeurs : 0, pour fermer la socket en réception, 1, en émission, 2 dans les deux sens.
Nous l'utiliserons ainsi, si l'on se place du côté du serveur :
Code : C
Maintenant que nous avons tout, nous allons faire le point et améliorer nos deux applications.
Faisons le point
Nous allons améliorer nos deux applications pour qu'elles se transmettent des données : pour cela, nous allons nous servir des trois fonctions précédemment apprises...
Nous allons du côté serveur envoyer un "bonjour" quand un client se connecte, puis fermer l'application.
Du côté client, nous allons recevoir la chaîne de caractères, et l'afficher à l'écran.
Vous pouvez vous lancer maintenant, ce n'est pas difficile

. Je suis même persuadé que vous pouvez le faire

.
Codons ... ...
... ... ... ...
C'est fini ! Voilà la correction

:
Secret (cliquez pour afficher)
L'application SERVEUR :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99 | #if defined (WIN32)
#include <winsock2.h>
#elif defined (linux)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(s) close(s)
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#endif
#include <stdio.h>
#include <stdlib.h>
#define PORT 23
int main(void)
{
#if defined (WIN32)
WSADATA WSAData;
int erreur = WSAStartup(MAKEWORD(2,0), &WSAData);
#else
int erreur = 0;
#endif
SOCKET sock;
SOCKADDR_IN sin;
SOCKET csock;
SOCKADDR_IN csin;
char buffer[32] = "Bonjour !";
int recsize = (int) sizeof csin;
int sock_err;
/* Si les sockets Windows fonctionnent */
if(!erreur)
{
sock = socket(AF_INET, SOCK_STREAM, 0);
/* Si la socket est valide */
if(sock != INVALID_SOCKET)
{
printf("La socket %d est maintenant ouverte en mode TCP/IP\n", sock);
/* Configuration */
sin.sin_addr.s_addr = htonl(INADDR_ANY); /* Adresse IP automatique */
sin.sin_family = AF_INET; /* Protocole familial (IP) */
sin.sin_port = htons(PORT); /* Listage du port */
sock_err = bind(sock, (SOCKADDR *) &sin, sizeof sin);
/* Si la socket fonctionne */
if(sock_err != SOCKET_ERROR)
{
/* Démarrage du listage (mode server) */
sock_err = listen(sock, 5);
printf("Listage du port %d...\n", PORT);
/* Si la socket fonctionne */
if(sock_err != SOCKET_ERROR)
{
/* Attente pendant laquelle le client se connecte */
printf("Patientez pendant que le client se connecte sur le port %d...\n", PORT);
csock = accept(sock, (SOCKADDR *) &csin, &recsize);
printf("Un client se connecte avec la socket %d de %s:%d\n", csock, inet_ntoa(csin.sin_addr), htons(csin.sin_port));
sock_err = send(csock, buffer, 32, 0);
if(sock_err != SOCKET_ERROR)
printf("Chaine envoyée : %s\n", buffer);
else
printf("Erreur de transmission\n");
/* Il ne faut pas oublier de fermer la connexion (fermée dans les deux sens) */
shutdown(csock, 2);
}
}
/* Fermeture de la socket */
printf("Fermeture de la socket...\n");
closesocket(sock);
printf("Fermeture du serveur terminee\n");
}
#if defined (WIN32)
WSACleanup();
#endif
}
/* On attend que l'utilisateur tape sur une touche, puis on ferme */
getchar();
return EXIT_SUCCESS;
}
|
L'application CLIENT
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 | #if defined (WIN32)
#include <winsock2.h>
#elif defined (linux)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define closesocket(s) close(s)
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
#endif
#include <stdio.h>
#include <stdlib.h>
#define PORT 23
int main(void)
{
#if defined (WIN32)
WSADATA WSAData;
int erreur = WSAStartup(MAKEWORD(2,0), &WSAData);
#else
int erreur = 0;
#endif
SOCKET sock;
SOCKADDR_IN sin;
char buffer[32] = "";
/* Si les sockets Windows fonctionnent */
if(!erreur)
{
/* Création de la socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
/* Configuration de la connexion */
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
/* Si l'on a réussi à se connecter */
if(connect(sock, (SOCKADDR *)&sin, sizeof(sin)) != SOCKET_ERROR)
{
printf("Connection à %s sur le port %d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
/* Si l'on reçoit des informations : on les affiche à l'écran */
if(recv(sock, buffer, 32, 0) != SOCKET_ERROR)
printf("Recu : %s\n", buffer);
}
/* sinon, on affiche "Impossible de se connecter" */
else
{
printf("Impossible de se connecter\n");
}
/* On ferme la socket */
closesocket(sock);
#if defined (WIN32)
WSACleanup();
#endif
}
/* On attend que l'utilisateur tape sur une touche, puis on ferme */
getchar();
return EXIT_SUCCESS;
}
|
Sachez que vous pouvez créer un fichier
header.h qui contient les inclusions,
typedef et déclarations. Je ne l'ai pas fait pour éviter de vous perdre, avec les fichiers (déjà pas facile, les sockets... avec deux applications

). C'est en temps normal vivement recommandé

.