[Python] I Socket: un client TCP-IP

Ho trovato alcune guide molto interessanti per la programmazione dei socket in Python, ed ho deciso di cominciare a studiare un po il linguaggio.

socket Quale situazione migliore se non quella di commentare gli esercizi per fissare il tutto e, per chi fosse interessato, insegnare qualcosa.

Premetto che il codice e la procedure è tutto tranne farina del mio sacco:

Quest blog ha ottimi articoli sul Networking e, ovviamente, su Python :)

In particolare ho utilizzato una Guida introduttiva ai Sockets in Python; fate tesoro del sito, perché è davvero ottimo.

Per aiutarmi ed aiutarvi, metto anche il link alle pagine di manuale sui sockets (è python 2.7).

L'articolo sul blog del link è composto di due parti:

Oggi io descriverò il client, lasciano il server python per la prossima..

Il client

Ed ecco il mio primo socket:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#!/usr/bin/env python2
import socket # :)
import sys  # per exit code
try:
    # creo il socket TCP/IP
    sockMe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg:
    print 'Failed to create a socket. Error Code: ' + str(msg[0]) + ' , error  message: ' + msg[1]
    sys.exit();
print 'il mio primo Socket esiste!'

La mia intestazione deriva dal fatto che utilizzando Archlinux ho Python3 come interprete di _default _mentre questo codice è stato creato per python 2.x. La maggior parte delle distribuzioni utilizzano python 2.x quindi dovreste poter utilizzare

1
#!/usr/bin/env python

Senza stare troppo a divagare, in queste quattro righe di codice si importano le librerie standard pyhton per i sockets (import socket) e di quelle di sistema (guarda un po) per la gestione dell'I/O, l'imput utente etcetc..

Visto che, chi ha fatto la guida è una persona colta, lui ha utilizzato un metodo di gestione delle "eccezioni" del tipo: prova a fare questo e se non riesci, giustificati con questo.

La forma è try:/except: e come tutte le cose in python gli spazi e l'indentazione contano.

Quel che si "prova" (try) a fare è creare un socket, associando la variabile sockMe:

socket.AF_INET, socket.SOCK_STREAM Dove AF_INET sta per protocollo IP e SOCK_STREAM è il portocollo tcp (indovinate cosa è SOCK_DATAGRAM..).

Andando avanti col codice, aggiungiamo un po di pezzi dato che un socket da solo non fa la felicità..

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python2

## My fist approach to sockets

import socket # :)
import sys # for exit code
try:
    # creo il socket TCP/IP
    sockMe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, msg:
    print 'Failed to create a socket. Error Code: ' + str(msg[0]) + ' , error message: ' + msg[1]
    sys.exit();

print 'il mio primo Socket esiste!'

host = sys.argv[1]
port = int(sys.argv[2])

try:
    remote_ip = socket.gethostbyname( host )
except socket.gaierror:
    # non posso risolvere: non ho internet o il dns non funziona
    print 'Hostname non trovato.'
    sys.exit();

print "L'IP di " + host + " e' " + remote_ip

print 'ed ora connettiamoci...'

# connettiamoci a ip-porta
sockMe.connect((remote_ip, port))

Facciamo faville :)

Ora stiamo usando il socket, connettendoci ad un host ed una _porta _che definiamo con _argv (_cioè dall'input passando due argomenti allo script tipo "./client.py google.com 80").

socket.gethostbyname, invece, è il resolver python, che cerca di risolvere l'hostname tramite dns o file /etc/hosts; anche questa volta proviamo a risolvere e, se tutto fila liscio, eseguiamo un sockMe.connect per aprire la connessione con l'host (risolto come IP) e la porta definiti.

Ora che abbiamo un bel socket aperto e connesso all'altro capo della linea, perché non inviamo un bel messaggio al nostro interlocutore:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python2

## My fist approach to sockets

import socket # :)
import sys # for exit code
try:
    # creo il socket TCP/IP
    sockMe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

except socket.error, msg:
    print 'Failed to create a socket. Error Code: ' + str(msg[0]) + ' , error message: ' + msg[1]
    sys.exit();

print 'il mio primo Socket esiste!'

host = sys.argv[1]
port = int(sys.argv[2])

try:
     remote_ip = socket.gethostbyname( host )
except socket.gaierror:
    # non posso risolvere: non ho internet o il dns non funziona
    print 'Hostname non trovato.'
    sys.exit();

print "L'IP di " + host + " e' " + remote_ip

print 'ed ora connettiamoci...'

# connettiamoci a ip-porta
sockMe.connect((remote_ip, port))

message = "W000t W000T"

try:
#inviamo tutto
sockMe.sendall(message)

except socket.error:
    print 'Errore! Non ho potuto inviare i pacchetti'
    sys.exit()

print 'messaggio consegnato'

Semplicemente, con un altro metodo try/except inviamo il messaggio salvato nel buffer con il comando _sendall. _Questo comando svuota il buffer inviando tutto quello che trova all'altro capo della connessione.

Ed ora, il tocco finale: riceviamo la risposta, la leggiamo, la stampiamo e in fine chiudiamo la comunicazione da bravi sviluppatori (non vogliamo lasciare appese le sessioni giusto?!).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python2

## My fist approach to sockets

import socket # :)
import sys # for exit code
try:
# creo il socket TCP/IP
sockMe = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

except socket.error, msg:
    print 'Failed to create a socket. Error Code: ' + str(msg[0]) + ' , error message: ' + msg[1]
    sys.exit();

print 'il mio primo Socket esiste!'

host = sys.argv[1]
port = int(sys.argv[2])

try:
    remote_ip = socket.gethostbyname( host )

except socket.gaierror:
    # non posso risolvere: non ho internet o il dns non funziona
    print 'Hostname non trovato.'
sys.exit();

print "L'IP di " + host + " e' " + remote_ip

print 'ed ora connettiamoci...'

# connettiamoci a ip-porta
sockMe.connect((remote_ip, port))

message = "W000t W000T"

try:
#inviamo tutto
sockMe.sendall(message)

except socket.error:
    print 'Errore! Non ho potuto inviare i pacchetti'
    sys.exit()

print 'messaggio consegnato'

#e ora ricevo
reply = sockMe.recv(1024)

print reply

sockMe.close()

recv è impostato per ricevere una certa quantità di dati (nel nostro caso 1024 byte). Per concludere in bellezza lo script, la sessione e l'articolo, chiudiamo il nostro socket python invocando la funzione close().