Bonjour à tous
Je cherche à faire un générateur de trafic en C sur lequel j'aurai entièrement la main. J'ai donc deux programmes. Un client qui va générer et envoyer des messages. Et un serveur en charge de les réceptionner et de faire des stats. Les termes client et serveur pourraient ne pas être approprié ici, j'utilise juste le vocabulaire iPerf.
Mon code fonctionne bien en local, mais pas lorsque le deux programmes tournent sur deux PC différent sur le réseau. La connection entre les deux PC fonctionne (ping, ssh, etc.)
Le client envoie ses paquets sur le réseau et ne retourne pas d'erreur ( pas étonnant vu qu j'utilise UDP ). Le serveur ne reçoit simplement aucun message de la part du client.
J'ai observé les trames circulant entre les deux PC avec Wireshark. J'y ai vu deux choses interesantes :
1 - Des trames correspondant aux messages envoyée par le client arrivent bien au niveau de l'interface réseau de la machine hébergeant le serveur
2 - J'observe également des paquets ICMP indiquant "Destination unreachable (Port Unreachable)". Ce que j'observe pas lorsque je fait le même test avec mes deux programme en local (et que tout marche bien) mais que je peux observer en local si je ne lance le client sans le serveur.
De plus, je me suis basé sur du code écrit par quelqu'un d'autres + différents tuto, donc n'hésitez pas à me dire s'il y a des choses absurde dans mon code
Voici mon code client que je compile avec la commande : gcc -o client client.c
Et que j'exécute avec : ./client [taille paquet] [temps interpaquet en ùs] [destination]
La destination est une ip ou "localhost"
Voici mon code serveur. Que je compile avec la commande : g++ -pthread -o server server.c
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 #include <stdio.h> #include <stdlib.h> #include <unistd.h> /* Bibliotheques pour les fonctions manipulant les chaines de caracteres (strcpy(), strcmp(), strchr(), etc.) */ #include <string.h> /* Bibliotheques pour avoir acces a la variable globale errno */ #include <errno.h> /* Bibliotheques pour la programmation socket */ #include <sys/types.h> #include <sys/socket.h> /* Bibliotheque contenant entre autres les structures des adresses */ #include <netinet/in.h> /* Bibliotheque pour la fonction getaddrinfo() */ #include <netdb.h> /* Bibliotheques pour les ouvertures/lectures/ecritures sur fichiers */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #define MAXBUFF_SIZE 65507 /* Affiche un message d'erreur avec perror. Cela permet de voir le type d'erreur. * perror() interprete la valeur de errno. * Finit par un exit. exit_status inique le type d'erreur (1: fonction, 2: appel systeme reseau, 3: erreur sur fichier, 4: autres appels systeme). */ void erreur(char* string, int exit_status) { perror(string); exit(exit_status); } ssize_t sendRate(int _sockfd,int flags ,struct sockaddr * _res,socklen_t _len,useconds_t delay,unsigned int pktSize){ ssize_t r; char _buff[pktSize]; memset(_buff,0,pktSize); while (1){ r = sendto(_sockfd, _buff , sizeof(_buff), flags, _res, _len); printf(" %ld bytes sent \n",r); if(r != sizeof(_buff)){ erreur("Erreur sendto",2); } usleep(delay); } return r; } int main(int argc, char* argv[]) { // arg 1 : taille pkt // arg 2 : débit ( délais ? ) // arg 3 : destination ( devrait être fixé ? ) int sockfd, size, nbBytes; struct addrinfo hints, *res; struct sockaddr_in6 clientAddr; char buffer[MAXBUFF_SIZE]; char destination[100]=""; unsigned int pktSize; useconds_t delay; if(argc != 4 ){ fprintf(stderr,"Usage: \n\t arg 1 : pkt size in octet \n\t arg 2 : delay in us \n\t arg 3 : destination \n"); exit(1); } if(argc == 4){ pktSize = atoi(argv[1]); delay = atof(argv[2]); strcpy(destination,argv[3]); } memset(&hints,0,sizeof(hints)); hints.ai_family = AF_INET; //IPv4 ou IPv6 hints.ai_socktype = SOCK_DGRAM; //UDP if(getaddrinfo(destination,"1080",&hints,&res)!=0) erreur("Erreur getaddrinfo()",1); if((sockfd=socket(res->ai_family, res->ai_socktype, res->ai_protocol))<0) erreur("Erreur socket():",2); //printf("Pret à envoyer des message de %d bytes toutes les %u usecondes \n",pktSize,delay); getchar(); ssize_t r = sendRate(sockfd, 0, (struct sockaddr *) res->ai_addr, (socklen_t) res->ai_addrlen,delay, pktSize); size=sizeof(clientAddr);//N'était pas nécessaire en IPv4 if((nbBytes=recvfrom(sockfd, buffer, 100, 0, (struct sockaddr *) &clientAddr, (socklen_t *) &size))>0) { buffer[nbBytes]='\0';//Au cas ou... printf("Message recu du serveur: %s\n",buffer); } else { erreur("Erreur recvfrom",2); } close(sockfd); return(0); }
( je me suis autorisé du c++ pour la gestion du temps ... )
Et que j'éxécute avec la commande : ./server
Aucun paramètre n'est obligatoire
Merci par avance pour ceux qui prendront le temps de lire et de m'aider
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190 #include <stdio.h> #include <stdlib.h> /* Bibliotheques pour les fonctions manipulant les chaines de caracteres (strcpy(), strcmp(), strchr(), etc.) */ #include <string.h> /* Bibliotheques pour avoir acces a la variable globale errno */ #include <errno.h> /* Bibliotheques pour la programmation socket */ #include <sys/types.h> #include <sys/socket.h> /* Bibliotheque contenant entre autres les structures des adresses */ #include <netinet/in.h> /* Bibliotheque pour la fonction getaddrinfo() */ #include <netdb.h> /* Bibliotheques pour les ouvertures/lectures/ecritures sur fichiers */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include <chrono> #include <iostream> #define MAXBUFF_SIZE 65507 unsigned int totalrcvd = 0; // Nombre de bytes reçu depuis le lancement du server unsigned int rcvdBytes = 0; // Nombre de bytes reçu durant le derniers pas de temps /* Affiche un message d'erreur avec perror. Cela permet de voir le type d'erreur. * perror() interprete la valeur de errno. * Finit par un exit. exit_status inique le type d'erreur (1: fonction, 2: appel systeme reseau, 3: erreur sur fichier, 4: autres appels systeme). */ void erreur(char* string, int exit_status) { perror(string); exit(exit_status); } // Structure permettant le bon fonctionnement du thread typedef struct { int sockfd; char * buffer; size_t buffersize; int flags; struct sockaddr_in6 clientAddr; int size; } recvThreadStruct; // Fonction gérant la réception des messages void receptionLoop(int sockfd, char * buffer, size_t buffersize, int flags, struct sockaddr_in6 clientAddr, int size){ int nbBytes; while((nbBytes=recvfrom(sockfd, buffer, buffersize, 0, (struct sockaddr *) &clientAddr, (socklen_t *) &size)) >0 ){ buffer[nbBytes]='\0';//Au cas ou... //printf("Just received nbBytes: %d\n",nbBytes); if(nbBytes<0) erreur("Erreur recvfrom",2); totalrcvd+=nbBytes; rcvdBytes+=nbBytes; } } // Fonction appelée par le thread void* threadrcvUDP( void* _r){ recvThreadStruct r; r = *(recvThreadStruct*) _r; receptionLoop(r.sockfd, r.buffer, r.buffersize, r.flags, r.clientAddr, r.size); } int main(int argc, char* argv[]) { int sockfd, size, nbBytes, error; struct sockaddr_in6 serverAddr, clientAddr; struct addrinfo hints, *res; struct ip_mreq multiaddr; struct ipv6_mreq multiaddr6; pthread_t threadId; recvThreadStruct recvStruct; std::chrono::duration<double> reportTime; if (argc == 2){ reportTime = std::chrono::duration<double>(atof(argv[1])) ; } else { reportTime = std::chrono::duration<double>(1.0) ; } // On prévoit le buffer le plus grand possible en réception char buffer[MAXBUFF_SIZE]; // Si on veux retourner une réponse pré-enregistrée // char *reponse="Bien recu\n"; memset(&hints,0,sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags |= AI_NUMERICHOST; fprintf(stderr,"ICI000\n"); if((error=getaddrinfo(NULL,"1080",&hints,&res))!=0) gai_strerror(error); if((sockfd=socket(res->ai_family, res->ai_socktype, res->ai_protocol))<0) erreur("Erreur socket():",2); fprintf(stderr,"ICI\n"); if(argc>1) { //Multicast IPv4 if(argv[1][0]=='2'){//Le 2 c'est celui de 225.0.0.1 multiaddr.imr_multiaddr.s_addr = htonl(0xe1000001);//225.0.0.1 multiaddr.imr_interface.s_addr = htonl(INADDR_ANY); setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &multiaddr, sizeof(multiaddr)); } //Multicast IPv6 - A voir plus tard encore des soucis du cote du client if(argv[1][0]=='f'){//Le f c'est celui de ff02::A:A --> adresse multicast scope link memcpy(&multiaddr6.ipv6mr_multiaddr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, sizeof(struct in6_addr));//ff02::A:A multiaddr6.ipv6mr_interface = 0; setsockopt(sockfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &multiaddr6, sizeof(multiaddr6)); } } if(bind(sockfd, (struct sockaddr *) res->ai_addr, res->ai_addrlen)<0) erreur("Erreur bind()",2); size=sizeof(clientAddr);//N'était pas nécessaire en IPv4 recvStruct.sockfd =sockfd; recvStruct.buffer =buffer; recvStruct.buffersize=MAXBUFF_SIZE; recvStruct.flags =0; recvStruct.clientAddr =clientAddr; recvStruct.size =size; auto start = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now(); std::chrono::duration<double> elapsed; pthread_create(&threadId, NULL, threadrcvUDP, &recvStruct); while(1){ now = std::chrono::steady_clock::now(); elapsed = now - start; if ( elapsed >= reportTime ){ printf("Total bytes received so far : %u \n",totalrcvd); printf("\tTotal bytes received last %lf secondes : %u \n",reportTime,rcvdBytes); printf ("\t Throughput : %lf bytes/s\n",rcvdBytes/elapsed.count()); start=now; rcvdBytes=0; } } // Ne compile pas avec g++ // La socket se ferme de toute façon à la fin du programme ... //close(sockfd); return(0); }![]()
Partager