Compare commits

..

No commits in common. "eda9fc8abd6c7bc7cd0ef9d21c56735a7f12e414" and "6b13b3cdb4b6ac2e420e6265b7524963b4fcb8f7" have entirely different histories.

2 changed files with 19 additions and 418 deletions

View file

@ -1,241 +1,57 @@
--- ---
title: "PdP: Architecture logicielle" title: "PdP: Architecture loficielle"
date: 2024-03-14 date: 2024-03-14
tags: ["besoins", "UML", "developpement logiciel"] tags: ["besoins", "UML", "developpement logiciel"]
categories: ["Projet de programmation", "Cours"] categories: ["Projet de programmation", "Cours"]
--- ---
## Introduction
une architecture logicielle est une structure composite basée sur une
décomposition de composants distincts et une ensemble de dépendance et/ou
d'interaction entre ces composants.
La finalité est de répondre un ensemble de besoins logiciels et d'offrir un
ensemble de services.
> Engineering is all about breaking down big problems into smaller ones
> and putting the solutions for those problems back together
Boswell and Fouchet, 2011
Il est donc question ici de maitriser la taille et la complexité pour assurer le
**passage à l'échelle** : décomposer pour maîtriser et faire évoluer. En effet
les architectures logicielles doivent aussi être modifiables, extensible et
évolutives. Les composants et leurs dépendances doivent **permetttre et
faciliter le changement**.
## Dépendances et composants
Un composant, aussi appelé aussi *bloc de construction* est tout élément
syntaxique qui participe à la structure de son architecture : les intructions,
les expressions, fonctions, classes, modules, paquetages, etc.
Un composant \\(C_1\\) est dépendant de \\(C_2\\) si \\(C_1\\) utilise \\(C_2\\)
dans sa définition ou implémentation.
![Représentation des dépendences](./imgs/dependence_c1_c2.svg)
Voici comment représenter les dépendances entre deux classes, notez le sens de
la flèche, elle pointe vers \\(C_2\\) : *"dépends de".
Nous avons trois grande famille de dépendances:
* Les **utilisations simples** d'éléments de composants. Par exemple une
fonction \\(f_1\\) appelle une autre fonction \\(f_2\\);
* Les **associations** entre composants par imbrications. Par exemple une
classe \\(C_1\\) inclut un attribut de la classe \\(C_2\\);
* Les **inclusions** ou **héritages** entre composants par réutilisation. Par
exemple un sources C \\(s_1\\) inclut via `#include` un autre fichier
\\(s_2\\).
### Taille -- flux et débit -- des dépendances
Les dépendances d'une architecture doivent être utilisable, utilisées et utiles.
Elles doivent transmettre que le stricte minimum d'informations et de qualité
suffisante (contrôle et maîtrise).
La **taille** d'une dépendance entre \\(c_{1}\\) et \\(c_{2}\\) reflète la
quantité d'information qu'elle peut faire transiter ce qui inclus:
* Le nombre d'éléments distincts transmis à \\(c_{1}\\) par \\(c_{2}\\);
* Le volume des éléments distincts transmis à \\(c_{1}\\) par \\(c_{2}\\);
* La complexité des éléments distincts transmis à \\(c_{1}\\) par \\(c_{2}\\).
Cette mesure n'as pas vocation être précise.
Dasn une architecture logicielle, il faut favoriser la définition d'une
dependance **avec une taille minimale**
### Diffusion des changements
Une architecture où les changements dasn les composants sont diffusés aux
autres composants n'est qu'une *architecture factice*.
Nous devons donc introduire la notion de **force d'une dépendance** entre deux
composants \\(c_{1}\\) et \\(c_{2}\\):
* Elle est **forte** si une modification dans \\(c_{2}\\) entraine une
modification dans \\(c_{1}\\).
* Elle est faible dans le cas contraire.
La diffusion des changement va toujours dans le *sens inverse* des dépendances.
Dans une architecture logicielle, il faut limiter les dépendances forte pour les
changements les plus probables dans cette architecture. On est obligé de se
concentrer sur les changements les plus probables (on ne pourra considérer tous
les changements). Il faut identifier ces changement possibles par le contexte,
les besoins non fonctionnels, l'expérience, etc.
Plusieurs techniques d'implémentation sont disponible pour appliquer le principe
de diffusion minimisée des changements comme:
* Les mécanismes de cloisonnement des langages (expression du privée / public);
* En appliquant le principe de *taille minimisé*;
* En utilisant les moyens d'expression d'abstration des langages comme *les
interfaces*.
### Dépendances et utilisation des interfaces ### Dépendances et utilisation des interfaces
La spécification d'un composant représente la description abstraite et rigoureuse de spécification d'un composant -> description abstraite et rigoureuse de ses
ses qualités, de sa manière de se comporter, des services qu'il est censé qualités, de sa manière de se comporter, des services qu'il est censé rendre.
rendre. Un composant qui satisfait une spécification \\(S\\) est dit *réaliser* Un composant qui satisfait une spécification S est dit réaliser S
\\(S\\)
Utilisation de l'interface au lieu d'une classe Utilisation de l'interface au lieu d'une classe
```c++ ```c++
class C2 { class C2 {
IC1 obj; IC1 obj;
// This is a simple class // ...
} }
interface IC1 { interface IC1 {
// Interface - kind of specification
} }
class C1 implement IC1 { class C1 implement IC1 {
// then iplementation. // ...
} }
``` ```
![Introduction d'une interface](./imgs/interface.svg)
Ainsi `C2` ne dépends pas de `C1` mais de l'interface `IC1`. En plus de décrire Ainsi `C2` ne dépends pas de `C1` mais de l'interface `IC1`. En plus de décrire
abstraitement les composants à implémenter, elles servent d'intermédiaire dans abstraitement les composants à implémenter, elles servent d'intermédiaire dans
les dépendances. les dépendances.
Les interfaces contrôlent une part des dépendances fortes, on y spécifie les
éléments les moins susceptibles de changer. Les interfaces ont deux rôles
important dans une architecture logicielle:
* Décrire de manière abstraite les composants à implémenter;
* Servir d'intermédiaire dans les dépendances et ainsi contrôler la force ou la
faiblesse des dépendances.
#### Principe d'inversion des dépendances.
**Une dependance implicite** est une dépendance qui ne se voit pas directement
dans le source d'un programme pas ses appels et ses annotations. Elle est
toujours forte par rapport au changement qui la détermine.
> Dans une architecture logicielle, il est préférable de faire dépendre les > Dans une architecture logicielle, il est préférable de faire dépendre les
> composants d'interfaces et non les composants implémentés. > ocmposants d'interfaces et non les composants implémentés.
si nous reprenons le schéma précédent, alors \\(IC_{1}\\) a été écrite par Principe d'inversion des dépendances.
rapport à l'usage de \\(C_{2}\\) par \\(C_{1}\\), nous pouvons alors en déduire
que \\(C_{2}\\) dépends de la paire \\((C_{1},IC_{1})\\). C'est notre
**inversion de dépendance**
### Notion étendue des dépendances Une dependance implicite -> qui ne se voit pas directement dans le source d;un
programme pas ses appels et ses annotations. Elle est toujours forte par rapport
Nous avons vu des *dépendances explicites* qui se matérialise par les appels de au chamgement qui la détermine.
méthodes, les classes etc. Mais il existe aussi les **dépendances implicite**:
c'est une dépendance qui ne se voit pas directement dans le code source.
Elles peuvent prendre des formes très différentes des dépendances explicite par Elles peuvent prendre des formes très différentes des dépendances explicite par
* La duplication de code; * la duplication de code
* les similarité de services offerts par des composants distincts; * les similarité de services
* les similarité de structure entre composants distincts. * <cf slide>
Les dépendances implicites sont toujours des dépendances fortes car se sont les
changements qui les définissent. Il est donc préférable de minimiser leur
apparition. (on minimise alors la duplication de code et les similarité par
des techniques de factorisation de code)
LEs archilogicielles sont généralement des structures hiérarchisées.
## Granularité de structure, cohésion et couplage ## Granularité de structure, cohésion et couplage
Les architecture logicielles sont généralement des structures hiérarchisées de niveau de granularité défini par
composants, c'est une conséquence naturelle du *décomposer pour maîtriser*.
Cette hiérarchisation est généralement simplifiée car elle peut être constituée
essentiellement de niveaux homogène de structure de composants.
Son niveau de granularité est déterminé par des composants de même espèce Gq et G2 deux niveau de granilarité alirs couplage : ensemble des dépendances
capable d'inclure des composants du niveau inférieur (s'il y en a). Les niveaux entre les composants de G2
habituels de granularité offerts par les langages de programmation sont:
* Les valeurs, données, instructions;
* Les variables, attributs, fonctions (ou méthodes);
* Les modules, classes, objets,
* Les paquetages, clusters, namespace
* Les super-paquetages;
* Les couches.
Pour des raison de simplification et d'intelligibilité, les dépendances peuvent
se restreindre à ne s'énoncer qu'au sein de chaque niveau de granularité : un
composant \\(C1\\) est explicitement dépendant de \\(C2\\) si \\(C1\\) utilise
\\(C2\\) dans sa définition ou son implémentation et si tous les deux sont au
même niveau de granularité. Les dépendances sont donc à considérer entre
fonctions, entre classes, entre paquetages, etc.
### Graphes de dépendances
Les dépendances dans une granularité fixée induisent des **graphes** dont les
sommets sont d'une seule espèce de composants et les arcs des dépendances
explicites.
Dans la mesure du possible, il faut favoriser la construction de niveau de
granularité homogènes donnant lieu à des graphe de dépendances distincts. C'est
un principe de simplification et d'intelligibilité des architectures logicielles
mais qui n;est pas applicable tout le temps (et c'est parfois souhaitable). Les
langages permettent souvent des moyens de déroger à ce principe (*nested
classes* en Java, lambda expression ...).
### Suites de granularités, consécutivité
Les granularités peuvent donc former des suites constitués des éléments que nous
avons évoqué plus haut (données, variables, fonctions etc.). Deux **granularité
consécutives** sont deux granularité qui se suivent dans cette liste, elles
engendre des **graphes consécutifs de dépendances**.
Il faut veiller à ce que chaque graphes de consécutivité soit plus simple que le
précédent, ils dessinent alors **une pyramide**. Chaque niveaux est une
opportunité de simplifier, synthétiser la description d'une architecture. Bien
entendu il existe des contre-exemples, comme **les classes qui ne contiennent
qu'une seule méthode**.
Il serait naturel de regrouper un ensemble de méthodes très dépendantes en elles
dans une seule classe, ou encore des classes très dépendances dans un
paquetage. C'est le principe **de simplification min-max**: il faut en sorte
que les composants du niveau de granularité \\(G2\\) englobent le **maximum** de
dépendances de \\(G1\\) tel qu'il reste un **minimum** de dépendances en
composants de \\(G2\\).
### Couplage et cohésion
Le couplage représente l'ensemble des dépendances entre composants de \\(G2\\),
la cohésion celles au sein de chacun des composants de \\(G2\\) entre les
éléments de \\(G1\\).
Si \\(G2\\) est un ensemble de classes et \\(G1\\) un ensemble d'attributs et de
méthodes de ces classes alors:
* le couplage correspond à l'ensemble des dépendances entre classe de \\(G2\\);
* la cohésion définie pour chaque classe de \\(G2\\) correspond à l'ensemble
des dépendances entre ses attributs et méthodes.
Pour reprendre notre **principe min-max**: dans une architecture logicielle, pour
toute paire de niveau de granularité, il faut privilégier à la fois **la
cohésion la plus forte** et **le couplage le plus faible**

View file

@ -1,215 +0,0 @@
---
title: "Réseaux et Protocole: couche liaison"
date: 2024-03-29
tags: ["OSI", "ethernet"]
categories: ["Réseaux et protocoles", "Cours"]
mathjax: true
---
Commençons déjà par la terminologie:
* *nœuds* : équipement (routeur, host)
* *canal de communication* : lien adjacent entre nœuds;
* *frame* : paquet de la couche liaison (niveau 2)
La couche 2 transmet une frame entre deux **nœuds ajacents**. Il existe les
liaisons **filaires** (ethernet) et **sans-fils** (wifi, LTE). Chaque protocole
propose des caratéristiques différentes (fiabilité, rapidité etc.)
Comme pour les autres couches étudiée jusqu'à maintenant, celle-ci encapsule les
éléments de celle au dessus en y ajoute son entête. La couche liason ajoute
ainsi les propres mécanismes de contrôles de flux entre les nœuds adjacent ou
encore la détection voire la correction d'erreur. Le mode de communication peut
être *half-duplex* ou *full-duplex*
La couche liason est implémentée sur chacun des nœuds, elle peut l'être
directement sur le contrôleur réseau de façon matérielle mais aussi logicielle.
## Détection d'erreur
Elle se fait par l'ajout d'un champs *EDC* pour *Error Detection Code* dans la
frame. C'est un simple ajout de bits de parité. Cependant ajouter un seul bit de
parité **n'est pas assez fiable**. Il est alors préférable d'utiliser le *two
dimentionnal parity*, un tableau à deux dimensions pour faire du *calcul de
parité* permettant non seulement **la correction** mais aussi la **correction**.
## Multiple access link
Nous avons d'abors des protocole utilisé pour la communication entre deux nœuds :
*PPP* pour *Point to Point Protocol* ou encore *ethernet*. Mais d'autres sont
fait poue la diffusion (*broadcast*), la communication entre plusieurs nœuds
comme le *Wifi* ou les communications par *Satellite*
Dans le second cas, il nous faut un algorithme pour déterminer **l'ordre de
transmisson** : à quel moment un équipement peut envoyer des données sur le
canal partagé entre tous. Il n'y a pas de canal auxiliaire ( *out of band* )
pour organiser le partage de la ressource, il faut alors organiser le
partitionnement
### MAC Protocol
Le canal est divisé en plus petites pièces à l'exclusivité de nœuds. Les accès
sont aléatoires et au tour par tour.
#### partitionnement en fonction du temps
Chaque station a un slot de temps fixe permettant la transmission, si la station
n'a pas de donnée à transmettre, alors le slot passe en IDLE.
#### Frequencie division multiple access *FDMA*
Le canal est divisé sur plusieurs fréquences, chaque nœud se voit attribuer une
fréquence et peut transmettre sur celle-ci en continue.
#### accès aléatoire
Lorqu'un nœud veut émettre, il prend la communication sur l'ensemble du canal,
il n'y a pas de gestion de la coordination. Donc si deux nœuds ou plus émettent
en même temps alors il y a **collision**.
Ces types de protocoles définissent alors comment détecter les collisions et ce
qu'il faut faire dans ce cas.
Vopici des exemples de protocoles de type accès aléatoires: ALOHA, Slotter
ALOHA, CSMA, CSMA/CD, CSMA/CA
##### Slotted ALOHA
Le temps est divisé en intervalle régulier (intervalle de temps) correspondant au
temps nécessaire pour la transmission d'une trame. L'émetteur attend donc le
début d'une intervalle de temps pour transmettre sa trame.
En cas de collision, le nœud retransmet son paquet après avoir attendu un temps
aléatoire. Au mieux, 37% d'efficacité.
##### Carrier Sense Multiple Access
C'est un protocole simple : écouter avant de transmettre :
* Si le canal est libre alors on transmet **la trame entière**;
* sinon on repousse la transmission
##### Carrier Sense Multiple Access with Collision Detection (CSMA/CD)
Les collision sont possible avec CSMA, à cause du délais de propagation de la
trame. La probabilité d'une collision est déterminée par la distance et le
délais de propagation.
Avec CSMA/CD, un hôte annule la transmission d'une trame à l'instant de la
détection d'une collision. Dans le cadre d'Ethernet par exemple:
* Vérification du canal;
* Envoi d'une trame entière si le canal est libre;
* Si aucune collision détectée : fin!
* Sinon annule la transmission et envoie le *signal jam*;
* Entre dans le *binary exponential backoff* : choisi un nombre \\(K\\) entre
\\([1..2^{m-1}]\\) et attend \\(K x 512 \\)temps / bit et relance les étapes
à partir de la vérification du canal.
#### à tour de rôle
C'est un protocole reprenant le meilleur de l'accès aléatoire et du partage de
canal
Deux façons de faire :
* Un contrôleur décide du partage de la ressource et invite les équipement.
C'est le fonctionnent du **Bluetooth** par exemple.
* Un jeton donne le droit à transmission et passe d'hôte en hôte. Cette façon
de procéder donne lieu à un peu *d'overhead*, de plus que faire si le jeton
se perd?
## LAN
### Adressage
La base de cette des LAN est l'adresse MAC inscrite en dur dans le matériel --
mais parfois dans le logiciel, pensez aux machines virtuelles par exemple -- sur
48 bits. Les 3 premiers octets de cette adresse sont alloués par l'IEE en
fonction du fabricat du périphérique.
### Le protocole ARP
Il fait le lien entre l'adresse Ethernet (couche liaison) et l'adresse IP
(couche réseau). La **table ARP** présent sur tous les hôtes connecte au reseau
permet de faire le lien entre les adresses MAC et IP en y associant aussi une
durée de vie , le `TTL`.
Des message de broadcast sont envoyés par les hôtes afin de connaitre l'adresse
MAC des autres si l'entrée n'est pas disponible dans la table (encapsulé dans
une trame Ethernet).
### Ethernet
C'est la technologie dominante sur les réseaux locaux depuis les années 90.
Plusieurs implémentation sont disponibles :
* **bus** pour les réseaux coaxiaux avec un seul domaine de collision;
* **switch** pour les réseaux avec connecteur RJ45, ici un domaine de collision
par machine.
D;abors il y a le préambules : une série de 8 octets dont les 7 presiers sont
`1010101010` puis le dernier à `1010101011`. Son rôle est de synchroniser les
hôtes et de délimiter le début de la transmission.
Viens ensuite la *trame* composées de :
* l'adresse *MAC* de destination;
* l'adresse *MAC* source;
* le *type* indiquant le protocole de la couche supérieure;
* la *charge utile*
* le contrôle d'intégrité (*CRC* pour **Cyclic Redundancy Check*).
Ethernet est un protocole sans connexion : il n'y a pas de *handshake*, pas de
mécanisme d'*ACK*, pas de retransmission. On laisse ça **à la couche supérieure.
Il existe aussi plusieurs standards Ethernet en fonction du type de liaison.
### Les switches
C'est un équipement de la **couche liaison** qui stocket te retransmet les
trames Ethernet. Il examine les adresse MAC dans les entêtes et envoie les
trames vers les hôtes concernés. Il est transparent et ne nécessite pas de
configuration pour un *switching* basique.
Les switches possèdent une **table de transfert** qui contient les tuples
`MAC:Port:TTL` mis a jour en fonction des trames qu'il voit passer. Le switch
modifie dynamiquement cette table en fonction des trames qu'il voit passer sur
ses différents ports. Lorqu'il doit envoyer des trames vers une adresse qu'il ne
connait pas, alors il va emmetre une demande ARP sur **l'ensemble de ses
ports**. Cette auto-apprentissage peur fonctionner même un fois les *switches*
brachés les un aux autres.
Il est capable de communiquer sur plusieurs canaux simultaniement sans
collisions par exemple, communication A <---> B et C <---> D ( mais A <---> B et
A <--> C ne peut se faire en simultané )
### VLAN
Ils permettent de reduire le domaine de broadcast (ARP, DHCP) pour plusieurs
éventuels objectifs:
* attacher logiquement des utilisateurs à leur machines / réseau;
* faciliter la gestion du réseau
* séparer les utilisateurs (sécurité, vie privée)
Mais pour celà il faut des équipement compatibles et de la **configuration**.
Par exemple avec les *ports based VLAN* il est possible d'affecter
dynamiquement les ports à des VLAN.
L'échange de messages entre les VLANs doivent se faire via un **routeur** ou un
**switch de niveau 3**. Les connexions entre les différents switches doivent se
faire va un **lien trunk** permettant de transmettre les informations relatives
aux VLAN.
Ces information son positionnées dans l'entête Ethernet au format *802,1q*. elle
se comporte du *tag protocol* et du *vlan id*.
### MPLS
Ce protocole permet d'assurer un routage et un forwarding plus rapide des
paquets. Il s'intercale entre les couches 2 et 3 et se base sut l'ajout de
labels de taille fixe entre l'entête Ethernet et la charge utile.
Le routeur MPSL regarde donc le label pour décider quoi faire avec le paquet: le
routage est plus rapide (pas de décapsulation) et plus flexible.