[PYTHON] Observez le contrôle de congestion TCP avec ns-3

1.Tout d'abord

La plupart du trafic sur Internet serait contrôlé par ** TCP (Transmission Control Protocol) **. L'une des caractéristiques de TCP est que chaque nœud de transmission ajuste la quantité de données transmises en une seule fois en fonction de ** l'algorithme de contrôle de congestion **. Dans cet article, ns-3 simule le fonctionnement de chaque algorithme, et NumPy + [matplotlib] Visualisez avec (http://matplotlib.org/). Pour comparer les algorithmes de contrôle de congestion TCP, ns-3 a [tcp-variants-comparaison.cc](https://www.nsnam.org/docs/release/3.26/doxygen/tcp-variants- Un exemple de scénario appelé comparaison_8cc.html) est préparé. Cependant, si ce script de scénario est utilisé tel quel, il existe un problème en raison du fait que certaines variables d'intérêt dans cet article ne peuvent pas être surveillées (dans ns-3, elles sont appelées ** trace **). Par conséquent, dans cet article, nous allons présenter comment ajouter des informations de trace arbitraires au script de scénario.

TcpAll20.0-cwnd.png

Le code source de cet article est entièrement disponible sur Github.

[^ Congestion]: Un mot pour l'image de la congestion dans un réseau.

2. Construction de l'environnement

2.1 ns-3 (Network Simulator 3) ns-3 est un simulateur de réseau d'événements discrets open source. Il est développé pour une utilisation à des fins de recherche et d'enseignement. Cet article suppose l'environnement et la structure de répertoires créés dans l'article suivant.

Simulation de réseau à partir de ns-3.26

2.2 python Dans cet article, NumPy est utilisé pour le traitement des données, et matplotlib est utilisé pour le dessin graphique. Nous avons confirmé l'opération dans l'environnement suivant.

3. Contrôle de la congestion dans TCP

3.1 Aperçu

La figure ci-dessous est une image du contrôle de congestion TCP supposé dans cet article. Le nœud émetteur TCP (TCP Sender) dispose de la quantité de données en fonction de l'accusé de réception (accusé de réception, ACK) [^ retardé ACK] [^ numéro ACK] du nœud récepteur (récepteur TCP) et du temps d'aller-retour du signal (Round Trip Time, RTT). Ajustez (DATA).

[^ ACK retardé]: Par souci de simplicité, cet article ne considère pas ACK retardé (accusé de réception différé). Delayed ACK est une méthode pour améliorer l'efficacité d'utilisation du réseau en transmettant plusieurs ACK à la fois.

[^ ACKnumber]: Le numéro d'accusé de réception est strictement le numéro de séquence reçu + la taille du segment. Dans cet article, afin de rendre l'explication intuitive, nous utilisons un diagramme d'image qui ACK le numéro de segment de DATA tel quel.

model.png

A proprement parler, l'ajustement de la quantité de données peut être exprimé par la formule $ swnd = min (awnd, cwnd) $. Ici, $ swnd $ est la limite supérieure du nombre de DONNÉES que TCP Sender peut envoyer sans ACK, $ cwnd $ est la taille de la fenêtre (fenêtre d'encombrement) que TCP Sender ajuste de manière autonome, et $ et $ sont TCP. Il s'agit de la taille de la fenêtre (fenêtre publiée) annoncée par le récepteur. L'unité de la formule ci-dessus est appelée un segment et la taille d'un segment est déterminée par la négociation entre l'expéditeur TCP et le récepteur. Puisque $ awnd $ est souvent défini sur une très grande valeur, par souci de simplicité, cet article se concentrera uniquement sur $ cwnd $. Plus $ cwnd $ est grand, plus vous pouvez envoyer de données à la fois. TCP Sender prédit l'occupation du réseau avec le récepteur depuis ACK et RTT, et ajuste de manière autonome la taille de $ cwnd $. La stratégie d'ajustement pour $ cwnd $ est appelée ** contrôle de la congestion ** dans cet article. Le contrôle de la congestion est déterminé par deux facteurs: ** état de congestion [^ diagramme de transition d'état] ** (état de congestion) et ** algorithme ** (algorithme de contrôle de congestion). L'état d'encombrement indique l'état d'encombrement du réseau tel que * OPEN *, * DISORDER *, * RECOVER *, * LOSS *. L'algorithme décrit comment mettre à jour $ cwnd $ dans chaque état de congestion.

[^ Diagramme de transition d'état]: Il est différent du soi-disant diagramme de transition d'état TCP (machine à états finis). Le diagramme de transition d'état couvre le processus de l'établissement de la connexion à la déconnexion, tandis que l'état d'encombrement cible l'état d'encombrement pendant l'établissement de la connexion (ESTABLISHED).

3.2 État de congestion

Dans cet article, l'implémentation de ns-3 (~ / ns-3.26 / source / ns-3.26 / src / internet / model / tcp-socket-base.cc Basé sur docs / release / 3.26 / doxygen / tcp-socket-base_8cc.html)), les quatre types d'encombrement suivants sont supposés.

state.png

3.3 Algorithme de contrôle de la congestion

Dans cet article, les algorithmes de contrôle de congestion suivants sont supposés basés sur [l'implémentation] de ns-3 (https://www.nsnam.org/docs/models/html/tcp.html). TypeId est comme le nom de l'algorithme dans ns-3. Le code source est stocké dans ~ / ns-3.26 / source / ns-3.26 / src / internet / model respectivement.

algorithme TypeId Code source
NewReno TcpNewReno tcp-congestion-ops.cc
HighSpeed TcpHighSpeed tcp-highspeed.cc
Hybla TcpHybla tcp-hybla.cc
Westwood TcpWestwood tcp-westwood.cc
Westwood+ TcpWestwoodPlus tcp-westwood.cc
Vegas TcpVegas tcp-vegas.cc
Scalable TcpScalable tcp-scalable.cc
Veno TcpVeno tcp-veno.cc
Bic TcpBic tcp-bic.cc
YeAH TcpYeah tcp-yeah.cc
Illinois TcpIllinois tcp-illinois.cc
H-TCP TcpHtcp tcp-htcp.cc

Par exemple, l'un des algorithmes de contrôle de congestion les plus importants, New Reno (Reno), met à jour $ cwnd $ dans chaque état de congestion comme suit: Je vais.

Opportunité de renouvellement Formule de renouvellement
OPENLorsque ACK est reçu dans l'état (SS) $ cwnd \gets cwnd + 1$
OPENLorsque ACK est reçu dans l'état (CA) $ \displaystyle cwnd \gets cwnd + \frac{1}{cwnd}$
RECOVERYLors de la transition vers l'état ssth \gets max(\mathit{inflight} /2, 2 \cdot smss), cwnd \gets ssth +3
RECOVERYLorsqu'un ACK en double est reçu dans l'état $ \displaystyle cwnd \gets cwnd + 1$
RECOVERYDans l'état, recevez un nouvel ACK etOPENLors de la transition vers l'état $ cwnd \gets ssth$
LOSSLors de la transition vers l'état $ cwnd \gets smss$, ssth \gets max(\mathit{inflight} /2, 2 \cdot smss)

Ici, $ \ mathit {inflight} $ représente le montant total de DATA pour lequel ACK n'a pas été retourné, et $ smss $ représente la taille minimale du segment. De plus, pour plus de simplicité, les ACK partiels et complets ne sont pas pris en compte. Pour plus de détails sur le fonctionnement de NewReno, reportez-vous à la RFC6582. NewReno envoie efficacement les DONNÉES en augmentant $ cwnd $ à grande vitesse dans la phase de démarrage lent où la possibilité de congestion est faible, tandis que la phase d'évitement de congestion où la possibilité de congestion est élevée. En, nous avons adopté une stratégie pour éviter les embouteillages soudains en augmentant progressivement $ cwnd $. Veuillez noter que la formule de mise à jour déclenchée par la réception d'ACK est ** la formule de mise à jour d'ACK pour un segment ** (j'en étais accro ici). Par exemple, lorsque $ cwnd = 4 $, l'expéditeur TCP reçoit ACK [^ delayACK] pour DATA pour 4 segments, donc la mise à jour ci-dessus est effectuée ** 4 fois **. Il existe trois types d'implémentation TCP dans ns-3, mais dans cet article, ns-3 native ( Seuls les algorithmes implémentés dans src / internet / model) sont ciblés. En d'autres termes, le majeur [CUBIC] sur Linux (http://dl.acm.org/citation.cfm?id=1400105) et le majeur [CTCP] sur Windows (http://www.dcs.gla.ac.). uk / ~ lewis / CTCP.pdf) est hors de portée. Je voudrais les présenter séparément.

[^ ack]: ACK retardé.

4. Création d'un script de scénario

Dans ce chapitre, nous expliquerons l'exemple de scénario tcp-variantes-comparaison.cc basé sur celui-ci, ses problèmes et la version modifiée de my-tcp-variants-comparaison.cc.

4.1 Exemple de scénario basé

ns-3 est [tcp-variants-comparaison.cc](https://www.nsnam.org/docs/release/3.26/doxygen/tcp-variants-comparison_8cc] pour la comparaison des algorithmes de contrôle de congestion TCP. .html) Nous avons préparé un exemple de scénario appelé (situé dans ~ / ns-3.26 / source / ns-3.26 / examples / tcp /). Ce script de scénario peut suivre le changement d'heure des variables suivantes et l'afficher dans un fichier.

Dans ce qui suit, nous expliquerons le code source, en nous concentrant sur ** Trace **, qui est le thème de cet article.

Variables de traçage

Les lignes 58 à 70 définissent les variables utilisées pour le traçage. bool first * indique s'il faut ou non afficher la valeur initiale de la cible de trace, et Ptr <OutputStreamWrapper> * Stream indique le flux pour sortir la cible de trace dans un fichier, respectivement. ʻUint32_t * Value` Représente les variables qui sont temporairement utilisées lors du traitement des valeurs initiales à tracer.

tcp-variants-comparison.cc (lignes 58 à 70)


bool firstCwnd = true;
bool firstSshThr = true;
bool firstRtt = true;
bool firstRto = true;
Ptr<OutputStreamWrapper> cWndStream;
Ptr<OutputStreamWrapper> ssThreshStream;
Ptr<OutputStreamWrapper> rttStream;
Ptr<OutputStreamWrapper> rtoStream;
Ptr<OutputStreamWrapper> nextTxStream;
Ptr<OutputStreamWrapper> nextRxStream;
Ptr<OutputStreamWrapper> inFlightStream;
uint32_t cWndValue;
uint32_t ssThreshValue;

Définition d'une fonction de rappel pour le traçage

Les lignes 73 à 145 définissent la fonction de rappel de trace «* Tracer ()» et les lignes 147 à 202 définissent la fonction «Trace * ()» qui associe la fonction de rappel à la cible de trace. Je le ferai. Ici, nous allons expliquer l'utilisation de BytesInFlight, qui est l'une des cibles de trace, à titre d'exemple.

tcp-variants-comparison.cc (lignes 73 à 202)


static void
InFlightTracer (uint32_t old, uint32_t inFlight)
{
  *inFlightStream->GetStream () << Simulator::Now ().GetSeconds () << " " << inFlight << std::endl;
}

//=====Abréviation=====

static void
TraceInFlight (std::string &in_flight_file_name)
{
  AsciiTraceHelper ascii;
  inFlightStream = ascii.CreateFileStream (in_flight_file_name.c_str ());
  Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/BytesInFlight", MakeCallback (&InFlightTracer));
}

Dans ns-3, toutes les variables ʻAddTraceSource () dans le fichier source ( ns-3.26 / source / ns-3.26 / src / * / model / ) sont définies comme cibles de trace dans le script de scénario. pouvez. Par exemple, BytesInFlight ci-dessus est [ ~ / ns-3.26 / source / ns-3.26 / src / internet / model / tcp-socket-base.cc](https://www.nsnam.org/docs) /release/3.26/doxygen/tcp-socket-base_8cc.html) ʻAddTraceSource () . Chaque fois que la variable tracée est mise à jour, ns-3 appelle la fonction de rappel associée à cette variable. Par conséquent, pour définir la cible de trace, il est nécessaire de ** définir la fonction de rappel ** et de ** lier la cible de trace et la fonction de rappel **. En tant que fonction de rappel, une fonction telle que ʻInFlightTracer () ci-dessus est souvent utilisée. ʻInFlightTracer () est une fonction qui affiche l'heure actuelle (Simulator :: Now (). GetSeconds ()) et la valeur mise à jour (ʻinFlight) à chaque fois. Lorsque vous associez la cible de trace à la fonction de rappel, vous pouvez utiliser la syntaxe Config :: ConnectWithoutContext (variable, MakeCallback (& func))comme décrit dansTraceInFlight ()ci-dessus. Ici,variabledoit décrire le chemin de l'objet à tracer./ NodeList / 1 / $ ns3 :: TcpL4Protocol / SocketList / 0 / BytesInFlight signifie la variableBytesInFlight du socket SocketList 0, qui se bloque depuis le nœud NodeList`1. Pour plus de détails sur la méthode de traçage pour ns-3, reportez-vous à la section 1.10 du Manuel officiel.

Configuration du réseau

Définissez la configuration du réseau avec main () à partir de la 204ème ligne. Les détails sont donnés dans le Manuel officiel, et seuls les points sont décrits ici.

network.png

La figure ci-dessus est une image de ce scénario. Imaginez une configuration simple avec un expéditeur TCP et un récepteur. Utilisez BulkSendHelper pour simuler une grande quantité de transmission de données de type FTP. La taille du paquet IP est de 400 octets. Le temps de simulation est de 100 secondes par défaut.

Arguments de ligne de commande

Définissez les arguments de ligne de commande sur les lignes 224 à 243. Comme indiqué dans l'article précédent (http://qiita.com/haltaro/items/b474d924f63692c155c8), vous pouvez définir des arguments de ligne de commande en faisant CommandLine.AddValue ().

tcp-variants-comparison.cc (lignes 224 à 243)


CommandLine cmd;
  cmd.AddValue ("transport_prot", "Transport protocol to use: TcpNewReno, "
                "TcpHybla, TcpHighSpeed, TcpHtcp, TcpVegas, TcpScalable, TcpVeno, "
                "TcpBic, TcpYeah, TcpIllinois, TcpWestwood, TcpWestwoodPlus ", transport_prot);
  cmd.AddValue ("error_p", "Packet error rate", error_p);
  cmd.AddValue ("bandwidth", "Bottleneck bandwidth", bandwidth);
  cmd.AddValue ("delay", "Bottleneck delay", delay);
  cmd.AddValue ("access_bandwidth", "Access link bandwidth", access_bandwidth);
  cmd.AddValue ("access_delay", "Access link delay", access_delay);
  cmd.AddValue ("tracing", "Flag to enable/disable tracing", tracing);
  cmd.AddValue ("prefix_name", "Prefix of output trace file", prefix_file_name);
  cmd.AddValue ("data", "Number of Megabytes of data to transmit", data_mbytes);
  cmd.AddValue ("mtu", "Size of IP packets to send in bytes", mtu_bytes);
  cmd.AddValue ("num_flows", "Number of flows", num_flows);
  cmd.AddValue ("duration", "Time to allow flows to run in seconds", duration);
  cmd.AddValue ("run", "Run index (for setting repeatable seeds)", run);
  cmd.AddValue ("flow_monitor", "Enable flow monitor", flow_monitor);
  cmd.AddValue ("pcap_tracing", "Enable or disable PCAP tracing", pcap);
  cmd.AddValue ("queue_disc_type", "Queue disc type for gateway (e.g. ns3::CoDelQueueDisc)", queue_disc_type);
  cmd.Parse (argc, argv);

Dans cet article, nous utiliserons en particulier les arguments de ligne de commande suivants.

Planification des paramètres de suivi

De la 460ème ligne à la 476ème ligne, la fonction de paramétrage de la trace (fonction de rappel et la fonction qui associe la cible de trace) telle que celle ci-dessus TraceInFlight () est planifiée.

tcp-variants-comparison.cc (lignes 460 à 476)


  if (tracing)
    {
      std::ofstream ascii;
      Ptr<OutputStreamWrapper> ascii_wrap;
      ascii.open ((prefix_file_name + "-ascii").c_str ());
      ascii_wrap = new OutputStreamWrapper ((prefix_file_name + "-ascii").c_str (),
                                            std::ios::out);
      stack.EnableAsciiIpv4All (ascii_wrap);

      Simulator::Schedule (Seconds (0.00001), &TraceCwnd, 
                           prefix_file_name + "-cwnd.data");
      Simulator::Schedule (Seconds (0.00001), &TraceSsThresh, 
                           prefix_file_name + "-ssth.data");
      Simulator::Schedule (Seconds (0.00001), &TraceRtt, 
                           prefix_file_name + "-rtt.data");
      Simulator::Schedule (Seconds (0.00001), &TraceRto, 
                           prefix_file_name + "-rto.data");
      Simulator::Schedule (Seconds (0.00001), &TraceNextTx, 
                           prefix_file_name + "-next-tx.data");
      Simulator::Schedule (Seconds (0.00001), &TraceInFlight, 
                           prefix_file_name + "-inflight.data");
      Simulator::Schedule (Seconds (0.1), &TraceNextRx, 
                           prefix_file_name + "-next-rx.data");
    }

Dans ns-3, vous pouvez programmer l'exécution de func (args, ...) à time avec la syntaxeSimulator :: Schedule (time, & func, args, ...). Cependant, je ne sais pas pourquoi vous ne devriez pas Trace * () dans le texte local. Je pense qu'il y a probablement un problème avec le timing de la création des objets ...

4.2 Défis pour tcp-variants-comparaison.cc

tcp-variants-comparaison.cc est un script de scénario très bien conçu avec lequel vous pouvez jouer simplement en modifiant les arguments de la ligne de commande. Mais je ne peux pas retracer les ACK ou les conditions de congestion qui nous intéressent! Heureusement, la variable LowestRxAck pour le dernier ACK et la variable CongState pour l'état de congestion sont tcp-socket-base.cc release / 3.26 / doxygen / tcp-socket-base_8cc.html) ʻAddTraceSource () `. Par conséquent, vous pouvez les ajouter à la cible de trace avec seulement quelques modifications du script de scénario. Dans ce qui suit, nous présenterons la méthode.

4.3 Nouveau script de scénario my-tcp-variants-comparaison.cc

Tout d'abord, copiez l'original tcp-variantes-comparaison.cc dans ~ / ns-3.26 / source / ns-3.26 / scratch et renommez-le en my-tcp-variantes-comparaison.cc Faire.

$ cd ~/ns-3.26/source/ns-3.26
$ cp examples/tcp/tcp-variants-comparison.cc scratch/my-tcp-variants-comparison.cc 

Afin d'ajouter ACK et l'état de congestion à la cible de trace, ajoutez des variables de trace, définissez la fonction de rappel de trace et planifiez les paramètres de trace.

Ajouter des variables de trace

Ajoutez le flux ʻackStreampour le traçage ACK et le fluxcongStateStream` pour le traçage de l'état de congestion.

cpp:my-tcp-variants-comparison.cc(tcp-variants-comparison.Equivalent aux lignes 58 à 70 de cc)


bool firstCwnd = true;
bool firstSshThr = true;
bool firstRtt = true;
bool firstRto = true;
Ptr<OutputStreamWrapper> cWndStream;
Ptr<OutputStreamWrapper> ssThreshStream;
Ptr<OutputStreamWrapper> rttStream;
Ptr<OutputStreamWrapper> rtoStream;
Ptr<OutputStreamWrapper> nextTxStream;
Ptr<OutputStreamWrapper> nextRxStream;
Ptr<OutputStreamWrapper> inFlightStream;
Ptr<OutputStreamWrapper> ackStream; // NEW!
Ptr<OutputStreamWrapper> congStateStream; // NEW!
uint32_t cWndValue;
uint32_t ssThreshValue;

Définition d'une fonction de rappel pour le traçage

Ajoutez la fonction de rappel ʻAckTrace () pour le traçage ACK et la fonction de rappel CongStateTracer () pour le traçage de l'état de congestion, respectivement. L'état de congestion est le type d'énumération TcpSocketState :: TcpCongState_t défini dans tcp-socket-base.h. Ajoutez également les fonctions TraceAck ()etTraceCongState ()` qui associent respectivement la fonction de rappel ci-dessus à la variable à tracer.

cpp:my-tcp-variants-comparison.cc(tcp-variants-comparison.Equivalent aux lignes 73 à 202 de cc)


static void
AckTracer (SequenceNumber32 old, SequenceNumber32 newAck)
{
  *ackStream->GetStream () << Simulator::Now ().GetSeconds () << " " << newAck << std::endl;
}

static void
CongStateTracer (TcpSocketState::TcpCongState_t old, TcpSocketState::TcpCongState_t newState)
{
  *congStateStream->GetStream () << Simulator::Now ().GetSeconds () << " " << newState << std::endl;
}

//=====Abréviation=====

static void
TraceAck (std::string &ack_file_name)
{
  AsciiTraceHelper ascii;
  ackStream = ascii.CreateFileStream (ack_file_name.c_str ());
  Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/HighestRxAck", MakeCallback (&AckTracer));
}

static void
TraceCongState (std::string &cong_state_file_name)
{
  AsciiTraceHelper ascii;
  congStateStream = ascii.CreateFileStream (cong_state_file_name.c_str ());
	Config::ConnectWithoutContext ("/NodeList/1/$ns3::TcpL4Protocol/SocketList/0/CongState", MakeCallback (&CongStateTracer));
}

Planification des paramètres de suivi

Enfin, planifiez les éléments ci-dessus TraceAck () et TraceCongState ().

cpp:my-tcp-variants-comparison.cc(tcp-variants-comparison.cc Équivaut aux lignes 460 à 476)


  if (tracing)
    {

// =====Abréviation=====

      Simulator::Schedule (Seconds (0.00001), &TraceAck, prefix_file_name + "-ack.data"); // NEW!
      Simulator::Schedule (Seconds (0.00001), &TraceCongState, prefix_file_name + "-cong-state.data"); // NEW!
    }

4.4 Compilation de my-tcp-variants-comparaison.cc

Vous pouvez compiler my-tcp-variants-comparaison.cc en faisant. / Waf dans le répertoire ~ / ns-3.26 / source / ns-3.26.

$ ./waf
Waf: Entering directory '~/ns-3.26/source/ns-3.26/source/ns-3.26/build'
[ 985/2524] Compiling scratch/my-tcp-variants-comparison.cc
[2501/2524] Linking build/scratch/my-tcp-variants-comparison
Waf: Leaving directory '~/ns-3.26/source/ns-3.26/source/ns-3.26/build'
Build commands will be stored in build/compile_commands.json
'build' finished successfully (3.240s)

Modules built:
antenna                   aodv                      applications              
bridge                    brite (no Python)         buildings                 
click                     config-store              core                      
csma                      csma-layout               dsdv                      
dsr                       energy                    fd-net-device             
flow-monitor              internet                  internet-apps             
lr-wpan                   lte                       mesh                      
mobility                  mpi                       netanim (no Python)       
network                   nix-vector-routing        olsr                      
point-to-point            point-to-point-layout     propagation               
sixlowpan                 spectrum                  stats                     
tap-bridge                test (no Python)          topology-read             
traffic-control           uan                       virtual-net-device        
wave                      wifi                      wimax                     
xgpon (no Python)         

Modules not built (see ns-3 tutorial for explanation):
openflow                  visualizer                

5. Expérience

5.1 Exécution d'un script de scénario

Commencez par créer le répertoire de stockage de données data.

$ mkdir ~/ns-3.26/source/ns-3.26/data

Exécutez le script shell suivant pour expérimenter les 12 types d'algorithmes. Comme présenté dans Article précédent, vous pouvez passer la valeur «value» à l'argument de ligne de commande «arg» par «--arg = valeur». Je vais. transport_prot est l'algorithme de contrôle de la congestion, prefix_name est le préfixe du nom du fichier de sortie, tracing est la présence ou l'absence de traçage et duration est le [s] temps de simulation.

compare-tcp-algorithms.sh


#!/bin/bash

ALGORITHMS=(TcpNewReno TcpHighSpeed TcpHybla TcpWestwood TcpWestwoodPlus \ 
 TcpVegas TcpScalable TcpVeno TcpBic TcpYeah TcpIllinois TcpHtcp)

for algo in ${ALGORITHMS[@]}; do
  echo "----- Simulating $algo -----"
  ./waf --run "my-tcp-variants-comparison --transport_prot=$algo --prefix_name='data/$algo' --tracing=True --duration=20"
done

5.2 Observer le contrôle de la congestion pour tous les algorithmes

Pour l'instant, traçons $ cwnd $ et $ ssth $ de tous les algorithmes et le changement d'état de congestion avec le plot_cwnd_all_algorithms () suivant.

plottcpalgo.py


# -*- coding: utf-8 -*-
#!/usr/bin/env python


import numpy as np
import matplotlib.pyplot as plt

#Il s'agit d'une fonction de mise en forme des données.
def get_data(algo, variable='cwnd', duration=20.):
    file_name = 'data/Tcp' + algo + '-' + variable + '.data'
    data = np.empty((0, 2)) #Initialisation

    #Récupérez les données du fichier.
    for line in open(file_name, 'r'):
        data = np.append(
            data, np.array([map(float, line[:-1].split())]),
            axis=0)
    data = data.T

    #Extraire uniquement les données inférieures à la durée, à la fin[duration, data[1,-1]]À
    #ajouter. C'est un processus pour tracer magnifiquement jusqu'à la durée.
    data = data[:, data[0] < duration]
    if len(data[0])==0:
        data = np.append(
            data, np.array([[duration, 0]]).T,
            axis=1)
    else:
        data = np.append(
            data, np.array([[duration, data[1, -1]]]).T,
            axis=1)

    return data


def plot_cwnd_all_algorithms(duration=20.):
    algos = ['NewReno', 'HighSpeed', 'Hybla', 'Westwood', 'WestwoodPlus',
             'Vegas', 'Scalable', 'Veno', 'Bic', 'Yeah', 'Illinois', 'Htcp']
    segment = 340 #Dans cette configuration expérimentale, la longueur du segment est de 340 octets.

    plt.figure(figsize=(15, 10))
    for i, algo in enumerate(algos): 
        plt.subplot(3, 4, i + 1)

        #Acquisition des données cwnd, ssth, état de congestion
        cwnd_data = get_data(algo, 'cwnd', duration) 
        ssth_data = get_data(algo, 'ssth', duration)
        state_data = get_data(algo, 'cong-state', duration)

        #Les couleurs sont différentes selon l'état de congestion.
        #OPEN (bleu), DISORDER (vert), RECOVERY (jaune), LOSS (rouge)
        plt.fill_between(cwnd_data[0], cwnd_data[1] / segment,
                         facecolor='lightblue') #L'état initial est OPEN (bleu)
        for n in range(len(state_data[0])-1):
            fill_range = cwnd_data[0] >= state_data[0, n]
            if state_data[1, n]==1: # 1: DISORDER
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='lightgreen')
            elif state_data[1, n]==3: # 3: RECOVERY
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='khaki')
            elif state_data[1, n]==4: # 4: LOSS
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='lightcoral')
            else: # OPEN
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='lightblue')

        #Tracés de cwnd (ligne continue) et ssth (ligne pointillée).
        plt.plot(cwnd_data[0], cwnd_data[1] / segment, drawstyle='steps-post')
        plt.plot(ssth_data[0], ssth_data[1] / segment, drawstyle='steps-post',
                 color='b', linestyle='dotted')
        plt.ylim(0, 1200) #La valeur initiale de ssth étant énorme, définissez la limite supérieure avec ylim
        plt.title(algo)

    #Enregistrer et afficher des diagrammes
    plt.savefig('data/TcpAll' + str(duration).zfill(3) + '-cwnd.png')
    plt.show()

TcpAll20.0-cwnd.png

L'axe horizontal correspond au temps [s] et l'axe vertical correspond à $ cwnd $ et $ ssth $ [segment]. $ cwnd $ est une ligne continue et $ ssth $ est une ligne pointillée. Les couleurs sont colorées en fonction de l'état de congestion. $ OPEN $ est bleu, $ DISORDER $ est vert, $ RECOVERY $ est jaune et $ LOSS $ est rouge. L'individualité de chaque algorithme est ressortie plus fortement que prévu initialement.

5.3 Observer la relation entre cwnd, ACK et RTT de chaque algorithme

Ensuite, tracez $ cwnd $, ACK et RTT de chaque algorithme avec le plot_cwnd_ack_rtt_each_algorithm () suivant.

plot_cwnd_ack_rtt_each_algorithm()


def plot_cwnd_ack_rtt_each_algorithm(duration=20.):
    algos = ['NewReno', 'HighSpeed', 'Hybla', 'Westwood', 'WestwoodPlus',
             'Vegas', 'Scalable', 'Veno', 'Bic', 'Yeah', 'Illinois', 'Htcp']
    segment = 340 #Dans cette configuration expérimentale, la longueur du segment est de 340 octets.
    plt.figure()

    for algo in algos:
        plt.subplot(311) #Tracé de cwnd, ssth et congestion

        #Acquisition des données cwnd, ssth, état de congestion
        cwnd_data = get_data(algo, 'cwnd', duration)
        ssth_data = get_data(algo, 'ssth', duration)
        state_data = get_data(algo, 'cong-state', duration)


        #Les couleurs sont différentes selon l'état de congestion.
        #OPEN (bleu), DISORDER (vert), RECOVERY (jaune), LOSS (rouge)
        plt.fill_between(cwnd_data[0], cwnd_data[1] / segment,
                         facecolor='lightblue') #L'état initial est OPEN (bleu)
        for n in range(len(state_data[0])-1):
            fill_range = cwnd_data[0] >= state_data[0, n]
            if state_data[1, n]==1: # 1: DISORDER
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='lightgreen')
            elif state_data[1, n]==3: # 3: RECOVERY
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='khaki')
            elif state_data[1, n]==4: # 4: LOSS
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='lightcoral')
            else: # OPEN
                plt.fill_between(
                    cwnd_data[0, fill_range], cwnd_data[1, fill_range] / segment,
                    facecolor='lightblue')

        #Tracés de cwnd (ligne continue) et ssth (ligne pointillée).
        plt.plot(cwnd_data[0], cwnd_data[1] / segment, drawstyle='steps-post',
                 color='b', label='cwnd')
        plt.plot(ssth_data[0], ssth_data[1] / segment, drawstyle='steps-post',
                 color='b', linestyle='dotted', label='ssth')
        ymax = max(cwnd_data[1] / segment)*1.1
        plt.ylim(0, ymax) #La valeur initiale de ssth étant énorme, définissez la limite supérieure avec ylim
        plt.ylabel('cwnd [segment]')
        plt.legend()
        plt.title(algo)

        plt.subplot(312) #Graphique ACK
        ack_data = get_data(algo, 'ack', duration)
        plt.plot(ack_data[0], ack_data[1] / segment, drawstyle='steps-post',
                 color='r')
        plt.ylabel('ACK [segment]')

        plt.subplot(313) #Graphique RTT
        rtt_data = get_data(algo, 'rtt', duration)
        plt.plot(rtt_data[0], rtt_data[1], drawstyle='steps-post', color='g')
        plt.ylabel('RTT[s]')
        plt.xlabel('Time [s]')

        #Enregistrer l'image.
        plt.savefig('data/Tcp' + algo + 'Sub-' + str(int(duration)).zfill(3) +
                    '-cwnd-ack-rtt.png')
        plt.clf()
    plt.show()

Dans ce qui suit, nous analyserons les résultats en utilisant NewReno comme exemple. De plus, pour votre information, les résultats d'autres algorithmes sont également inclus.

NewReno

TcpNewReno020-cwnd-ack-rtt.png

Environ 1,93 [s], trois ACK en double sont reçus et l'état passe à l'état $ RECOVERY $. Débit approximatif à ce moment:

\frac{cwnd}{RTT} \simeq 
\frac{321\rm{[segment]} \cdot 340 \rm{[byte/segment] \cdot 8 \rm{[bit/byte]}}}{0.33[s]} \simeq 2.3 \rm{[Mbps]}

Ici, la bande passante de la liaison de goulot d'étranglement était de 2,0 Mbps (voir la section 4.1), donc ce n'est pas anormal en cas de congestion [^ analyse]. Après la transition vers $ RECOVERY $, vous pouvez voir que les mises à jour ACK et RTT se sont arrêtées. Vous pouvez également voir que les mises à jour pour $ cwnd $ et $ ssth $ sont cohérentes avec la section 3.3. Autour de 3,26 [s], il expire sans recevoir de nouvel ACK et passe à l'état $ LOSS $. Vous pouvez voir que les mises à jour de $ cwnd $ et $ ssth $ sont cohérentes avec la section 3.3. Aux alentours de 4,69 [s], un nouvel ACK est finalement reçu et l'état passe à l'état $ OPEN $.

[^ Analyse]: Peut-être. Je pense qu'une analyse plus détaillée telle que l'analyse de la file d'attente est nécessaire, mais je suis épuisé ...

Autres algorithmes

Pour votre information, les résultats d'autres algorithmes sont également affichés.

TcpHighSpeed020-cwnd-ack-rtt.png

TcpHybla020-cwnd-ack-rtt.png

TcpWestwood020-cwnd-ack-rtt.png

TcpWestwoodPlus020-cwnd-ack-rtt.png

TcpVegas020-cwnd-ack-rtt.png

TcpScalable020-cwnd-ack-rtt.png

TcpVeno020-cwnd-ack-rtt.png

TcpBic020-cwnd-ack-rtt.png

TcpYeah020-cwnd-ack-rtt.png

TcpIllinois020-cwnd-ack-rtt.png

TcpHtcp020-cwnd-ack-rtt.png

6. Conclusion

Dans cet article, j'ai utilisé ns-3 pour simuler le contrôle de congestion TCP et je l'ai visualisé avec python. En tant que débutant, j'ai pu avoir une idée de TCP. Nous avons également réaffirmé l'excellence du scénario de l'échantillon ns-3. Désormais, CUBIC et CTCP Je voudrais essayer d'implémenter des algorithmes majeurs tels que) et les derniers algorithmes tels que Remy. Alternativement, je pense qu'il serait bon d'observer la concurrence entre différents algorithmes. Merci d'avoir lu jusqu'au bout!

référence

En créant cet article, j'ai fait référence à ce qui suit. Merci beaucoup!

Recommended Posts

Observez le contrôle de congestion TCP avec ns-3
Contrôler les scripts avec des exceptions
Contrôlez plusieurs robots avec jupyter-lab