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
3
Embed
Programowanie sieciowe wPythonie WĄŻ W SIECI · ślonym adresem IP iportem – przy czym tyl-ko jeden serwer może zostać związany z da-ną parą IP-port. ... albo włączona
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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:
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.