Python Language
Douilles
Recherche…
Introduction
De nombreux langages de programmation utilisent des sockets pour communiquer entre les processus ou entre les périphériques. Cette rubrique explique l'utilisation appropriée du module sockets dans Python pour faciliter l'envoi et la réception de données via les protocoles réseau courants.
Paramètres
Paramètre | La description |
---|---|
socket.AF_UNIX | Socket UNIX |
socket.AF_INET | IPv4 |
socket.AF_INET6 | IPv6 |
socket.SOCK_STREAM | TCP |
socket.SOCK_DGRAM | UDP |
Envoi de données via UDP
UDP est un protocole sans connexion. Les messages vers d'autres processus ou ordinateurs sont envoyés sans établir de connexion. Il n'y a pas de confirmation automatique si votre message a été reçu. UDP est généralement utilisé dans les applications sensibles à la latence ou dans les applications qui envoient des diffusions à l'échelle du réseau.
Le code suivant envoie un message à un processus écoutant sur le port localhost 6667 en utilisant UDP
Notez qu'il n'est pas nécessaire de "fermer" le socket après l'envoi, car UDP est sans connexion .
from socket import socket, AF_INET, SOCK_DGRAM
s = socket(AF_INET, SOCK_DGRAM)
msg = ("Hello you there!").encode('utf-8') # socket.sendto() takes bytes as input, hence we must encode the string first.
s.sendto(msg, ('localhost', 6667))
Recevoir des données via UDP
UDP est un protocole sans connexion. Cela signifie que les pairs qui envoient des messages ne nécessitent pas d'établir de connexion avant d'envoyer des messages. socket.recvfrom
retourne donc un tuple ( msg
[le message reçu par le socket], addr
[l'adresse de l'expéditeur])
Un serveur UDP utilisant uniquement le module socket
:
from socket import socket, AF_INET, SOCK_DGRAM
sock = socket(AF_INET, SOCK_DGRAM)
sock.bind(('localhost', 6667))
while True:
msg, addr = sock.recvfrom(8192) # This is the amount of bytes to read at maximum
print("Got message from %s: %s" % (addr, msg))
Voici une implémentation alternative utilisant socketserver.UDPServer
:
from socketserver import BaseRequestHandler, UDPServer
class MyHandler(BaseRequestHandler):
def handle(self):
print("Got connection from: %s" % self.client_address)
msg, sock = self.request
print("It said: %s" % msg)
sock.sendto("Got your message!".encode(), self.client_address) # Send reply
serv = UDPServer(('localhost', 6667), MyHandler)
serv.serve_forever()
Par défaut, bloc de sockets
. Cela signifie que l'exécution du script attendra que le socket reçoive des données.
Envoi de données via TCP
L'envoi de données via Internet est possible grâce à plusieurs modules. Le module sockets fournit un accès de bas niveau aux opérations du système d'exploitation sous-jacentes responsables de l'envoi ou de la réception de données provenant d'autres ordinateurs ou processus.
Le code suivant envoie la chaîne d'octets b'Hello'
à un serveur TCP écoutant sur le port 6667 sur l'hôte localhost et ferme la connexion lorsque vous b'Hello'
terminé:
from socket import socket, AF_INET, SOCK_STREAM
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost', 6667)) # The address of the TCP server listening
s.send(b'Hello')
s.close()
La sortie Socket est bloquée par défaut, ce qui signifie que le programme attendra la connexion et enverra des appels jusqu'à ce que l'action soit terminée. Pour la connexion, cela signifie que le serveur accepte réellement la connexion. Pour l'envoi, cela signifie uniquement que le système d'exploitation dispose de suffisamment d'espace tampon pour mettre en file d'attente les données à envoyer ultérieurement.
Les prises doivent toujours être fermées après utilisation.
Serveur TCP multi-thread
Lorsqu'il est exécuté sans arguments, ce programme démarre un serveur socket TCP qui écoute les connexions à 127.0.0.1
sur le port 5000
. Le serveur gère chaque connexion dans un thread séparé.
Lorsqu'il est exécuté avec l'argument -c
, ce programme se connecte au serveur, lit la liste des clients et l'imprime. La liste de clients est transférée sous forme de chaîne JSON. Le nom du client peut être spécifié en passant l'argument -n
. En passant des noms différents, l'effet sur la liste des clients peut être observé.
client_list.py
import argparse
import json
import socket
import threading
def handle_client(client_list, conn, address):
name = conn.recv(1024)
entry = dict(zip(['name', 'address', 'port'], [name, address[0], address[1]]))
client_list[name] = entry
conn.sendall(json.dumps(client_list))
conn.shutdown(socket.SHUT_RDWR)
conn.close()
def server(client_list):
print "Starting server..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('127.0.0.1', 5000))
s.listen(5)
while True:
(conn, address) = s.accept()
t = threading.Thread(target=handle_client, args=(client_list, conn, address))
t.daemon = True
t.start()
def client(name):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 5000))
s.send(name)
data = s.recv(1024)
result = json.loads(data)
print json.dumps(result, indent=4)
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('-c', dest='client', action='store_true')
parser.add_argument('-n', dest='name', type=str, default='name')
result = parser.parse_args()
return result
def main():
client_list = dict()
args = parse_arguments()
if args.client:
client(args.name)
else:
try:
server(client_list)
except KeyboardInterrupt:
print "Keyboard interrupt"
if __name__ == '__main__':
main()
Sortie du serveur
$ python client_list.py
Starting server...
Sortie client
$ python client_list.py -c -n name1
{
"name1": {
"address": "127.0.0.1",
"port": 62210,
"name": "name1"
}
}
Les tampons de réception sont limités à 1024 octets. Si la représentation de la chaîne JSON de la liste de clients dépasse cette taille, elle sera tronquée. Cela provoquera la levée de l'exception suivante:
ValueError: Unterminated string starting at: line 1 column 1023 (char 1022)
Sockets Raw sous Linux
D'abord, vous désactivez la somme de contrôle automatique de votre carte réseau:
sudo ethtool -K eth1 tx off
Ensuite, envoyez votre paquet en utilisant un socket SOCK_RAW:
#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("eth1", 0))
# We're putting together an ethernet frame here,
# but you could have anything you want instead
# Have a look at the 'struct' module for more
# flexible packing/unpacking of binary data
# and 'binascii' for 32 bit CRC
src_addr = "\x01\x02\x03\x04\x05\x06"
dst_addr = "\x01\x02\x03\x04\x05\x06"
payload = ("["*30)+"PAYLOAD"+("]"*30)
checksum = "\x1a\x2b\x3c\x4d"
ethertype = "\x08\x01"
s.send(dst_addr+src_addr+ethertype+payload+checksum)