This is an old revision of the document!
Pour pouvoir discuter de la communication dans un réseau, il est important de connaître les notions suivantes:
L'adresse physique, également appelée adresse MAC, est un nombre de 48 bits qui identifie de manière unique une carte réseau. Ce numéro est inscrit sur la carte réseau par le fabricant, donc généralement il ne peut pas être modifié. L'adresse MAC est utilisée pour la transmission de données au niveau d'accès au réseau dans la pile TCP / IP.
Une adresse MAC est représentée en regroupant les 48 bits en 6 octets et en écrivant chaque octet dans la base 16 (ex: 00: A0: C9: 14: C8: 29 ou 00A0-C914-C829).
Une adresse IP identifie une station dans un réseau et est utilisée pour la transmission de données au niveau du réseau à partir de la pile TCP / IP.
Fondamentalement, l'IP d'une station est un nombre, 32 bits dans le cas du protocole IPv4 ou 128 bits dans le cas du protocole IPv6. Habituellement, les adresses IP sont écrites sous une forme restreinte. Dans le cas d'IPv4, l'adresse IP est écrite sous la forme de 4 chiffres dans la base décimale, avec des valeurs comprises entre 0 et 255, séparées par. (ex: 192.168.0.14), et dans le cas d'IPv6, l'adresse IP est écrite sous la forme de 8 groupes de nombres dans la base hexadécimale, avec des valeurs comprises entre 0000 et ffff, séparées par : (ex: 2001:0db8:85a3:0000:0000:8a2e:0370:7334).
Le port est un identifiant utilisé pour la transmission de données au niveau de l'application.
Pour garantir que deux applications communiquent dans un réseau, nous devons spécifier l'adresse IP des stations communicantes, ainsi que le port. Alors que l'adresse IP garantit que les paquets arrivent à destination, le port garantit que le paquet reçu est utilisé par l'application appropriée.
Par exemple, deux ordinateurs peuvent communiquer à la fois pour échanger des e-mails, mais aussi pour réaliser une video-conférence. Ainsi, les deux ordinateurs échangent des messages pour les deux applications (messagerie électronique et video-conférence). Alors que les adresses IP sont utilisées pour garantir que tous les paquets arrivent au bon endroit, les applications de messagerie électronique et de video-conférence auront un port différent, en fonction des messages qui seront distribués à la bonne application.
Afin de pouvoir transmettre des messages sur le réseau, nous utiliserons des structures de socket.
Un socket permet la transmission de messages entre applications sur la même machine ou sur différentes machines physiques, d'une manière similaire à celle utilisant des descripteurs de fichiers.
En Python, on va utiliser le module socket pour les opérations de communication en réseau. Parmi les opérations que nous pouvons effectuer en utilisant le module “socket” figurent:
La fonction gethostname renvoie l'adresse IPv4 de la station locale.
socket.gethostname()
La fonction getaddrinfo renvoie une liste de 5 éléments qui contiennent des informations sur une adresse et un port spécifiés. La liste renvoyée contient les informations suivantes: (family, type, proto, canonname, sockaddr)
, et sockaddr
est une autre liste qui contient (address, port)
.
socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)
La fonctiongethostbyname renvoie l'adresse IPv4 d'un hostname
.
socket.gethostbyname(hostname)
En utilisant des sockets, nous pouvons réaliser deux types de transmission de données: UDP ou TCP.
Pour les deux types de transmissions, nous devons créer un nouvel objet socket à l'aide de la fonction socket (family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None). Selon les paramètres transmis au constructeur, le socket est configuré pour un certain type de transfert de données.
AF_INET
, pour IPv6 le type est AF_INET6
); dans notre cas, nous utiliserons AF_INET (socket.AF_INET, en Python); SOCK_STREAM
(pour le transfert TCP) et SOCK_DGRAM
(pour le transfert UDP); descripteur de fichier
, et si ce paramètre est passé, toutes les 3 autres valeurs sont définies automatiquement en fonction de celui-ci.import socket s = socket.socket()
Pour le transfert TCP, on utilise le paradigme client-serveur, dans lequel un processus attend les connexions (serveur), tandis que d'autres processus peuvent s'y connecter (client). En utilisant TCP, les données transmises atteindront sûrement la destination ou une erreur sera reçue.
Pour démarrer un serveur en mode socket, nous devons effectuer les opérations suivantes:
bind
est 0.0.0.0
, le socket prend en charge les connexions sur toutes les cartes réseau.
import socket s = socket.socket() s.bind(('0.0.0.0', 8000)) s.listen(0) conn, addr = serv.accept()
Pour établir une connexion à partir du serveur, en utilisant le mode socket, nous devons appeler la fonction connect.
Pour mettre fin à la connexion avec le serveur, nous utiliserons la fonction close.
import socket s = socket.socket() s.connect(('localhost', 8000)) # send/receive data s.close()
localhost
ou 127.0.0.1
.
Pour envoyer des données à l'aide du module socket, nous pouvons utiliser les fonctions send et sendto.
socket.send(bytes[, flags]) socket.sendto(bytes, address) socket.sendto(bytes, flags, address)
La fonction send
envoie des données uniquement si le socket est connecté à un autre socket, il ne peut donc être utilisé que pour une transmission TCP.
La fonction sendto
envoie des données à une adresse passée en paramètre, sans nécessiter de connexion entre les deux objets socket, afin qu'elle puisse être utilisée pour la transmission UDP.
Pour recevoir des données en utilisant le module socket
nous pouvons utiliser les fonctions recv et recvfrom.
socket.recv(bufsize[, flags]) socket.recvfrom(bufsize[, flags])
La fonction recv
lit les données à leur taille maximale, spécifiée comme paramètre ( bufzise
), et les retourne comme un objet bytes
. Cette fonctionnalité convient à la transmission TCP, où nous avons une connexion constante à une autre socket.
La fonction recvfrom
lit des données similaires à la fonction recv
, mais renvoie deux objets: le message et l'adresse source. Ainsi, nous pouvons utiliser l'adresse retournée pour renvoyer des messages. Cette fonctionnalité convient à la transmission UDP, où nous n'avons aucune connexion à une autre prise.
address
renvoyé est un tuple de type (adresse, port), par exemple (“0.0.0.0”, 8000).
Dans ces exemples, nous allons créer un processus qui attend et répond continuellement aux messages (serveur) et un autre processus qui envoie un message et attend une réponse (client).
import socket buffersize = 2048 # create socket s = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) # bind socket to address and port s.bind (("0.0.0.0", 8000)) # listen for data forever and reply with "Message received" while True: data, addr = s.recvfrom(buffersize) print ("Data from {} is {}".format (addr, data)) msg = str.encode ("Message received") s.sendto (msg, addr)
import socket buffersize = 2048 # create socket s = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) # create bytes object msg = str.encode("Hello") # send message s.sendto (msg, ("localhost", 8000)) # read message data, addr = s.recvfrom (buffersize) print ("Data from {} is {}".format (addr, data))
import socket buffersize = 2048 # create socket s = socket.socket (family=socket.AF_INET, type=socket.SOCK_STREAM) # bind socket to address and port s.bind (("0.0.0.0", 8000)) # wait for connections s.listen (0) # accept connections forever while True: conn, addr = s.accept () print ("Connected to {}".format (addr)) # listen for data and reply with "Message received" while True: data = conn.recv(buffersize) # client finished sending message if not data: break print ("Received {}".format (data)) msg = str.encode ("Message received") conn.send (msg) # close connection conn.close ()
import socket buffersize = 2048 # create socket s = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) # connect to server s.connect (("localhost", 8000)) # create bytes object msg = str.encode("Hello") # send message s.send (msg) # read message data, addr = s.recv (buffersize) # close connection s.close () print ("Data from {} is {}".format (addr, data))
Netcat est un utilitaire de la ligne de commande qui permet des opérations réseau. Dans cet exercice, nous allons l'utiliser pour échanger des messages avec des programmes python dans les exemples ci-dessus.
netcat -z -v localhost 8000
. Notez la réponse reçue. echo test | netcat -u localhost 8000
. Notez le comportement résultant (l'option -u spécifie le mode de transmission UDP). netcat
.
Créez deux programmes qui communiquent en mode UDP via socket afin que l'un des programmes reçoive trois nombres séparés par ;
et réponde avec leur moyenne arithmétique.
Simulez un serveur TCP qui reçoit des commandes bash et répond avec leur résultat. Créez un client qui envoie des commandes et affiche le résultat reçu du serveur. Astuce: utilisez un pipe pour stocker la sortie de la commande démarrée.
Modifiez le serveur créé au point précédent pour prendre en charge plusieurs connexions simultanées. Utilisez des threads dans la mise en œuvre. Astuce: Le programme principal accepte les connexions et une fois la connexion établie, démarrez un nouveau thread qui reçoit la connexion en tant que paramètre.