Compare commits

...

2 commits

Author SHA1 Message Date
9104f40aaf Conform to datId = senderId 2016-11-27 13:26:27 +01:00
6d3774bc99 Update readme, start report 2016-11-27 13:07:01 +01:00
8 changed files with 193 additions and 37 deletions

View file

@ -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).

View file

@ -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();

View file

@ -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;
}; };

View file

@ -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) {

View file

@ -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
View file

@ -0,0 +1,3 @@
*.aux
*.log
*.pdf

6
report/Makefile Normal file
View file

@ -0,0 +1,6 @@
TEX=report.tex
all: $(TEX)
pdflatex $(TEX)
pdflatex $(TEX)

135
report/report.tex Normal file
View 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}