Ricerca…


introduzione

Molti linguaggi di programmazione utilizzano socket per comunicare tra processi o tra dispositivi. Questo argomento spiega come utilizzare correttamente il modulo socket in Python per facilitare l'invio e la ricezione di dati su protocolli di rete comuni.

Parametri

Parametro Descrizione
socket.AF_UNIX Socket UNIX
socket.AF_INET IPv4
socket.AF_INET6 IPv6
socket.SOCK_STREAM TCP
socket.SOCK_DGRAM UDP

Invio di dati tramite UDP

UDP è un protocollo senza connessione. I messaggi ad altri processi o computer vengono inviati senza stabilire alcun tipo di connessione. Non c'è conferma automatica se il tuo messaggio è stato ricevuto. UDP viene solitamente utilizzato in applicazioni sensibili alla latenza o in applicazioni che inviano trasmissioni di rete.

Il seguente codice invia un messaggio ad un processo in ascolto sulla porta localhost 6667 usando UDP

Si noti che non è necessario "chiudere" il socket dopo l'invio, poiché UDP è senza connessione .

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

Ricezione di dati tramite UDP

UDP è un protocollo senza connessione. Ciò significa che i peer che inviano messaggi non richiedono di stabilire una connessione prima di inviare messaggi. socket.recvfrom restituisce quindi una tupla ( msg [il messaggio ricevuto dal socket], addr [l'indirizzo del mittente])

Un server UDP che utilizza esclusivamente il modulo 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))

Di seguito è riportata un'implementazione alternativa utilizzando 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()

Di default, blocco sockets . Ciò significa che l'esecuzione dello script attenderà fino a quando il socket riceve i dati.

Invio di dati tramite TCP

L'invio di dati su Internet è reso possibile utilizzando più moduli. Il modulo socket fornisce un accesso a basso livello alle operazioni del sistema operativo sottostante responsabili dell'invio o della ricezione di dati da altri computer o processi.

Il seguente codice invia la stringa di byte b'Hello' a un server TCP in ascolto sulla porta 6667 sull'host local host e chiude la connessione al termine:

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

L'uscita socket sta bloccando di default, il che significa che il programma attenderà il collegamento e invierà le chiamate fino a quando l'azione non sarà completata. Per la connessione significa che il server effettivamente accetta la connessione. Per inviarlo significa solo che il sistema operativo ha abbastanza spazio nel buffer per accodare i dati da inviare in seguito.

Le prese dovrebbero essere sempre chiuse dopo l'uso.

TCP Socket Server multi-threaded

Quando viene eseguito senza argomenti, questo programma avvia un server socket TCP che ascolta le connessioni a 127.0.0.1 sulla porta 5000 . Il server gestisce ciascuna connessione in un thread separato.

Quando viene eseguito con l'argomento -c , questo programma si collega al server, legge l'elenco dei client e lo stampa. L'elenco dei client viene trasferito come una stringa JSON. Il nome del client può essere specificato passando l'argomento -n . Passando nomi diversi, si può osservare l'effetto sulla lista dei clienti.

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

Uscita del server

$ python client_list.py
Starting server...

Uscita client

$ python client_list.py -c -n name1
{
    "name1": {
        "address": "127.0.0.1", 
        "port": 62210, 
        "name": "name1"
    }
}

I buffer di ricezione sono limitati a 1024 byte. Se la rappresentazione della stringa JSON dell'elenco dei client supera questa dimensione, verrà troncata. Ciò causerà la seguente eccezione:

ValueError: Unterminated string starting at: line 1 column 1023 (char 1022)

Socket raw su Linux

Innanzitutto disabiliti il ​​checksum automatico della scheda di rete:

sudo ethtool -K eth1 tx off

Quindi invia il tuo pacchetto, utilizzando 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)


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow