Mon répertoire de travail pour le BE Réseau de 3 MIC à l'INSA Toulouse
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mictcp.c 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. #include <mictcp.h>
  2. #include <api/mictcp_core.h>
  3. /*
  4. TP BE Réseaux
  5. À L'ATTENTION DU CORRECTEUR :
  6. - Ce fichier est le seul fichier que j'ai modifié
  7. - La v1 a été implémenté et fonctionne avec tsock_texte et
  8. tsock_video.
  9. - La v2 a été implémentée et reprise de perte ok. Pour tester la
  10. v2, vous devez faire quelques modifications :
  11. * Régler les parametres comme suivant :
  12. MOD_SEQ_NUM = 2
  13. ADMITTED_LOSS_RATE = 0
  14. * Il faut décommenter les lignes commençant par // [V2]
  15. Ces lignes sont dans la fonction process_received_PDU().
  16. - La v3 est implémentée et fonctionnelle. ADMITTED_LOSS_RATE permet de
  17. parametrer le taux de paquets perdus abandonnés. S'il vaut 5, alors 5%
  18. des paquets perdus ne seront pas renvoyés. Pour tester la v3, passez :
  19. * DO_HANDSHAKE = 0
  20. Utiliser cette version permet d'avoir moins de ralentissements, mais au
  21. prix de coupure et des pertes d'informations.
  22. - La v4.1 est implémentée et fonctionnelle. Le handshake permet également
  23. d'échanger le taux de pertes admissibles. Pour tester la v3, le handshake
  24. peut être désactivé avec DO_HANDSHAKE = 0.
  25. On peut faire plusieurs remarques sur le programme final :
  26. - Le taux de pertes admis (ADMITTED_LOSS_RATE) est calculé à chaque
  27. itération à partir de l'ensemble des pertes et des envoies fait jusqu'à
  28. présent. On aurait pu implémenter une fenêtre (e.g. avec une pile FIFO,
  29. et ça serait plus pertinent pour de longues transmissions) mais il est alors
  30. plus difficile d'observer les effets de la v3 et de se s'arrurer que les
  31. taux de pertes admises sont corrects.
  32. - La gestion des pertes admissibles est entièrement gérée par la source
  33. et fonctionne sur le principe du stop and wait uniquement.
  34. Améliorations possibles :
  35. Je vous propose ici quelques idées pour aller plus loin que je n'ai pas eu
  36. le temps d'implémenter :
  37. - Mettre en place de l'asynchronisme entre le client et MIC TCP. Cela pourrait
  38. être fait avec un buffer créant une file d'attente avant le stop and wait.
  39. - Mettre en place le multiplexage, en passant toutes les variables globales
  40. dans un struct pour chaque socket et utiliser le numéro de port pour identifier
  41. à quel socket un paquet appartient.
  42. - Utiliser le RTT plutôt qu'un timeout fixe
  43. - Regarder l'implémentation de TCP pour le remplacer par MIC-TCP avec par exemple
  44. l'utilisation de LD_PRELOAD (bon ça, c'est vraiment ambitieux).
  45. */
  46. /* RÉGLAGE DES FONCTIONNALITÉES */
  47. // Taux de perte simulé (%)
  48. #define LOSS_RATE 10
  49. // Taux de perte admis (%)(pour la v3)
  50. #define ADMITTED_LOSS_RATE 5
  51. // Nombre de numéros de séquence différents possible (v3 : 2147483646)
  52. #define MOD_SEQ_NUM 2147483646
  53. // 0: Ne pas utiliser les fonctions connect() et accept()
  54. // 1: Utiliser ces fonctions (facultatif dans le BE)
  55. #define DO_HANDSHAKE 1
  56. // Niveau de verbositée: 0, faible; 1, fort
  57. #define DEBUG 1
  58. // Autres parametres
  59. #define MAX_SOCK 10
  60. #define TIMEOUT 2
  61. #define N_S 0
  62. /// Variable globale
  63. // Contient l'ensemble des sockets créés, dans les faits
  64. // seul l'utilisation d'un seul socket a été implémenté (n°0)
  65. mic_tcp_sock tab_sock[MAX_SOCK];
  66. // Contient le loss rate connu
  67. int admitted_loss_rate = 0;
  68. // Numéros de séquence
  69. int seq_num = 0;
  70. int ack_num = 0;
  71. // Compteur permettant de savoir si des packets
  72. // ont été perdus. --> Aucune pertes pour la v2.
  73. int rcv_c = 0; /* Compteur de paquets reçus */
  74. int lss_c = 0; /* Compteur de paquets perdus */
  75. int snd_c = 0; /* Compteur de paquets envoyés */
  76. // Compteur pour la v3
  77. int acc_c = 0; /* Compteur des pertes renvoyés */
  78. int iac_c = 1; /* Compteur des pertes abandonnée */
  79. /*
  80. * Fonction initialisant toutes les composantes d'un PDU
  81. * à zéro.
  82. */
  83. void reset_pdu(mic_tcp_pdu *pdu)
  84. {
  85. (*pdu).header.source_port = 0;
  86. (*pdu).header.dest_port = 0;
  87. (*pdu).header.seq_num = 0;
  88. (*pdu).header.ack_num = 0;
  89. (*pdu).header.syn = 0;
  90. (*pdu).header.ack = 0;
  91. (*pdu).header.fin = 0;
  92. (*pdu).payload.data = NULL;
  93. (*pdu).payload.size = 0;
  94. }
  95. /*
  96. * Permet de créer un socket entre l’application et MIC-TCP
  97. * Retourne le descripteur du socket ou bien -1 en cas d'erreur
  98. */
  99. int mic_tcp_socket(start_mode sm)
  100. {
  101. if (DEBUG)
  102. {
  103. printf("[MIC-TCP] Appel de la fonction: ");
  104. printf(__FUNCTION__);
  105. printf("\n");
  106. }
  107. int result = -1;
  108. mic_tcp_sock s;
  109. result = initialize_components(sm); /* Appel obligatoire */
  110. s.fd = 0;
  111. if (result != -1)
  112. result = s.fd;
  113. set_loss_rate(LOSS_RATE);
  114. return result;
  115. }
  116. /*
  117. * Permet d’attribuer une adresse à un socket.
  118. * Retourne 0 si succès, et -1 en cas d’échec
  119. */
  120. int mic_tcp_bind(int socket, mic_tcp_sock_addr addr)
  121. {
  122. if (DEBUG)
  123. {
  124. printf("[MIC-TCP] Appel de la fonction: ");
  125. printf(__FUNCTION__);
  126. printf("\n");
  127. }
  128. // On test que l'identifiant donné est bon
  129. if (socket < 0 || socket >= MAX_SOCK)
  130. {
  131. return -1;
  132. }
  133. tab_sock[socket].state = IDLE;
  134. tab_sock[socket].addr = addr;
  135. return 0;
  136. }
  137. /*
  138. * Met le socket en état d'acceptation de connexions
  139. * Retourne 0 si succès, -1 si erreur
  140. */
  141. // Fonction implélentée dans le cadre de la v4
  142. int mic_tcp_accept(int socket, mic_tcp_sock_addr *addr)
  143. {
  144. if (DEBUG)
  145. {
  146. printf("[MIC-TCP] Appel de la fonction: ");
  147. printf(__FUNCTION__);
  148. printf("\n");
  149. }
  150. int success = 0;
  151. if (!DO_HANDSHAKE) {
  152. admitted_loss_rate = ADMITTED_LOSS_RATE;
  153. goto skip;
  154. }
  155. mic_tcp_pdu pdu;
  156. // On attend un SYN, process_received_PDU() doit nous le donner
  157. while (tab_sock[socket].state != SYN_RECEIVED) {}
  158. printf("[CONNECTION STATE] SYN RCV\n");
  159. // Construction du SYN ACK
  160. reset_pdu(&pdu);
  161. pdu.header.source_port = tab_sock[socket].addr.port;
  162. pdu.header.dest_port = addr->port;
  163. pdu.header.syn = 1;
  164. pdu.header.ack = 1;
  165. // On l'envoie
  166. if ((success = IP_send(pdu, *addr)) == -1) {
  167. return -1;
  168. }
  169. printf("[CONNECTION STATE] SYNACK sent.\n");
  170. // On attend l'ACK
  171. while (tab_sock[socket].state != ESTABLISHED) {
  172. IP_send(pdu, *addr);
  173. printf("[CONNECTION STATE] SYNACK re-sent.\n");
  174. }
  175. printf("[CONNECTION STATE] ESTABLISHED\n");
  176. skip:
  177. tab_sock[socket].state = ESTABLISHED;
  178. return success;
  179. }
  180. /*
  181. * Permet de réclamer l’établissement d’une connexion
  182. * Retourne 0 si la connexion est établie, et -1 en cas d’échec
  183. */
  184. // Fonction implélentée dans le cadre de la v4
  185. int mic_tcp_connect(int socket, mic_tcp_sock_addr addr)
  186. {
  187. if (DEBUG)
  188. {
  189. printf("[MIC-TCP] Appel de la fonction: ");
  190. printf(__FUNCTION__);
  191. printf("\n");
  192. }
  193. admitted_loss_rate = ADMITTED_LOSS_RATE;
  194. if (!DO_HANDSHAKE) goto skip;
  195. int success = -1;
  196. mic_tcp_pdu pdu, pdu_rcv;
  197. // On vérifie que notre socket est prêt
  198. if (tab_sock[socket].state != IDLE){
  199. return -1;
  200. }
  201. // On construit notre pdu SYN et on l'envoie
  202. printf("[CONNECTION STATE] SYN\n");
  203. reset_pdu(&pdu);
  204. pdu.header.dest_port = addr.port;
  205. pdu.header.syn = 1;
  206. pdu.payload.data = (char *) &admitted_loss_rate;
  207. pdu.payload.size = 4;
  208. printf("[CONNECTION STATE] Config done. Sending...");
  209. if ((success = IP_send(pdu, addr)) == -1){
  210. printf("Fail !\n");
  211. return -1;
  212. }
  213. printf("Done.\n");
  214. // On attend le SYN ACK
  215. while (1){
  216. printf("[CONNECTION STATE] Wait SYNACK (d.: %d)\n", success);
  217. reset_pdu(&pdu_rcv);
  218. success = IP_recv(&pdu_rcv, &addr, TIMEOUT);
  219. if (pdu_rcv.header.syn == 1 && pdu_rcv.header.ack == 1){
  220. printf("[CONNECTION STATE] SYNACK, sending ACK.\n");
  221. pdu.header.syn = 0;
  222. pdu.header.ack = 1;
  223. if ((success = IP_send(pdu, addr)) == -1){
  224. return -1;
  225. }
  226. break;
  227. }
  228. IP_send(pdu, addr);
  229. }
  230. // Connecté !
  231. skip:
  232. tab_sock[socket].state = ESTABLISHED;
  233. tab_sock[socket].addr = addr;
  234. if (DEBUG) printf("[CONNECTION STATE] ESTABLISHED\n");
  235. return 0;
  236. }
  237. /*
  238. * Fonction determinant si une perte est acceptable ou non.
  239. * Elle effectue des calculs sur integer uniquement pour
  240. * augmenter sa réactivité.
  241. */
  242. int is_acceptable(){
  243. return acc_c * 1000000 / (10000 * (iac_c+acc_c)) < admitted_loss_rate && ADMITTED_LOSS_RATE;
  244. }
  245. /*
  246. * Permet de réclamer l’envoi d’une donnée applicative
  247. * Retourne la taille des données envoyées, et -1 en cas d'erreur
  248. */
  249. int mic_tcp_send(int mic_sock, char *mesg, int mesg_size)
  250. {
  251. if (DEBUG)
  252. {
  253. printf("[MIC-TCP] Appel de la fonction: ");
  254. printf(__FUNCTION__);
  255. printf("\n");
  256. }
  257. if (tab_sock[mic_sock].state != ESTABLISHED)
  258. return -1;
  259. mic_tcp_pdu pdu, rcv;
  260. int sq = seq_num;
  261. int sent_size = -1;
  262. seq_num = (seq_num + 1) % MOD_SEQ_NUM;
  263. reset_pdu(&pdu);
  264. pdu.header.dest_port = tab_sock[mic_sock].addr.port;
  265. pdu.header.seq_num = sq;
  266. pdu.payload.data = mesg;
  267. pdu.payload.size = mesg_size;
  268. int ok = 0;
  269. int counted = 0;
  270. while (!ok)
  271. {
  272. if (DEBUG) printf("[DEBUG: mic_tcp_send] Envoi du paquet N°%d...\n", sq);
  273. sent_size = IP_send(pdu, tab_sock[mic_sock].addr);
  274. if (sent_size <= 0){
  275. printf("[ERREUR] Erreur send().\n");
  276. return -1;
  277. }
  278. if (DEBUG) printf("[DEBUG: mic_tcp_send] Attente de l'ACK n°%d...\n", sq);
  279. reset_pdu(&rcv);
  280. IP_recv(&rcv, &(tab_sock[mic_sock].addr), TIMEOUT);
  281. // Doit-on ne renvoyer pas le paquet ?
  282. if (rcv.header.ack_num == sq && rcv.header.ack == 1) {
  283. // Envoi ok.
  284. ok++;
  285. snd_c++;
  286. if (DEBUG) printf("[DEBUG V3] Envoi ok : n°%d (%d/%d)\n", sq, acc_c, iac_c);
  287. }
  288. // Conditions pour la v3 uniquement.
  289. else if (is_acceptable()) {
  290. // Envoi nok, mais acceptable
  291. ok++;
  292. lss_c++;
  293. acc_c++;
  294. if (DEBUG) printf("[DEBUG V3] Perte acceptable : n°%d (%d/%d)\n", sq, acc_c, iac_c);
  295. }
  296. else {
  297. // Envoi nok, inacceptable
  298. if (!counted) {
  299. iac_c++;
  300. counted++;
  301. }
  302. if (DEBUG) printf("[DEBUG V3] Perte inacceptable : n°%d (%d/%d)\n", sq, acc_c, iac_c);
  303. }
  304. }
  305. if (DEBUG) printf("[DEBUG] ACK n°%d reçu. Nb de pckt send : %d\n", rcv.header.ack_num, snd_c);
  306. return sent_size;
  307. }
  308. /*
  309. * Permet à l’application réceptrice de réclamer la récupération d’une donnée
  310. * stockée dans les buffers de réception du socket
  311. * Retourne le nombre d’octets lu ou bien -1 en cas d’erreur
  312. * NB : cette fonction fait appel à la fonction app_buffer_get()
  313. */
  314. int mic_tcp_recv(int socket, char *mesg, int max_mesg_size)
  315. {
  316. if (DEBUG)
  317. {
  318. printf("[MIC-TCP] Appel de la fonction: ");
  319. printf(__FUNCTION__);
  320. printf("\n");
  321. }
  322. if (tab_sock[socket].state != ESTABLISHED)
  323. return -1;
  324. mic_tcp_payload payload;
  325. payload.data = mesg;
  326. payload.size = max_mesg_size;
  327. int read_size = app_buffer_get(payload);
  328. if (DEBUG) printf("[DEBUG] Read: read_size = %d\n", read_size);
  329. if (read_size > 0) rcv_c++;
  330. if (DEBUG) printf("[DEBUG] Nb de pckt recu : %d\n", rcv_c);
  331. return read_size;
  332. }
  333. /*
  334. * Permet de réclamer la destruction d’un socket.
  335. * Engendre la fermeture de la connexion suivant le modèle de TCP.
  336. * Retourne 0 si tout se passe bien et -1 en cas d'erreur
  337. */
  338. // Non implémentée cette année, retourne 0 dans tous les cas
  339. int mic_tcp_close(int socket)
  340. {
  341. if (DEBUG)
  342. {
  343. printf("[MIC-TCP] Appel de la fonction : ");
  344. printf(__FUNCTION__);
  345. printf("\n");
  346. }
  347. tab_sock[socket].state = CLOSED;
  348. return 0;
  349. }
  350. /*
  351. * Traitement d’un PDU MIC-TCP reçu (mise à jour des numéros de séquence
  352. * et d'acquittement, etc.) puis insère les données utiles du PDU dans
  353. * le buffer de réception du socket. Cette fonction utilise la fonction
  354. * app_buffer_put().
  355. */
  356. void process_received_PDU(mic_tcp_pdu pdu, mic_tcp_sock_addr addr)
  357. {
  358. if (DEBUG)
  359. {
  360. printf("[MIC-TCP] Appel de la fonction: ");
  361. printf(__FUNCTION__);
  362. printf("\n");
  363. }
  364. switch (tab_sock[N_S].state)
  365. {
  366. case IDLE:
  367. if (pdu.header.syn == 1)
  368. {
  369. admitted_loss_rate = *((int*)pdu.payload.data);
  370. if (DEBUG) printf("[CONNECTION STATE] Getting loss rate : %d\n", admitted_loss_rate);
  371. tab_sock[N_S].state = SYN_RECEIVED;
  372. }
  373. else
  374. {
  375. if (DEBUG)
  376. printf("[DEBUG] Paquet ignoré.\n");
  377. }
  378. break;
  379. case SYN_RECEIVED:
  380. if (pdu.header.ack == 1)
  381. {
  382. tab_sock[N_S].state = ESTABLISHED;
  383. }
  384. else
  385. {
  386. if (DEBUG)
  387. printf("[DEBUG] Paquet ignoré.\n");
  388. }
  389. break;
  390. case CLOSED:
  391. if (DEBUG) printf("[DEBUG] Reception d'un paquet sur un port fermé\n");
  392. break;
  393. case ESTABLISHED:
  394. if (DEBUG) printf("[DEBUG: ESTABLISHED] Rcv n°%d, wait n° %d\n", pdu.header.seq_num, ack_num);
  395. // [V2] if (pdu.header.seq_num == ack_num)
  396. if (pdu.header.seq_num >= ack_num)
  397. {
  398. if (DEBUG) printf("[DEBUG] Num ok. In buffer.\n");
  399. app_buffer_put(pdu.payload);
  400. // [V2] ack_num = (ack_num + 1) % MOD_SEQ_NUM;
  401. ack_num = pdu.header.seq_num + 1;
  402. // Préparation du ACK
  403. mic_tcp_pdu ack;
  404. reset_pdu(&ack);
  405. int s;
  406. ack.header.source_port = pdu.header.source_port;
  407. ack.header.dest_port = addr.port;
  408. ack.header.ack_num = pdu.header.seq_num;
  409. ack.header.ack = 1;
  410. if (DEBUG) printf("[DEBUG] Envoi du ACK\n");
  411. if ((s = IP_send(ack, addr)) == -1)
  412. {
  413. printf("[ERREUR] Envoi ACK fail.\n");
  414. return;
  415. }
  416. }
  417. else
  418. {
  419. if (DEBUG) printf("[DEBUG] No go.\n");
  420. mic_tcp_pdu ack;
  421. reset_pdu(&ack);
  422. int s;
  423. ack.header.source_port = pdu.header.source_port;
  424. ack.header.dest_port = addr.port;
  425. ack.header.ack = 1;
  426. ack.header.ack_num = pdu.header.seq_num;
  427. if (DEBUG) printf("[DEBUG] Envoi du ACK N°%d\n", ack.header.ack_num);
  428. if ((s = IP_send(ack, addr)) == -1)
  429. {
  430. printf("[ERREUR] Envoi ACK fail.\n");
  431. return;
  432. }
  433. if (DEBUG) printf("[DEBUG] Ignored.\n");
  434. }
  435. break;
  436. default:
  437. break;
  438. }
  439. }