Programowanie sieciowe wPythonie WĄŻ W SIECI · ślonym adresem IP iportem – przy czym tyl-ko...

3
69 NUMER 60 LUTY 2009 WWW.LINUX-MAGAZINE.PL PROGRAMOWANIE Wąż w sieci Serwer echa Na Listingu 1 widzimy kod prostego serwera echa. Działa on bardzo podobnie do podane- go wcześniej przykładu z netcatem: nasłu- chuje na wybranym porcie i wyświetla na standardowym wyjściu przesyłane do niego komunikaty. Istnieją jednak dwie subtelne różnice: komunikaty są nieco modyfikowane (czego niestety oryginalny netcat nie potrafi, musielibyśmy posłużyć się narzędziem ta- kim jak netsed Michała Zalewskiego), zaś połączenie zamyka się, kiedy serwer otrzy- muje ciąg znaków koniec. Przyjrzyjmy się na- szemu przykładowi bliżej. N a co dzień wszyscy korzystamy ze skryptów powłoki, czy to krótkich, czy bardziej rozbudowanych. Jeśli mamy do wykonania proste, jednorazowe za- danie, zaprzęganie do pracy Pythona najczę- ściej nie ma sensu – łatwiej i szybciej napisać jednolinijkowy skrypt powłoki, tym bardziej że narzut związany z uruchomieniem interpre- tera jest najczęściej zupełnie niepotrzebny. Weźmy prosty przykład: chcemy urucho- mić prosty program wyświetlający komuni- katy na konsoli serwera. Zamiast od razu pi- sać skomplikowany skrypt, wystarczy na ser- werze uruchomić netcata: nc --l numer_portu Komunikaty możemy przesyłać również za pomocą netcata: nc adres_serwera umer_portu Równie dobrze możemy użyć starego, do- brego telneta: telnet adres_serwera umer_portu Kiedy jednak nasze wymagania rosną, netcat przestaje wystarczać. Zacznijmy jed- nak od początku. Programowanie sieciowe w Pythonie WĄŻ W SIECI W życiu każdego administratora przychodzi moment, kiedy standardowe narzędzia nie wystarczają i trzeba przygotować własne rozwiązanie. Lepiej wtedy zamiast ze skryptów powłoki skorzystać z potężnego języka, jakim jest Python. W niniejszym odcinku naszego cyklu pokażemy, jak korzystać z Pythona w komunikacji klient-serwer. Rysunek 1: Prosta sesja netcata – serwer echo. www.sxc.hu

Transcript of Programowanie sieciowe wPythonie WĄŻ W SIECI · ślonym adresem IP iportem – przy czym tyl-ko...

69NUMER 60 LUTY 2009WWW.LINUX-MAGAZINE.PL

PROGRAMOWANIEWąż w sieci

Serwer echa Na Listingu 1 widzimy kod prostego serweraecha. Działa on bardzo podobnie do podane-go wcześniej przykładu z netcatem: nasłu-chuje na wybranym porcie i wyświetla nastandardowym wyjściu przesyłane do niegokomunikaty. Istnieją jednak dwie subtelne

różnice: komunikaty są nieco modyfikowane(czego niestety oryginalny netcat nie potrafi,musielibyśmy posłużyć się narzędziem ta-kim jak netsed Michała Zalewskiego), zaśpołączenie zamyka się, kiedy serwer otrzy-muje ciąg znaków koniec. Przyjrzyjmy się na-szemu przykładowi bliżej.

Na co dzień wszyscy korzystamy zeskryptów powłoki, czy to krótkich,czy bardziej rozbudowanych. Jeśli

mamy do wykonania proste, jednorazowe za-danie, zaprzęganie do pracy Pythona najczę-ściej nie ma sensu – łatwiej i szybciej napisaćjednolinijkowy skrypt powłoki, tym bardziejże narzut związany z uruchomieniem interpre-tera jest najczęściej zupełnie niepotrzebny.

Weźmy prosty przykład: chcemy urucho-mić prosty program wyświetlający komuni-katy na konsoli serwera. Zamiast od razu pi-sać skomplikowany skrypt, wystarczy na ser-werze uruchomić netcata:

nc --l numer_portu

Komunikaty możemy przesyłać równieżza pomocą netcata:

nc adres_serwera umer_portu

Równie dobrze możemy użyć starego, do-brego telneta:

telnet adres_serwera umer_portu

Kiedy jednak nasze wymagania rosną,netcat przestaje wystarczać. Zacznijmy jed-nak od początku.

Programowanie sieciowe w Pythonie

WĄŻ W SIECIW życiu każdego administratora przychodzi moment,

kiedy standardowe narzędzia nie wystarczają

i trzeba przygotować własne rozwiązanie.

Lepiej wtedy zamiast ze skryptów powłoki

skorzystać z potężnego języka, jakim jest Python.

W niniejszym odcinku naszego cyklu pokażemy,

jak korzystać z Pythona w komunikacji klient-serwer.

Rysunek 1: Prosta sesja netcata – serwer echo.

ww

w.s

xc.h

u

069-071_python.qxd 12/4/2008 9:31 AM Page 69

Na początku definiujemy adres serwerai port. Zwróćmy uwagę, że jeśli zamiast ad-resu podamy ””, nasz serwer nasłuchuje nawszystkich interfejsach; chcąc ograniczyćdziałanie do lokalnego hosta, powinniśmy

podać 127.0.0.1 itd., możemy też użyć nazwydomenowej. Port 5060 jest zarezerwowanydla protokołu SIP, teoretycznie powinniśmywięc wybrać jakiś nieprzypisany (na przy-kład 1027 czy 2187), w praktyce nie ma towiększego znaczenia, zakładamy bowiem, żeznamy usługi zainstalowane w systemie –należy tylko pamiętać, że nasłuchiwanie naportach poniżej 1024 wymaga uprawnień ad-ministratora (lub innego mechanizmu po-zwalającego nieuprzywilejowanym użytkow-nikom korzystać z portów niższych od 1024).

Następnie tworzymy obiekt gniazda na-leżącego do rodziny (AF to „Address Fami-ly”, czyli właśnie rodzina adresów) AF_IN-ET. Jest to najbardziej popularny typ gniaz-da w połączeniach internetowych; do dyspo-zycji mamy również AF_UNIX, czyli unik-sowe gniazda używane do połączeń na tej sa-mej maszynie (na przykład przez MySQLi wiele innych procesów) i AF_INET6, uży-wane do połączeń IPv6. Kiedy mamy jużobiekt gniazda, korzystamy z sieciowej funk-cji bind(), która wiąże dane gniazdo z okre-ślonym adresem IP i portem – przy czym tyl-ko jeden serwer może zostać związany z da-ną parą IP-port. Samo przypisanie nie wy-starczy: dopiero dzięki funkcji listen(), którejargumentem jest liczba akceptowanych po-łączeń, nasz serwer faktycznie zaczyna na-słuchiwać na podanej kombinacji IP i portu.

Zwróćmy uwagę, że poniżej mamy za-gnieżdżoną pętlę: zewnętrzna akceptuje no-

we połączenia, a wewnętrzna – transmisjęw obrębie danego połączenia. Funkcja ac-cept() tworzy nowe połączenie, adres klientawyświetlamy na standardowym wyjściu. Po-ra teraz na pętlę wewnętrzną, obsługującąnawiązane właśnie połączenie: dane przesy-łane są za pomocą funkcji send() w buforzeo długości 1024 bajtów. Jeśli serwer otrzymaciąg koniec, połączenie zostaje przerwane –w przeciwnym wypadku dane są odesłanedo klienta wraz z ciągiem Dane:.

W tym prostym przypadku nie potrzebu-jemy nawet szczególnego programu w roliklienta – możemy przetestować nasz serwerzwykłym telnetem. Tak jak poprzednio, łą-czymy się z serwerem poleceniem:

telnet nazwa_serwera numer_portu

70 NUMER 60 LUTY 2009 WWW.LINUX-MAGAZINE.PL

Wąż w sieciPROGRAMOWANIE

#!/usr/bin/env pythonfrom socket import *ip_serwera = “”port_serwera = 5060

gniazdo = socket (AF_INET, SOCK_STRE-AM)gniazdo.bind ((ip_serwera, port_ser-wera))gniazdo.listen (10)

while 1:polaczenie, adres = gniazdo.accept()print “Klient z adresu”, adreswhile 1:dane = polaczenie.recv (1024)if dane.strip() == “koniec”:polaczenie.close()exit()else:polaczenie.send (“Dane: “ + dane)

Listing 1: Sieciowe echow Pythonie – kod serwera

#!/usr/bin/env pythonfrom socket import *import timeip_serwera = “”port_serwera = 5060

gniazdo = socket (AF_INET, SOCK_STRE-AM)gniazdo.bind ((ip_serwera, port_ser-wera))gniazdo.listen (10)

while 1:polaczenie, adres = gniazdo.accept()print “Klient z adresu”, adrespolaczenie.send (time.ctime())polaczenie.close()

Listing 2: Serwer czasuw Pythonie

#!/usr/bin/env python

from socket import *import sys

gniazdo = socket (AF_INET, SOCK_STRE-AM)gniazdo.connect ((sys.argv[1], int(sys.argv[2])))czas = gniazdo.recv (1024)gniazdo.close()print “Czas: “, czas

Listing 3: Klient serweraczasu

Rysunek 2: Działanie serwera echa z Listingu 1 – po otrzymaniu komunikatu „koniec” serwerprzestaje działać.

069-071_python.qxd 12/4/2008 9:31 AM Page 70

71NUMER 60 LUTY 2009WWW.LINUX-MAGAZINE.PL

PROGRAMOWANIEWąż w sieci

wiadomości znajdującej siew naszej skrzyn-ce pocztowej. Wykorzystamy do tego modułpoplib, implementujący wszystkie standar-dowe elementy protokołu POP3. Korzysta-nie z niego jest wyjątkowo proste, co poka-żemy na przykładzie interaktywnej sesjiz interpreterem Pythona. Najpierw impor-tujemy moduł:

import poplib

Następnie nawiązujemy połączenie z ser-werem POP3:

s =poplib.POP3 (“poczta.wp.pl”)

Jeśli nie pojawia się komunikat o błę-dzie, oznacza to, że serwer działa i obsługujeprotokół POP3. Podajemy więc nazwę użyt-kownika:

s.user(“grbpdfz”)

W odpowiedzi powinniśmy otrzymaćciąg zaczynający się od +OK. Możemy terazpodać hasło:

s.pass_(“grbpdfz1”)

Jeśli wszystko poszło sprawnie, serwerzwraca komunikat (również zaczynający sięod +OK), zwykle informujący o liczbie wia-domości w skrzynce. Wartość tę możemy po-brać w następujący sposób:

s.stat()

W rezultacie otrzymujemy krotkę skła-dającą się z liczby wiadomości i ich objętości.Wynika z tego, że numer ostatniej wiadomo-ści w skrzynce uzyskujemy poleceniem:

s.stat()[0]

W jaki sposób pobrać ostatnią wiado-mość? Nic prostszego – używamy w tym celufunkcji retr():

s.retr(s.stat()[0])

Możemy też pobrać sam nagłówek, coczęsto ma większy sens:

s.top(s.stat()[0], 0)

Co dalej?Mamy nadzieję, że prezentując podstawyprogramowania klient-serwer w Pythonie,zachęciliśmy do własnych eksperymentówi eksploracji fascynującego świata programo-wania sieciowego. W kolejnych artykułachz tej serii pokażemy bardziej rozbudowaneprzykłady korzystania z mechanizmów sie-ciowych, które oferują moduły Pythona. ■

Program możemy testować dowoli – wpi-sanie ciągu koniec zamyka połączenie i dodat-kowo kończy działanie serwera.

Serwer czasuPowinniśmy więc móc teraz napisać prostyserwer czasu, który nasłuchuje na danym por-cie i po nawiązaniu połączenia przez klientazwraca bieżącą datę i godzinę. Kod takiegoserwera przedstawiliśmy na Listingu 2. Jakwidzimy, jest on prostszy niż poprzednio –nie tworzymy nawet pętli obsługującej trans-misję danych w ramach danego połączenia,tylko przesyłamy bieżącą datę (time.ctime())i rozłączamy się, zaś serwer działa nadal.Działanie serwera możemy przetestować, jakpoprzednio, telnetem bądź netcatem.

W tym przypadku jednak przydaje namsię jednak prosty klient, który łączy się z ser-werem i zwraca pobrany napis. Kod takiegoklienta widzimy na Listingu 3. Tablicasys.argv zawiera listę argumentów wiersza po-leceń, tak więc sys.argv[1] zawiera pierwszyargument, sys.argv[2] – drugi itd. (sys.argv[0]to nazwa samego skryptu). Tak więc programwywołujemy z dwoma argumentami, pierw-szym jest adres IP serwera (bądź jego nazwa),a drugim port, na przykład:

./klient.py localhost 5060

Po nawiązaniu połączenia z serweremklient powinien zwrócić pobrany z serweranapis i zakończyć działanie. Jeśli tak się niedzieje, najprawdopodobniej serwer nie na-słuchuje na podanej kombinacji IP i portualbo włączona jest zapora sieciowa blokującanawiązywanie połączeń.

Programowanie serwerów w Pythonie tobardzo rozległe zagadnienie. Szczególnymwyzwaniem jest właściwa obsługa wieluklientów naraz. Do wyboru mamy wiele roz-wiązań, z których część jest zasobożernychi mało przenośnych (na przykład rozwidlanieprocesu dla każdego klienta) czy wymagają-cych dużej ostrożności (wątkowanie połą-czeń, używanie gniazd nieblokujących z se-lect()); Python udostępnia jednak gotowe mo-duły, takie jak asyncore, które ułatwią nampracę. Zainteresowani powinni zapoznać sięz dokumentacją odpowiednich modułówi dostępną literaturą.

Poczta w PythonieDo tej pory stosunkowo dużo czasu poświę-ciliśmy serwerom. Pora na użyteczny przy-kład klienta – tym razem będzie to prostenarzędzie służące do pobierania ostatniej

[1] http://lcamtuf.coredump.cx/soft/netsed.tgz

INFO

Rysunek 3: Testujemy netcatem serwer czasu z Listingu 2.

069-071_python.qxd 12/4/2008 9:31 AM Page 71