Python i programowanie sieciowe
14 July 2008
Comments
Python udostępnia szeroką gamę narzędzi do programowania sieciowego. Moduły pythona głównie współpracują z protokołami TPC i UDP. Ten pierwszy jest najczęściej używanym i bardziej wiarygodnym od UDP. Oba protokoły sieciowe realizowane są za pomocą abstrakcyjnych obiektów zwanych gniazdami. Gniazdo jest obiektem podobnym do pliku, który pozwala na nawiązywanie połączeń, odbieranie danych i inne operacje. Dodatkowo komputer odbierający dane musi powiązać swoje gniazdo z portem. Przykładowo FTP korzysta z portów 20 (dane) i 21 (sterowanie), SMTP 25, HTTP (www) 80, HTTPS 443.Moduł Socket
Moduł Pythona "socket" daje nam bezpośredni dostęp do standardowego interfejsu BSD dla gniazd, który wykorzystywany jest w większości współczesnych systemów operacyjnych. By stworzyć serwer musimy:- Stworzyć gniazdo
- Przypisać gniazdo do adresu IP i portu
- Nasłuchiwać nadchodzących połączeń
- Oczekiwać klientów
- Zaakceptować klienta
- Wysyłać i odbierać dane
- Stworzyć gniazdo
- Połączyć się z serwerem
- Wysyłać i odbierać dane
- AF_UNIX - Gniazdo Unixowe umożliwia dwóm procesom działającym na tej samej maszynie do porozumiewania się. W Pythonie adresy gniazd Unixowych reprezentowane są w postaci łańcuchów.
- AF_INET - Gniazdo IPv4 to gniazdo pomiędzy dwoma procesami, potencjalnie działającymi na dwóch maszynach używające obecnej wersji adresów IP. Ten typ jest obecnie najczęściej używany. W Pythonie gniazda IPv4 reprezentowane są w postaci tupli (host, port), gdzie host to łańcuch a port to liczba całkowita - numer portu. Jako host można podać IP lub adres www, np. www.google.pl
- AF_INET6 - Podobne do AF_INET, lecz używa IP w wersji 6, która to używa 128 bitowych adresów IP a nie 32 bitowych jak to robi obecnie stosowana wersja IPv4. W Pythonie gniazda tego typu reprezentowane są w postaci tupli (host, port, flowinfo, scopeid) gdzie "flowinfo" to identyfikator przepływu, a "scopeid" identyfikator zakresu. Obecnie IPv6 nie jest powszechnie stosowany.
socket(rodzina,typ[,protokół])
Rodzina przyjmuje wartości AF_UNIX, AF_INET, lub AF_INET6. Istnieje kilka typów gniazd: "SOCK_STREAM" dla gniazd TCP i SOCK_DGRAM dla UDP. Zazwyczaj można pominąć numer protokołu. Metoda "socket()" zwraca obiekt "socket", który można wykorzystać w celu wykonania na nim metod takich jak bind, listen, accept czy connect. Oto prosty przykład serwera i klienta komunikujących się poprzez TCP. Serwer:
from socket import *
import time
s = socket(AF_INET, SOCK_STREAM) #utworzenie gniazda
s.bind(('', 8888)) #dowiazanie do portu 8888
s.listen(5)
while 1:
client,addr = s.accept() # odebranie polaczenia
print 'Polaczenie z ', addr
client.send(time.ctime(time.time())) # wyslanie danych do klienta
client.close()
from socket import *
s = socket(AF_INET, SOCK_STREAM) #utworzenie gniazda
s.connect(('localhost', 8888)) # nawiazanie polaczenia
tm = s.recv(1024) #odbior danych (max 1024 bajtów)
s.close()
print 'Czas serwera: ', tm
Dokumentacja modułu
Moduł Select
Moduł "select" umożliwia aplikacji na oczekiwanie danych z różnych gniazd w tym samym czasie. Oznacza to że serwer używający tego modułu może obsługiwać wielu klientów na raz, jednakże nie używa on wątków, więc może obsługiwać jednego klienta na raz. Medota "select()" używa składni:select(input,output,exception[,timeout])
Pierwsze trzy argumenty to listy gniazd lub obiektów plików, które oczekują na dane wejściowe lub wyjściowe lub wyjątek. Jeżeli nie zostanie podany "timeout" - maksymalny czas działania, to wywołanie "select" będzie zablokowane dopóki na jednym z listowanych gniazd nie wydarzy się wyjątek/dane wejściowe/wyjściowe. Wartość zero oznacza że wywołanie nie będzie zablokowane jeżeli żadne z gniazd jest gotowe. Metoda "select()" zwraca tuplę składającą się z trzech list: listę gniazd i plików, na których zaszły zdarzenia. Jeżeli został przekroczony maksymalny czas bez zajścia zdarzeń wszystkie listy będą puste.Dokumentacja modułu
Wątki
Moduł "threading" udostępnia wysokopoziomowy interfejs wątków. Serwer bez wątków może obsługiwać jednocześnie jednego klienta, a reszta musi czekać. W przypadku zastosowania wątków serwer może obsługiwać wielu klientów jednocześnie, co znacznie zwiększa wydajność. Oto przykładowy serwer wykorzystujący wątki:import select
import socket
import sys
import threading
class Server:
def __init__(self):
self.host = ''
self.port = 50000
self.backlog = 5
self.size = 1024
self.server = None
self.threads = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.host,self.port))
self.server.listen(5)
except socket.error, (value,message):
if self.server:
self.server.close()
print "Could not open socket: " + message
sys.exit(1)
def run(self):
self.open_socket()
input = [self.server,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for s in inputready:
if s == self.server:
# handle the server socket
c = Client(self.server.accept())
c.start()
self.threads.append(c)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
# close all threads
self.server.close()
for c in self.threads:
c.join()
class Client(threading.Thread):
def __init__(self,(client,address)):
threading.Thread.__init__(self)
self.client = client
self.address = address
self.size = 1024
def run(self):
running = 1
while running:
data = self.client.recv(self.size)
if data:
self.client.send(data)
else:
self.client.close()
running = 0
if __name__ == "__main__":
s = Server()
s.run()
class Client(threading.Thread):
def __init__(self,(client,address)):
threading.Thread.__init__(self)
self.client = client
self.address = address
self.size = 1024
def run(self):
running = 1
while running:
data = self.client.recv(self.size)
if data:
self.client.send(data)
else:
self.client.close()
running = 0
class Server:
def __init__(self):
self.host = ''
self.port = 50000
self.backlog = 5
self.size = 1024
self.server = None
self.threads = []
def open_socket(self):
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.bind((self.host,self.port))
self.server.listen(5)
except socket.error, (value,message):
if self.server:
self.server.close()
print "Could not open socket: " + message
sys.exit(1)
def run(self):
self.open_socket()
input = [self.server,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for s in inputready:
if s == self.server:
# handle the server socket
c = Client(self.server.accept())
c.start()
self.threads.append(c)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
# close all threads
self.server.close()
for c in self.threads:
c.join()
Jako klient posłuży nam:
import socket
import sys
import time
host = 'localhost'
port = 50000
size = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
sys.stdout.write('%')
while 1:
line = str(time.ctime(time.time())) + '
'
s.send(line)
data = s.recv(size)
sys.stdout.write(data)
s.close()
Dokumentacja modułu
HTTP
Python umożliwia też programowanie serwerów HTTP za pomocą kilku modułów. Poniższy przykład prezentuje zastosowanie dwa z nich:from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import os
os.chdir("sciezka/do_katalogu/z_danymi")
serv = HTTPServer(("",8888), SimpleHTTPRequestHandler)
serv.serve_forever()
urllib - moduł stosowany do pobierania stron www - poprzez adresy URL.
urllib2 - ten moduł obsługuje również autoryzację, cookies i inne dodatkowe opcje.
import urllib2
f = urllib2.urlopen('http://www.strona.pl/index.html')
print f.read()
smtplib - moduł do obsługi protokołu smtp - wysyłania maili itp.
robotparser - moduł parsujący robots.txt na serwerach www
imaplib - moduł do obsługi protokołu IMAP
ftplib - moduł do obsługi połączeń FTP
Oraz: asyncore, CGIHTTPServer, htmllib, HTMLParser i inne :)
Jeżeli interesuje nas dość szczegółowo programowanie sieciowe w pythonie to warto przyjrzeć się frameworkowi Twisted - twistedmatrix.com.
W sieci
Kurs Pythona - Wykład 10, 11, PLTutorial on Network Programming with Python - PDF
Socket Programming HOWTO
Untwisting Python Network Programming
Python Network Programming
RkBlog
Comment article