Browse Source

Suppression du chapitre sur les sockets

Il ira dans un autre cours, ou peut-être dans le projet depydoc
lui-même
master
Dimitri Merejkowsky 4 years ago
parent
commit
9194a34902
9 changed files with 2 additions and 318 deletions
  1. +0
    -0
      cours/source/17-classes-03/index.rst
  2. +0
    -128
      cours/source/17-sockets/exemple.rst
  3. +0
    -14
      cours/source/17-sockets/index.rst
  4. +0
    -28
      cours/source/17-sockets/introduction.rst
  5. +0
    -145
      cours/source/17-sockets/protocoles.rst
  6. +0
    -0
      cours/source/18-functions-02/01-introduction.rst
  7. +0
    -0
      cours/source/18-functions-02/02-décorateurs.rst
  8. +0
    -0
      cours/source/18-functions-02/index.rst
  9. +2
    -3
      cours/source/index.rst

cours/source/18-classes-03/index.rst → cours/source/17-classes-03/index.rst View File


+ 0
- 128
cours/source/17-sockets/exemple.rst View File

@@ -1,128 +0,0 @@
Exemple
=======

Pour ce premier exemple, le client et le serveur vont tourner sur la même machine: la vôtre!

Le processus du serveur tournera dans un premier terminal, et le processus du client dans un autre.

Pour les différencier, on peut utiliser la variable `sys.ps1`.

Étape 1
-------

Ouvrez deux terminaux, lancez dans chacun d'eux la commande `python3` sans arguments, puis tapez::

>>> import sys
>>> sys.ps1 = "(serveur) >>> "

dans le premier, et::

>>> import sys
>>> sys.ps1 = "(client) >>> "

dans le second.

Vous devriez obtenir le résultat suivant::

(serveur) >>>

::

(client) >>>

Dans chacun d'eux, définissez les variables ``IP`` et ``PORT``::

(serveur) >>> IP = "127.0.0.1"
(serveur) >>> PORT = 3000

::

(client) >>> IP = "127.0.0.1"
(client) >>> PORT = 3000


L'adresse IP ``127.0.0.1`` est spéciale et désigne votre propre machine.
Le PORT 3000 est un port arbitraire


Étape 2
-------

Dans le REPL du serveur, créez une socket du type 'AF_INET' et 'SOCK_STREAM', puis appelez
``bind()`` avec le tuple (IP, PORT) [#f1]_ ::


(serveur) >>> IP = "127.0.0.1"
(serveur) >>> PORT = 3000
(serveur) >>> import socket
(serveur) >>> s_serveur = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
(serveur) >>> s_serveur.bind((IP, PORT))

Ensuite, appelez ``s_serveur.listen(0)``: cela permet à votre serveur d'accepter des connections::

(serveur) >>> s_serveur.listen(0)

Enfin, appelez ``s_serveur.accept`: cette méthode retourne un tuple qu'on note souvent ``con, addr``::

(serveur) >>> con, addr = s_serveur.accept()

Cette fois ci, vous devriez constater que le processus du serveur est *bloqué*: l'invite de commande ne s'affiche
pas - en effet, le serveur est en attente d'une connexion par le client


Étape 3
-------

De la même façon que pour le serveur, créez une socket du même type côté
client::

(client) >>> IP = "127.0.0.1"
(client) >>> PORT = 3000
(client) >>> import socket
(client) >>> s_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Ensuite, *connectez* la socket client à la socket serveur::

(client) >>> s_client.connect((IP, PORT))

Comme par magie, vous devriez voir le processus **dans l'autre terminal** reprendre son exécution:
les deux processus Python sont donc bien en train de *communiquer*


Étape 4
-------

Vous pouvez maintenant utiliser les méthodes ``send()`` et ``recv``, respectivement avec l'objet
``con`` côté serveur, et ``s_client`` côté client pour envoyer et recevoir des messages entre
les deux processus.

Notez que ``send`` prend des ``bytes`` en arguments et renvoie le nombre d'octets envoyés,
et que ``recv()`` prend un nombre d'octets à lire.

On peut envoyer des message du client vers le serveur::


(client) >>> s_client.send(b"Bonjour")
7

::

(serveur) >>> con.recv(7)
b'Bonjour'


Et du serveur vers le client::

(serveur) >>> con.send(b"Comment va ?")
12

::

(client) >>> s_client.recv(12)
b'Comment va ?'


.. rubric:: notes

.. [#f1] Il existe des sockets de plusieurs type et avec des comportements différents. AF_INET et SOCK_STREAM sont
les plus courants.

+ 0
- 14
cours/source/17-sockets/index.rst View File

@@ -1,14 +0,0 @@
Chapitre 17 - Sockets
=====================

Ce chapitre n'est pas à proprement parler un cours sur le langage
Python, mais contient des éléments qui nous servirons à bâtir
un projet à long terme : fabriquer un site Web !


.. toctree::
:maxdepth: 1

introduction
exemple
protocoles

+ 0
- 28
cours/source/17-sockets/introduction.rst View File

@@ -1,28 +0,0 @@
Introduction
=============

Clients et serveurs
-------------------

Dans le chapitre 15, nous avons parlé des données binaires et évoqué le
fait que cela permettait à nos programmes Python d'intéragir avec
l'extérieur, notamment via le système de fichiers.

Il existe dans la librairie standard une autre façon pour Python de
communiquer via *le réseau* : il s'agit du module ``socket``.

Une communication sur le réseau Internet implique:

* Un *client* qui va faire des *requêtes*
* Un *serveur* qui va renvoyer des *réponses* au client
* Une *adresse* du serveur utilisée par le client

Dans notre cas, cette adresse est un *tuple*, composé de deux éléments:
une *adresse IP* et un *port*.

.. image:: /img/client-serveur.png

En général:

* Le *client* et le *serveur* sont sur des machines différentes
* Il n'y a qu'un seul processus qui est capable d'écouter sur un port donné

+ 0
- 145
cours/source/17-sockets/protocoles.rst View File

@@ -1,145 +0,0 @@
Protocoles
==========

Le problème
------------

Notez que ``send()`` et ``recv()`` sont très basiques:

* On peut appeler ``send()`` plusieurs fois d'affilée
* On peut aussi appeler ``recv()`` alors que le client n'a encore rien envoyé,
le serveur va juste bloquer en attendant le prochain message du client.
* Si le client envoie 3 octets et que le serveur utilise ``recv(10)``, il n'y
a pas d'erreur
* Si le client envoie 10 octets et que le serveur utilise ``recv(3)``, il n'y
a pas non plus d'erreur, et il faut appeler ``recv()`` une deuxième fois
(avec 7 par exemple) pour récupérer le message en entier


Protocoles
-----------

Ainsi, si on veut que deux machines s'échangent des messages via Internet, il faut
convenir d'un **protocole** - qui parle en premier, quel genre de message peut-il
envoyer? Il faut aussi convenir d'une façon de communiquer la *taille* des réponses.



Le protocole HTTP
-----------------

Le protocole HTTP est relativement simple. On peut le vérifier en essayant
de se connecter à l'adresse ``(93.184.216.34, 80)``, et envoyant les messages
suivants - à l'heure où j'écris ces lignes, l'IP ci-dessus correspond au site
``http://example.com`` ::

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("93.184.216.34", 80))
s.send(b"GET / HTTP/1.1\r\n")
s.send(b"Host: example.com\r\n")
s.send(b"\r\n")

message = s.recv(351).decode()
print(message)


Résultat:

.. code-block:: text

HTTP/1.1 200 OK
Age: 514391
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Sun, 01 Mar 2020 15:57:23 GMT
Etag: "3147526947+ident"
Expires: Sun, 08 Mar 2020 15:57:23 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (bsa/EB18)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 1256

<!doctype html>


Notez qu'on a appelé `.decode()` sur le message reçu du serveur - on dit que HTTP
est un protocole de *texte*.

Notez que la réponse du serveur rappelle le nom du protocole ``HTTP/1.1`` et répond avec
de nombreuses lignes contenant une *clé* et une *valeur* séparé par des deux-points

On appelle cela des *en-têtes* (ou *headers*). L'un d'eux contient la taille totale du
message: ``Content-Length: 1256``.

Après le bloc de headers, on voit le *corps* de la réponse: quelque chose qui commence
par ``<!doctype html>``.

On peut lire la suite du message::


suite = s.recv(1000).decode()
print(suite)

.. code-block:: text

<html>
<head>
<title>Example Domain</title>

<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
body {
background-color: #f0f0f2;
margin: 0;
padding: 0;
font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;

}
div {
width: 600px;
margin: 5em auto;
padding: 2em;
background-color: #fdfdff;
border-radius: 0.5em;
box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
}
a:link, a:visited {
color: #38488f;
text-decoration: none;
}
@media (max-width: 700px) {
div {
margin: 0 auto;
width: auto;
}
}
</style>
</head>

<body>
<div>
<h1>Example Domain</h1>
<p>This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.</p>
<p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>


Si maintenant vous allez sur ``https://example.com`` avec un navigateur Web, et cliquez sur
"Afficher le code source de la page", vous devriez voir exactement le contenu ci-dessus.

Cela prouve que:

* Il y a un serveur qui écoute sur l'adresse IP de example.com, sur le port 80
* Ce serveur comprend le protocole HTTP
* Un navigateur ne fait rien d'autre que:
* envoyer des requêtes HTTP vers un serveur
* interpréter le texte retourné et l'afficher
* On peut facilement coder à la fois des *clients* et des *serveur* HTTP en Python, juste avec le module socket.


cours/source/19-functions-02/01-introduction.rst → cours/source/18-functions-02/01-introduction.rst View File


cours/source/19-functions-02/02-décorateurs.rst → cours/source/18-functions-02/02-décorateurs.rst View File


cours/source/19-functions-02/index.rst → cours/source/18-functions-02/index.rst View File


+ 2
- 3
cours/source/index.rst View File

@@ -33,6 +33,5 @@ remarques.
14-bibliothèques-01/index
15-fichiers-et-données-binaires/index
16-interpréteur-interactif/index
17-sockets/index
18-classes-03/index
19-functions-02/index
17-classes-03/index
18-functions-02/index