123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- #include <mictcp.h>
- #include <api/mictcp_core.h>
-
- /*
- TP BE Réseaux
-
- À L'ATTENTION DU CORRECTEUR :
- - Ce fichier est le seul fichier que j'ai modifié
-
- - La v1 a été implémenté et fonctionne avec tsock_texte et
- tsock_video.
-
- - La v2 a été implémentée et reprise de perte ok. Pour tester la
- v2, vous devez faire quelques modifications :
- * Régler les parametres comme suivant :
- MOD_SEQ_NUM = 2
- ADMITTED_LOSS_RATE = 0
- * Il faut décommenter les lignes commençant par // [V2]
- Ces lignes sont dans la fonction process_received_PDU().
-
- - La v3 est implémentée et fonctionnelle. ADMITTED_LOSS_RATE permet de
- parametrer le taux de paquets perdus abandonnés. S'il vaut 5, alors 5%
- des paquets perdus ne seront pas renvoyés. Pour tester la v3, passez :
- * DO_HANDSHAKE = 0
- Utiliser cette version permet d'avoir moins de ralentissements, mais au
- prix de coupure et des pertes d'informations.
-
- - La v4.1 est implémentée et fonctionnelle. Le handshake permet également
- d'échanger le taux de pertes admissibles. Pour tester la v3, le handshake
- peut être désactivé avec DO_HANDSHAKE = 0.
- On peut faire plusieurs remarques sur le programme final :
- - Le taux de pertes admis (ADMITTED_LOSS_RATE) est calculé à chaque
- itération à partir de l'ensemble des pertes et des envoies fait jusqu'à
- présent. On aurait pu implémenter une fenêtre (e.g. avec une pile FIFO,
- et ça serait plus pertinent pour de longues transmissions) mais il est alors
- plus difficile d'observer les effets de la v3 et de se s'arrurer que les
- taux de pertes admises sont corrects.
- - La gestion des pertes admissibles est entièrement gérée par la source
- et fonctionne sur le principe du stop and wait uniquement.
-
- Améliorations possibles :
- Je vous propose ici quelques idées pour aller plus loin que je n'ai pas eu
- le temps d'implémenter :
- - Mettre en place de l'asynchronisme entre le client et MIC TCP. Cela pourrait
- être fait avec un buffer créant une file d'attente avant le stop and wait.
- - Mettre en place le multiplexage, en passant toutes les variables globales
- dans un struct pour chaque socket et utiliser le numéro de port pour identifier
- à quel socket un paquet appartient.
- - Utiliser le RTT plutôt qu'un timeout fixe
- - Regarder l'implémentation de TCP pour le remplacer par MIC-TCP avec par exemple
- l'utilisation de LD_PRELOAD (bon ça, c'est vraiment ambitieux).
-
- */
-
-
- /* RÉGLAGE DES FONCTIONNALITÉES */
- // Taux de perte simulé (%)
- #define LOSS_RATE 10
-
- // Taux de perte admis (%)(pour la v3)
- #define ADMITTED_LOSS_RATE 5
-
- // Nombre de numéros de séquence différents possible (v3 : 2147483646)
- #define MOD_SEQ_NUM 2147483646
-
- // 0: Ne pas utiliser les fonctions connect() et accept()
- // 1: Utiliser ces fonctions (facultatif dans le BE)
- #define DO_HANDSHAKE 1
-
- // Niveau de verbositée: 0, faible; 1, fort
- #define DEBUG 1
-
- // Autres parametres
- #define MAX_SOCK 10
- #define TIMEOUT 2
- #define N_S 0
-
-
-
- /// Variable globale
- // Contient l'ensemble des sockets créés, dans les faits
- // seul l'utilisation d'un seul socket a été implémenté (n°0)
- mic_tcp_sock tab_sock[MAX_SOCK];
- // Contient le loss rate connu
- int admitted_loss_rate = 0;
-
- // Numéros de séquence
- int seq_num = 0;
- int ack_num = 0;
-
-
- // Compteur permettant de savoir si des packets
- // ont été perdus. --> Aucune pertes pour la v2.
- int rcv_c = 0; /* Compteur de paquets reçus */
- int lss_c = 0; /* Compteur de paquets perdus */
-
- int snd_c = 0; /* Compteur de paquets envoyés */
-
- // Compteur pour la v3
- int acc_c = 0; /* Compteur des pertes renvoyés */
- int iac_c = 1; /* Compteur des pertes abandonnée */
-
-
-
- /*
- * Fonction initialisant toutes les composantes d'un PDU
- * à zéro.
- */
- void reset_pdu(mic_tcp_pdu *pdu)
- {
- (*pdu).header.source_port = 0;
- (*pdu).header.dest_port = 0;
- (*pdu).header.seq_num = 0;
- (*pdu).header.ack_num = 0;
- (*pdu).header.syn = 0;
- (*pdu).header.ack = 0;
- (*pdu).header.fin = 0;
- (*pdu).payload.data = NULL;
- (*pdu).payload.size = 0;
- }
-
-
- /*
- * Permet de créer un socket entre l’application et MIC-TCP
- * Retourne le descripteur du socket ou bien -1 en cas d'erreur
- */
- int mic_tcp_socket(start_mode sm)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- int result = -1;
- mic_tcp_sock s;
- result = initialize_components(sm); /* Appel obligatoire */
- s.fd = 0;
-
- if (result != -1)
- result = s.fd;
-
- set_loss_rate(LOSS_RATE);
- return result;
- }
-
-
- /*
- * Permet d’attribuer une adresse à un socket.
- * Retourne 0 si succès, et -1 en cas d’échec
- */
- int mic_tcp_bind(int socket, mic_tcp_sock_addr addr)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- // On test que l'identifiant donné est bon
- if (socket < 0 || socket >= MAX_SOCK)
- {
- return -1;
- }
-
- tab_sock[socket].state = IDLE;
- tab_sock[socket].addr = addr;
- return 0;
- }
-
-
- /*
- * Met le socket en état d'acceptation de connexions
- * Retourne 0 si succès, -1 si erreur
- */
- // Fonction implélentée dans le cadre de la v4
- int mic_tcp_accept(int socket, mic_tcp_sock_addr *addr)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- int success = 0;
-
- if (!DO_HANDSHAKE) {
- admitted_loss_rate = ADMITTED_LOSS_RATE;
- goto skip;
- }
-
- mic_tcp_pdu pdu;
-
- // On attend un SYN, process_received_PDU() doit nous le donner
- while (tab_sock[socket].state != SYN_RECEIVED) {}
- printf("[CONNECTION STATE] SYN RCV\n");
-
- // Construction du SYN ACK
- reset_pdu(&pdu);
- pdu.header.source_port = tab_sock[socket].addr.port;
- pdu.header.dest_port = addr->port;
- pdu.header.syn = 1;
- pdu.header.ack = 1;
-
- // On l'envoie
-
- if ((success = IP_send(pdu, *addr)) == -1) {
- return -1;
- }
- printf("[CONNECTION STATE] SYNACK sent.\n");
-
- // On attend l'ACK
- while (tab_sock[socket].state != ESTABLISHED) {
- IP_send(pdu, *addr);
- printf("[CONNECTION STATE] SYNACK re-sent.\n");
- }
- printf("[CONNECTION STATE] ESTABLISHED\n");
-
-
- skip:
- tab_sock[socket].state = ESTABLISHED;
- return success;
- }
-
-
- /*
- * Permet de réclamer l’établissement d’une connexion
- * Retourne 0 si la connexion est établie, et -1 en cas d’échec
- */
- // Fonction implélentée dans le cadre de la v4
- int mic_tcp_connect(int socket, mic_tcp_sock_addr addr)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- admitted_loss_rate = ADMITTED_LOSS_RATE;
-
- if (!DO_HANDSHAKE) goto skip;
-
- int success = -1;
- mic_tcp_pdu pdu, pdu_rcv;
-
- // On vérifie que notre socket est prêt
- if (tab_sock[socket].state != IDLE){
- return -1;
- }
-
- // On construit notre pdu SYN et on l'envoie
- printf("[CONNECTION STATE] SYN\n");
- reset_pdu(&pdu);
- pdu.header.dest_port = addr.port;
- pdu.header.syn = 1;
- pdu.payload.data = (char *) &admitted_loss_rate;
- pdu.payload.size = 4;
-
- printf("[CONNECTION STATE] Config done. Sending...");
- if ((success = IP_send(pdu, addr)) == -1){
- printf("Fail !\n");
- return -1;
- }
- printf("Done.\n");
-
- // On attend le SYN ACK
- while (1){
- printf("[CONNECTION STATE] Wait SYNACK (d.: %d)\n", success);
- reset_pdu(&pdu_rcv);
- success = IP_recv(&pdu_rcv, &addr, TIMEOUT);
- if (pdu_rcv.header.syn == 1 && pdu_rcv.header.ack == 1){
- printf("[CONNECTION STATE] SYNACK, sending ACK.\n");
- pdu.header.syn = 0;
- pdu.header.ack = 1;
- if ((success = IP_send(pdu, addr)) == -1){
- return -1;
- }
- break;
- }
- IP_send(pdu, addr);
- }
-
-
- // Connecté !
- skip:
- tab_sock[socket].state = ESTABLISHED;
- tab_sock[socket].addr = addr;
-
- if (DEBUG) printf("[CONNECTION STATE] ESTABLISHED\n");
-
- return 0;
- }
-
- /*
- * Fonction determinant si une perte est acceptable ou non.
- * Elle effectue des calculs sur integer uniquement pour
- * augmenter sa réactivité.
- */
- int is_acceptable(){
- return acc_c * 1000000 / (10000 * (iac_c+acc_c)) < admitted_loss_rate && ADMITTED_LOSS_RATE;
- }
-
- /*
- * Permet de réclamer l’envoi d’une donnée applicative
- * Retourne la taille des données envoyées, et -1 en cas d'erreur
- */
- int mic_tcp_send(int mic_sock, char *mesg, int mesg_size)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- if (tab_sock[mic_sock].state != ESTABLISHED)
- return -1;
-
- mic_tcp_pdu pdu, rcv;
- int sq = seq_num;
- int sent_size = -1;
- seq_num = (seq_num + 1) % MOD_SEQ_NUM;
-
- reset_pdu(&pdu);
- pdu.header.dest_port = tab_sock[mic_sock].addr.port;
- pdu.header.seq_num = sq;
-
- pdu.payload.data = mesg;
- pdu.payload.size = mesg_size;
-
- int ok = 0;
- int counted = 0;
- while (!ok)
- {
- if (DEBUG) printf("[DEBUG: mic_tcp_send] Envoi du paquet N°%d...\n", sq);
- sent_size = IP_send(pdu, tab_sock[mic_sock].addr);
-
- if (sent_size <= 0){
- printf("[ERREUR] Erreur send().\n");
- return -1;
- }
-
- if (DEBUG) printf("[DEBUG: mic_tcp_send] Attente de l'ACK n°%d...\n", sq);
- reset_pdu(&rcv);
- IP_recv(&rcv, &(tab_sock[mic_sock].addr), TIMEOUT);
-
-
- // Doit-on ne renvoyer pas le paquet ?
- if (rcv.header.ack_num == sq && rcv.header.ack == 1) {
- // Envoi ok.
- ok++;
- snd_c++;
- if (DEBUG) printf("[DEBUG V3] Envoi ok : n°%d (%d/%d)\n", sq, acc_c, iac_c);
- }
- // Conditions pour la v3 uniquement.
- else if (is_acceptable()) {
- // Envoi nok, mais acceptable
- ok++;
- lss_c++;
- acc_c++;
- if (DEBUG) printf("[DEBUG V3] Perte acceptable : n°%d (%d/%d)\n", sq, acc_c, iac_c);
- }
- else {
- // Envoi nok, inacceptable
- if (!counted) {
- iac_c++;
- counted++;
- }
- if (DEBUG) printf("[DEBUG V3] Perte inacceptable : n°%d (%d/%d)\n", sq, acc_c, iac_c);
- }
- }
-
- if (DEBUG) printf("[DEBUG] ACK n°%d reçu. Nb de pckt send : %d\n", rcv.header.ack_num, snd_c);
-
- return sent_size;
- }
-
-
- /*
- * Permet à l’application réceptrice de réclamer la récupération d’une donnée
- * stockée dans les buffers de réception du socket
- * Retourne le nombre d’octets lu ou bien -1 en cas d’erreur
- * NB : cette fonction fait appel à la fonction app_buffer_get()
- */
- int mic_tcp_recv(int socket, char *mesg, int max_mesg_size)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- if (tab_sock[socket].state != ESTABLISHED)
- return -1;
-
- mic_tcp_payload payload;
- payload.data = mesg;
- payload.size = max_mesg_size;
-
- int read_size = app_buffer_get(payload);
- if (DEBUG) printf("[DEBUG] Read: read_size = %d\n", read_size);
- if (read_size > 0) rcv_c++;
- if (DEBUG) printf("[DEBUG] Nb de pckt recu : %d\n", rcv_c);
-
- return read_size;
- }
-
-
- /*
- * Permet de réclamer la destruction d’un socket.
- * Engendre la fermeture de la connexion suivant le modèle de TCP.
- * Retourne 0 si tout se passe bien et -1 en cas d'erreur
- */
- // Non implémentée cette année, retourne 0 dans tous les cas
- int mic_tcp_close(int socket)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction : ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- tab_sock[socket].state = CLOSED;
- return 0;
- }
-
-
- /*
- * Traitement d’un PDU MIC-TCP reçu (mise à jour des numéros de séquence
- * et d'acquittement, etc.) puis insère les données utiles du PDU dans
- * le buffer de réception du socket. Cette fonction utilise la fonction
- * app_buffer_put().
- */
- void process_received_PDU(mic_tcp_pdu pdu, mic_tcp_sock_addr addr)
- {
- if (DEBUG)
- {
- printf("[MIC-TCP] Appel de la fonction: ");
- printf(__FUNCTION__);
- printf("\n");
- }
-
- switch (tab_sock[N_S].state)
- {
- case IDLE:
- if (pdu.header.syn == 1)
- {
- admitted_loss_rate = *((int*)pdu.payload.data);
- if (DEBUG) printf("[CONNECTION STATE] Getting loss rate : %d\n", admitted_loss_rate);
- tab_sock[N_S].state = SYN_RECEIVED;
- }
- else
- {
- if (DEBUG)
- printf("[DEBUG] Paquet ignoré.\n");
- }
- break;
-
- case SYN_RECEIVED:
- if (pdu.header.ack == 1)
- {
- tab_sock[N_S].state = ESTABLISHED;
- }
- else
- {
- if (DEBUG)
- printf("[DEBUG] Paquet ignoré.\n");
- }
- break;
-
- case CLOSED:
- if (DEBUG) printf("[DEBUG] Reception d'un paquet sur un port fermé\n");
- break;
-
- case ESTABLISHED:
- if (DEBUG) printf("[DEBUG: ESTABLISHED] Rcv n°%d, wait n° %d\n", pdu.header.seq_num, ack_num);
-
- // [V2] if (pdu.header.seq_num == ack_num)
- if (pdu.header.seq_num >= ack_num)
- {
- if (DEBUG) printf("[DEBUG] Num ok. In buffer.\n");
- app_buffer_put(pdu.payload);
-
- // [V2] ack_num = (ack_num + 1) % MOD_SEQ_NUM;
- ack_num = pdu.header.seq_num + 1;
-
- // Préparation du ACK
- mic_tcp_pdu ack;
- reset_pdu(&ack);
- int s;
-
- ack.header.source_port = pdu.header.source_port;
- ack.header.dest_port = addr.port;
- ack.header.ack_num = pdu.header.seq_num;
- ack.header.ack = 1;
-
- if (DEBUG) printf("[DEBUG] Envoi du ACK\n");
- if ((s = IP_send(ack, addr)) == -1)
- {
- printf("[ERREUR] Envoi ACK fail.\n");
- return;
- }
- }
- else
- {
- if (DEBUG) printf("[DEBUG] No go.\n");
- mic_tcp_pdu ack;
- reset_pdu(&ack);
- int s;
-
- ack.header.source_port = pdu.header.source_port;
- ack.header.dest_port = addr.port;
-
- ack.header.ack = 1;
- ack.header.ack_num = pdu.header.seq_num;
-
- if (DEBUG) printf("[DEBUG] Envoi du ACK N°%d\n", ack.header.ack_num);
- if ((s = IP_send(ack, addr)) == -1)
- {
- printf("[ERREUR] Envoi ACK fail.\n");
- return;
- }
- if (DEBUG) printf("[DEBUG] Ignored.\n");
- }
- break;
-
- default:
- break;
- }
- }
|