Compare commits
2 commits
a31d9e29eb
...
9104f40aaf
Author | SHA1 | Date | |
---|---|---|---|
9104f40aaf | |||
6d3774bc99 |
8 changed files with 193 additions and 37 deletions
|
@ -6,7 +6,7 @@ Projet de cours "réseau", M1 <https://tobast.fr/m1/nw-project.pdf>
|
||||||
|
|
||||||
* `g++` (version supportant c++14) ou tout autre compilateur c++ (éditer le
|
* `g++` (version supportant c++14) ou tout autre compilateur c++ (éditer le
|
||||||
Makefile pour changer `CXX`)
|
Makefile pour changer `CXX`)
|
||||||
* Bibliothèques standard POSIX
|
* pthread
|
||||||
|
|
||||||
## Compiler
|
## Compiler
|
||||||
|
|
||||||
|
@ -23,13 +23,11 @@ arguments.
|
||||||
* `bootstrap [ID du nœud] [adresse IPv6 du nœud] [port]` : déclare le nœud
|
* `bootstrap [ID du nœud] [adresse IPv6 du nœud] [port]` : déclare le nœud
|
||||||
comme nœud de bootstrap. L'adresse peut être IPv4-mapped, eg.
|
comme nœud de bootstrap. L'adresse peut être IPv4-mapped, eg.
|
||||||
`::FFFF:42.42.42.42`.
|
`::FFFF:42.42.42.42`.
|
||||||
* `data [id de donnée] [donnée]` : déclare une donnée à propager. Si l'id est
|
* `data [donnée]` : déclare une donnée à propager. La donnée peut contenir des
|
||||||
0, il sera tiré au hasard puis enregistré. La donnée peut contenir des
|
|
||||||
espaces, et s'étend jusqu'à la fin de la ligne.
|
espaces, et s'étend jusqu'à la fin de la ligne.
|
||||||
|
|
||||||
Le programme, à l'initialisation, lit le fichier puis le réécrit avec
|
Le programme, à l'initialisation, lit le fichier puis le réécrit avec
|
||||||
éventuellement des données tirées au hasard si nécessaire (eg. ID du nœud, des
|
éventuellement des données tirées au hasard si nécessaire (eg. ID du nœud).
|
||||||
données).
|
|
||||||
|
|
||||||
Le programme produit des logs verbeux mais humainement lisibles sur sa sortie
|
Le programme produit des logs verbeux mais humainement lisibles sur sa sortie
|
||||||
d'erreur (stderr).
|
d'erreur (stderr).
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
ConfigFile::ConfigFile() {
|
ConfigFile::ConfigFile() : curDataSum(0) {
|
||||||
selfId = randId();
|
selfId = randId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,11 +48,6 @@ bool ConfigFile::read(const char* path) {
|
||||||
bootstrapNodes.push_back(Neighbour(id, addr));
|
bootstrapNodes.push_back(Neighbour(id, addr));
|
||||||
}
|
}
|
||||||
else if(attr == "data") {
|
else if(attr == "data") {
|
||||||
u64 id;
|
|
||||||
handle >> hex >> id >> dec;
|
|
||||||
if(id == 0)
|
|
||||||
id = randId();
|
|
||||||
|
|
||||||
string dataStr;
|
string dataStr;
|
||||||
getline(handle, dataStr);
|
getline(handle, dataStr);
|
||||||
|
|
||||||
|
@ -63,7 +58,16 @@ bool ConfigFile::read(const char* path) {
|
||||||
if(nonWSPos == dataStr.size())
|
if(nonWSPos == dataStr.size())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
data.push_back(make_pair(id, dataStr.substr(nonWSPos)));
|
std::string realDat = dataStr.substr(nonWSPos);
|
||||||
|
if(curDataSum + realDat.size() + 12 + 2 > 0xff) {
|
||||||
|
fprintf(stderr, "Too much data, won't fit. Discarding `%s`\n",
|
||||||
|
realDat.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
curDataSum += realDat.size() + 2;
|
||||||
|
|
||||||
|
data.push_back(realDat);
|
||||||
}
|
}
|
||||||
else if(attr.empty())
|
else if(attr.empty())
|
||||||
continue;
|
continue;
|
||||||
|
@ -96,8 +100,7 @@ bool ConfigFile::write(const char* path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto dat : data) {
|
for(auto dat : data) {
|
||||||
handle << "data " << hex << dat.first << dec << " "
|
handle << "data " << dat << '\n';
|
||||||
<< dat.second << '\n';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.close();
|
handle.close();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
class ConfigFile {
|
class ConfigFile {
|
||||||
public:
|
public:
|
||||||
|
typedef std::string CfgData;
|
||||||
ConfigFile();
|
ConfigFile();
|
||||||
|
|
||||||
bool read(const char* path);
|
bool read(const char* path);
|
||||||
|
@ -26,7 +27,7 @@ class ConfigFile {
|
||||||
};
|
};
|
||||||
/** Bootstrap nodes. No setter: wouldn't be useful. */
|
/** Bootstrap nodes. No setter: wouldn't be useful. */
|
||||||
|
|
||||||
const std::vector<std::pair<u64, std::string> >& getData() const {
|
const std::vector<CfgData>& getData() const {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
/** Data to publish. */
|
/** Data to publish. */
|
||||||
|
@ -36,6 +37,7 @@ class ConfigFile {
|
||||||
|
|
||||||
u64 selfId;
|
u64 selfId;
|
||||||
std::vector<Neighbour> bootstrapNodes;
|
std::vector<Neighbour> bootstrapNodes;
|
||||||
std::vector<std::pair<u64, std::string> > data;
|
std::vector<CfgData> data;
|
||||||
|
size_t curDataSum;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -85,23 +85,32 @@ void DataStore::setFlooded(u64 id) {
|
||||||
|
|
||||||
void DataStore::dump() {
|
void DataStore::dump() {
|
||||||
for(auto it=data.begin(); it != data.end(); ++it) {
|
for(auto it=data.begin(); it != data.end(); ++it) {
|
||||||
printf(">> DATA %lX (%u) ", it->first, curSeqno[it->first]);
|
|
||||||
Bytes dat = it->second.data;
|
Bytes dat = it->second.data;
|
||||||
|
while(dat.size() >= 2) {
|
||||||
|
printf(">> DATA %lX (%u) ", it->first, curSeqno[it->first]);
|
||||||
if(dat.size() < 2) {
|
if(dat.size() < 2) {
|
||||||
printf("INVALID\n");
|
printf("INVALID\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
u8 type, len;
|
u8 type, len;
|
||||||
dat >> type >> len;
|
dat >> type >> len;
|
||||||
|
if(dat.size() < len) {
|
||||||
|
printf("INVALID\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(type == csts::TLV_DATA_TEXT) {
|
if(type == csts::TLV_DATA_TEXT) {
|
||||||
char val[1024];
|
string val;
|
||||||
dat.writeToBuffer(val, 1023);
|
for(int i=0; i < len; i++) {
|
||||||
val[min((int)dat.size(), 1023)] = '\0';
|
u8 c;
|
||||||
printf("'%s'\n", val);
|
dat >> c;
|
||||||
|
val += c;
|
||||||
|
}
|
||||||
|
printf("'%s'\n", val.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
printf("type=%d\n", type);
|
printf("type=%d\n", type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataStore::handleExpire(u64 id, u32 seqno) {
|
void DataStore::handleExpire(u64 id, u32 seqno) {
|
||||||
|
|
14
main.cpp
14
main.cpp
|
@ -70,15 +70,15 @@ int main(int argc, char** argv) {
|
||||||
Protocol proto(addr, cfg.getSelfId());
|
Protocol proto(addr, cfg.getSelfId());
|
||||||
|
|
||||||
DataStore dataStore(&proto);
|
DataStore dataStore(&proto);
|
||||||
|
Bytes selfData;
|
||||||
for(auto dat : cfg.getData()) {
|
for(auto dat : cfg.getData()) {
|
||||||
Bytes pck;
|
selfData << csts::TLV_DATA_TEXT << (u8)dat.size();
|
||||||
pck << csts::TLV_DATA_TEXT << (u8)dat.second.size();
|
for(u8 chr : dat)
|
||||||
for(u8 chr : dat.second)
|
selfData << chr;
|
||||||
pck << chr;
|
fprintf(stderr, "[INFO] Adding data `%s`\n",
|
||||||
dataStore.addData(pck, time(NULL), dat.first, true);
|
dat.c_str());
|
||||||
fprintf(stderr, "[INFO] Adding data `%s` (%lX)\n",
|
|
||||||
dat.second.c_str(), dat.first);
|
|
||||||
}
|
}
|
||||||
|
dataStore.addData(selfData, time(NULL), cfg.getSelfId(), true);
|
||||||
|
|
||||||
Neighbours neighboursManager(&proto, &dataStore);
|
Neighbours neighboursManager(&proto, &dataStore);
|
||||||
|
|
||||||
|
|
3
report/.gitignore
vendored
Normal file
3
report/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.aux
|
||||||
|
*.log
|
||||||
|
*.pdf
|
6
report/Makefile
Normal file
6
report/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
TEX=report.tex
|
||||||
|
all: $(TEX)
|
||||||
|
pdflatex $(TEX)
|
||||||
|
pdflatex $(TEX)
|
||||||
|
|
||||||
|
|
135
report/report.tex
Normal file
135
report/report.tex
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
% vim: spelllang=fr
|
||||||
|
|
||||||
|
\documentclass[11pt,a4paper]{article}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[francais]{babel} %% FRENCH, FIXME if typing in english
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage{amsmath}
|
||||||
|
\usepackage{amsfonts}
|
||||||
|
\usepackage{amssymb}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage[left=2cm,right=2cm,top=2cm,bottom=2cm]{geometry}
|
||||||
|
|
||||||
|
% Custom packages
|
||||||
|
\usepackage{my_listings}
|
||||||
|
\usepackage{math}
|
||||||
|
|
||||||
|
\author{Théophile \textsc{Bastian}}
|
||||||
|
\title{Rapport~: projet réseau}
|
||||||
|
\date{\today}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
|
||||||
|
L'implémentation du protocole en C++ respecte la spécification sur tous les
|
||||||
|
cas observés. De plus, les paquets sont agrégés tant que possible pour
|
||||||
|
éviter une surcharge du réseau, l'inondation est un peu optimisée et
|
||||||
|
n'interrompt pas le reste du protocole et le programme affiche ses
|
||||||
|
informations dans la console sur demande. Toutefois, il n'est pas possible
|
||||||
|
de modifier dynamiquement les données propagées.
|
||||||
|
|
||||||
|
Le support de l'IPv4 et de l'IPv6 à la fois devrait être possible, mais n'a
|
||||||
|
pas pu être testé.
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
\section{Utilisation}
|
||||||
|
|
||||||
|
\subsection{Dépendances}
|
||||||
|
Seuls un compilateur C++ supportant C++14 et la bibliothèque pthread sont
|
||||||
|
nécessaires au fonctionnement du programme sur un environnement POSIX standard.
|
||||||
|
|
||||||
|
\subsection{Compilation}
|
||||||
|
|
||||||
|
\lstbash{make} suffit à compiler. Il est éventuellement possible d'utiliser un
|
||||||
|
autre compilateur C++ en éditant le \texttt{Makefile}.
|
||||||
|
|
||||||
|
\subsection{Utilisation}
|
||||||
|
|
||||||
|
Le programme fourni prend en argument le chemin vers un fichier de
|
||||||
|
configuration, dont chaque ligne commence par un mot-clé suivi de ses
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
|
||||||
|
\item \lstbash{id [ID du programme]}~: laisser vide par défaut, sera généré
|
||||||
|
automatiquement.
|
||||||
|
|
||||||
|
\item \lstbash{bootstrap [ID du nœud] [adresse IPv6 du nœud] [port]}~:
|
||||||
|
déclare le nœud comme nœud de bootstrap. L'adresse peut être
|
||||||
|
IPv4-mapped, eg. `::FFFF:42.42.42.42`.
|
||||||
|
|
||||||
|
\item \lstbash{data [donnée]}~: déclare une donnée à propager. La donnée
|
||||||
|
peut contenir des espaces, et s'étend jusqu'à la fin de la ligne.
|
||||||
|
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Le programme, à l'initialisation, lit le fichier puis le réécrit avec
|
||||||
|
éventuellement des données tirées au hasard si nécessaire (eg. ID du nœud).
|
||||||
|
|
||||||
|
\subsection{Entrée/sortie}
|
||||||
|
|
||||||
|
Le programme produit des logs verbeux mais humainement lisibles sur sa sortie
|
||||||
|
d'erreur (stderr).
|
||||||
|
|
||||||
|
Le programme affiche son état actuel (voisins + infos sur eux, données + infos
|
||||||
|
sur elles) lors d'un appui sur \textsc{return}.
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
\section{Implémentation}
|
||||||
|
|
||||||
|
Tout d'abord, mon code est très --- trop --- long pour le programme demandé.
|
||||||
|
J'ai manqué de temps pour ce projet (beaucoup de choses à faire en parallèle)
|
||||||
|
et j'ai donc plus produit du code rapidement qu'intelligemment\ldots
|
||||||
|
|
||||||
|
\subsection{Threads}
|
||||||
|
Le programme contient deux threads~:
|
||||||
|
\begin{itemize}
|
||||||
|
\item thread principal, gérant la plupart des choses~;
|
||||||
|
\item thread de lecture des paquets, réceptionnant les paquets et les
|
||||||
|
stockant dans une file pour que le thread principal puisse les
|
||||||
|
analyser.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Des mutexes sont utilisées quand nécessaire (accès à la file, en particulier).
|
||||||
|
|
||||||
|
\subsection{Lecture de l'entrée standard}
|
||||||
|
|
||||||
|
Pour éviter d'être bloquant sur la lecture de l'entrée standard, j'utilise dans
|
||||||
|
\texttt{main.cpp} \lstc{select} pour mettre un timeout d'une seconde sur
|
||||||
|
\lstc{getchar}. Ceci remplace \lstc{sleep} dans la boucle principale.
|
||||||
|
|
||||||
|
\subsection{Agrégation des paquets}
|
||||||
|
|
||||||
|
L'idée suggérée est implémentée~: la fonction \lstcpp{Protocol::sendBody}
|
||||||
|
agrège les TLVs qu'on lui donne dans une map de paquets (sans headers, \ie{}
|
||||||
|
une suite de TLVs), mappant les IDs de nœuds sur leur paquet en cours d'envoi.
|
||||||
|
Lorsqu'elle souhaite agréger plus d'un MTU~$- 12$ dans un paquet (\ie{}
|
||||||
|
MTU~$-$~taille des headers), le paquet dans la map est envoyé, vidé, puis on
|
||||||
|
agrèg le TLV qu'on souhaitait insérer.
|
||||||
|
|
||||||
|
À la fin de chaque itération de la boucle principale, tous les paquets en cours
|
||||||
|
sont envoyés, garantissant ainsi un délai inférieur à une seconde ---
|
||||||
|
acceptable compte-tenu des délais prévus dans le protocole.
|
||||||
|
|
||||||
|
\subsection{Amélioration de l'inondation}
|
||||||
|
|
||||||
|
Lorsqu'un premier IHU est reçu d'un pair, on inonde immédiatement la
|
||||||
|
donnée.
|
||||||
|
|
||||||
|
\subsection{Inondation non-bloquante}
|
||||||
|
|
||||||
|
L'objet \lstcpp{DataStore} décide périodiquement de republier ses données. Dans
|
||||||
|
ce cas, il met à jour la donnée stockée, puis met à jour un champ demandant la
|
||||||
|
propagation d'une certaine donnée. Ce champ est récupéré régulièrement par
|
||||||
|
l'objet \lstcpp{Neighbours}, pouvant lui aussi décider d'inonder une donnée
|
||||||
|
vers \emph{un} pair lorsque celui-ci envoie son premier IHU\@.
|
||||||
|
|
||||||
|
Lorsque \lstcpp{Neighbours} reçoit, d'une manière ou d'une autre, une requête
|
||||||
|
d'inondation, il crée un objet \lstcpp{Flooder} pour l'ID de la donnée à
|
||||||
|
inonder initialisé avec la liste des voisins vers qui on souhaite inonder.
|
||||||
|
Régulièrement, \lstcpp{Flooder::update} est appelé, et se charge d'inonder
|
||||||
|
plusieurs fois la donnée vers chaque pair de l'ayant pas encore acquittée.
|
||||||
|
|
||||||
|
\end{document}
|
Loading…
Reference in a new issue