|
|
Többplatformos hálózatos szolgáltatás készítése Python nyelven |
|
|
|
Olyan szerver program elkészítését mutatjuk be ebben a cikkben, amely:
- Több szálon fut, egyszerre több kérelmet is kiszolgálhat
- Több platformon is működik (Windows/Linux/FreeBSD)
- A háttérben, szolgáltatásként fut
A cikk több célból készült. Egyrészt megpróbálja a kedves Olvasót végigvezetni egy
többszálú szerver írásának lépésein, néhány kevésbé ismert 'trükköt' bemutatva.
Ez az úgynevezett 'KNOW-HOW', amely nélkül nehezen lehetne kapásból megírni egy ilyen
szervert. A példa úgy lett megkonstruálva, hogy könnyedén felhasználható más
szerverek vázaként is.
Másrészt az itt bemutatott példaprogram önmagában is hasznos lehet; mivel alkalmas
"Önfrissítő" alkalmazások létrehozására. Ehhez a kliens gépeken nincsen szükség a
Python telepítésére, mellékelünk egy Delphi nyelven írt közvetlenül futtatható win32
programot is, ami az itt leírt szervert felhasználva ilyen "Önfrissítő" alkalmazások
létrehozását teszi lehetővé.
|
|
|
|
|
|
|
|
A program futtatásához a Python 2.4 (vagy magasabb) verziója szükséges. Ezt a Python honlapjáról
szerezhetjük be. www.python.org Ezen kívül Windows rendszer
alatt szükségünk lesz a Python Windows Extensions telepítésére is, ez itt található:
sourceforge.net/projects/pywin32/
A pywin32 telepítése után föl kell vennünk a PATH környezeti változóba a pythonservice.exe
elérési útját.
Végezetül, ahhoz, hogy a pywin32 megfelelően működjön, be kell másolnunk az mfc71.dll állományt
a Windows SYSTEM32 könyvtárába. Licenszelési problémák miatt ez nem kerülhetett be a pywin32
csomagba, ezért külön kell letöltenünk, például innen.
Hozzunk létre egy üres könyvtárat a project számára, nevezzük el SimpleHTTPService-nek.
|
|
|
|
|
|
|
|
Feladatként egy olyan szerver megírását tűztük ki célul, ami könnyen megvalósítható, mégis
jól bemutatja a szerver írás lépéseit, és értelmesen felhasználható. Egy egyszerű webszervert
fogunk írni, ami egy előre megadott könyvtárban található állományokat publikál. A kiszolgáló
vázának felépítésére koncentrálunk, nem pedig a HTTP protokoll részletes ismeretetésére.
Ebből a célból a Python-ban már előre megírt modult, a
SimpleHTTPServer modult fogjuk
felhasználni.
|
|
|
|
|
|
|
|
A szervert több, jól elkülöníthető modulra fogjuk szétbontani:
- Config.py - A szerver konfigurációja. Nincs benne számottevő kód, csak konstansok.
- ServiceLog.py - Naplózó eszköz. Egységesített módon teszi lehetővé a szerverrel kapcsolatot események naplózását.
- Application.py - Egy wrapper program ami a szervert alkalmazásként vagy UNIX szerverként futtatja.
- Service.py - Egy wrapper program that ami a szervert win32 szerverként futtatja.
- Processor.py - A lényegi kód, ami a szolgáltatást valósítja meg. Ez az egyetlen lényeges
modul amit át kell írni, ha más szolgáltatást akarsz írni.
Most végigmegyünk a modulokon, és egyenként leírjuk őket.
|
|
|
|
|
|
|
|
Ebben a modulban a beállításokat helyezzük el. Nincs benne számottevő kód, csak nevesített konstansok.
import os
from socket import *
# Service configuration below
# A win32 szolgáltatás neve
SERVICE_NAME = 'SimpleHTTPService'
# A win32 szolgáltatás leírása
SERVICE_DISPLAY = SERVICE_NAME
# Ide írd be a szolgáltatás könyvtárát (ahol az Python állományok vannak)
BASEDIR = 't:\\Python\\Projects\\SimpleHTTPService'
# Ide fogjuk menteni a naplót
LOGDIR = BASEDIR + os.sep + 'Log'
# Ez lesz a szolgáltatott állományok könyvtára
DOCUMENTROOT = BASEDIR + os.sep + 'DocumentRoot'
# Itt fogadjuk a kérelmeket (IP cím)
LISTEN_HOST = ''
# ...ezen a porton
LISTEN_PORT = 8000
# Service configuration above
A LISTEN_HOST paraméter lehet üres sztring, ebben az esetben a szerver minden elérhető
címen fogadni fogja a kérelmeket. Ehelyett beírhatunk egy konkrét IP címet (például
127.0.0.1) vagy egy hostnevet is.
|
|
|
|
|
|
|
|
Az események naplózásához a Python előre beépített naplózó rendszerét fogjuk használni.
Erről bővebben itt lehet olvasni:
http://docs.python.org/lib/module-logging.html.
import sys
import os
# Ez itt a Python beépített naplózó modulja
import logging
# Naplózást végző "kezelő" osztály.
from logging.handlers import RotatingFileHandler
A naplózáshoz több dolog szükséges:
Egy "logger" objektum, ami az összes naplózási műveletet összegyűjti. Ezt mindig a loggig.getLogger
függvény hívásával példányosítjuk. Ennek a függvények a paramétere egy név. Ez a név az események
egy csoportját nevezi el. Az adott csoportban levő esemény bekövetkezésekor ennek az objektumnak
a metódusait fogjuk meghívni, így a naplóban látható lesz az esemény csoportja is. A logger objektumnak
van egy "szintje" is. Nyomkövetés közben ezt magasra állítjuk (debug mód, minden naplózásra kerül).
Később, ha már a szerver stabil, akkor lecsökkentjük; ezzel csökkentve a napló állományok méretét.
Szükség van egy kezelő osztályra is, amelyet a logger objektumhoz rendelünk. A kezelő oszályokból
sokfélét már előre beépítve tartalmaz a Python. Például:
- StreamHandler - állományba ír
- RotatingFileHandler - állományba ír, de egy bizonyos méret után új állományt kezd, az állományokat rotálja
- TimedRotatingFileHandler - állományba ír, de egy bizonyos idő után új állományt kezd, az állományokat rotálja
- SocketHandler - TCP/IP socket-be ír
- DatagramHandler - UDP/IP socket-be ír
- SMTPHandler - e-mail üzenetben küldi el az eseményt
- SysLogHandler - (csak UNIX) syslog-ba írja az eseményt
- HTTPHandler - HTTP GET vagy POST kérelemben küldi el az eseményt egy távoli szervernek
Ezen kívül írhatunk saját handlert is, illetve egyszerre több handlert is használhatunk.
Mi most mind a kettőt kipróbáljuk.
Végül szükségünk van egy formatter objektumra is, ami az üzenetek formázását határozza meg.
A formatter objektumok az eseménnyel kapcsolatos rendelkezésre álló információk alapján
egy sztring-ek állítanak elő, ami naplózásra kerül. Mi most a Python beépített formatáló
osztályát használtuk föl
Végül készítünk egy "getLogger" nevű függvényt, ami létrehoz egy logger
objektumot, hozzárendeli a két kezelőt, és a formatálót. Az általunk írt "getLogger"
fügvény meghívásával egy olyan objektumhoz juthatunk, ami már be van állítva; a szerver
egyes részeiben ezt fogjuk használni egy naplózási csatorna létrehozására.
import sys
import os
import logging
from logging.handlers import RotatingFileHandler
import Config
# Ha a naplóállományok könyvtára még nem létezik
# akkor létrehozzuk.
logdir = Config.LOGDIR
if not os.path.isdir(logdir):
os.mkdir(logdir)
# A saját kezelő osztályunk a standard output-ra ír
class StdOutHandler(logging.Handler):
def handle(self,record):
print self.format(record)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s %(message)s')
handler_1 = RotatingFileHandler(
logdir + os.sep + 'Service.log',
maxBytes = 2*1024*1024,
backupCount = 10
)
handler_2= StdOutHandler()
handler_1.setFormatter(formatter)
handler_2.setFormatter(formatter)
# Ez állítja be a naplózás szintjét
loglevel = logging.DEBUG
# Configure above
def getLogger(name,level=None):
if level is None:
level = loglevel
logger = logging.getLogger('.'+name)
logger.addHandler(handler_1)
logger.addHandler(handler_2)
logger.setLevel(level)
return logger
|
|
|
|
|
|
|
|
Ez az állomány egy főprogram lesz, amit közvetlenül parancssorból el tudunk indítani.
Mindössze annyit csinál, hogy létrehoz egy új szálat; amiben a szolgáltatást elindítja.
A fő szálban egy végtelen ciklus fut. A ciklusban meghívjuk a time modul sleep függvényét,
ami garantálja, hogy a végtelen ciklus nem foglal processzoridőt. (A sleep nem egyszerűen
várakozik, hanem a visszaadja a vezérlést az operációs rendszer ütemezőjének, így az
más processzek számára elérhetővé válik). Az egésznek az a célja, hogy a programot meg
lehessen szakítani. Amikor a felhasználó meg akarja szakítani a programot a Ctrl+C billentyűzet
kombinációval, akkor a főprogramban kiváltódik egy KeyboardInterrupt kivétel. Ez megszakítja
a végtelen ciklust (az except elkapja a kivételt, a break kilép a ciklusból). Ekkor szabályosan
leállítjuk a szolgáltatást:
- Beállítjuk a 'stopped' Event-et
- Megvárjuk, hogy a processor objektum által működtetett szolgáltatás szabályosan leálljon, és
csak ezután naplózzuk ki a leállás tényét.
import thread,threading,time
from Config import *
from Processor import *
from ServiceLog import *
# Az alkalmazás naplózó objektuma
logger = getLogger('Application')
logger.info("Staring as application. Please press CTRL+C to stop service.")
# Új Event létrehozása
stopped = threading.Event()
# Event törlése
stopped.clear()
# A szolgáltatás létrehozása
processor = Processor(stopped)
# A szolgáltatás indítása új szálban
thread.start_new_thread(processor.Process,())
# Várakozunk egy ciklusban...
while 1:
try:
time.sleep(1)
except:
# Ha kivétel történt, akkor leállítjuk a szolgáltatást.
break
logger.info("Stopping application " + SERVICE_NAME)
# Megkérjük a szolgáltatást, hogy álljon le
stopped.set()
# Megvárjuk, hogy szabályosan leálljon
while not processor.stopped.isSet():
logger.debug("Waiting for the processor to finish...")
time.sleep(1)
logger.info("Application stopped.")
|
|
|
|
|
|
|
|
Ez a modul egy win32 szolgáltatás lesz. A win32 szolgáltatások egyik előnye, hogy akkor is futnak,
amikor a felhasználó nincsen bejelentkezve a számítógépre, és akár a számítógép indításakor automatikusan
el is indulnak. Python-ban a win32 szolgáltatások készítéséhez egy olyan osztályt kell írnunk, ami a
win32serviceutil.ServiceFramework osztályból származik. Ez az osztály több esetben is példányosítva
lesz:
- A szolgáltatás telepítésekor
- A szolgáltatás eltávolításakor
- A szolgáltatás futtatásakor
Minden esetben a megfelelő nevű metódusok fognak meghívódni. A metódusok neveit nem mi határozzuk meg,
ezt a win32serviceutil.ServiceFramework ősosztály határozza meg.
- SvcDoRun - Akkor hívódik meg, amikor a szolgáltatás el akarjuk indítani
- SvcStop - Akkor hívódik meg, amikor a szolgáltatás le akarjuk állítani
Ezen kívül más standard metódusok is léteznek (például a szolgáltatás felfüggesztésére), ezekről
bővebben a "Python Programming On Win32" O'Reilly könyvben lehet olvasni.
(Normális internetes dokumentációt eddig nem találtam róla, ha valaki tud ilyet akkor
jelezze.)
Windows-os szolgáltatásoknál az a szokás, hogy a tényleges szolgáltatást végző kódot nem az alkalmazás
fő szálából (SvcDoRun metódusból) hívjuk meg, hanem egy külön szálat készítünk neki. A fő szál arra van
fenntartva, hogy a külső eseményekre
(pl. a leállítási kérelemre) reagáljon. Ha a fő szálban futtatnánk a szolgáltatás kódját, akkor a
leállítási/újraindítási/felfüggesztési kérelmek feldolgozása soha nem történne meg, ezért soha nem tudnánk leállítani
a szolgáltatást.
A felhasznált kód tulajdonképpen nagyon egyszerű, csak arra kell vigyázni, hogy a Windows rendszer felé
folyamatosan jelezni kell a szolgáltatás állapotát. Erre való a ReportServiceStatus függvény. Ha ezt
nem tesszük meg, akkor a Windows szolgáltatások ablakban (Vezérlőpult/Felügyeleti eszközök/Szolgáltatások)
nem fog helyesen látszódni a státusz (fut/leáll/leállt), illetve adott esetben el sem indul a szolgáltatás.
Ha nem jelzünk vissza időben, akkor a Windows rendszer azt fogja gondolni, hogy a szolgáltatás lefagyott,
ezért durván megszakítja a processzt.
Még egy további részt építettünk a kódba: a Windows eseménykezelőbe is elküldünk két üzenetet
(a szolgáltatás indulásáról és leállásáról). Ezzel párhuzamosan természetesen a saját naplózó rendszerünk
is működni fog.
from Config import *
from Processor import *
from ServiceLog import *
import win32serviceutil, win32service
import pywintypes, win32con, winerror
from win32event import *
from win32file import *
from win32pipe import *
from win32api import *
from ntsecuritycon import *
import traceback
import thread,threading
class Service(win32serviceutil.ServiceFramework):
_svc_name_ = SERVICE_NAME
_svc_display_name_ = SERVICE_DISPLAY
def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.stopped = threading.Event()
self.stopped.clear()
self.logger = getLogger(SERVICE_NAME)
def SvcStop(self):
self.logger.info("Got SvcStop, trying to stop service...")
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.stopped.set()
def SvcDoRun(self):
try:
import servicemanager
# Bejegyezzük az eseménynaplóba, hogy a szolgáltatás elindult.
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STARTED,
(self._svc_name_, '')
)
self.logger.info("Started.")
# Itt hozzuk létre az új szálat, ami a tényleges
# szolgáltatást végzi.
self.logger.debug("Creating processor instance")
processor = Processor(self.stopped)
self.logger.debug("Starting processor thread")
thread.start_new_thread(processor.Process,())
# Addig várakozunk, amíg a processor leállítási kérelmet nem kap
self.logger.debug("Waiting for the processor thread to finish")
self.stopped.wait()
self.logger.debug("Stopping")
time.sleep(1)
# Addig várunk, amíg tényleg le nem áll, de közben
# már jelezzük a Windows felé hogy a szolgáltatás
# épp leállítás alatt van
while not processor.stopped.isSet():
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, 5000)
time.sleep(5)
# Végül bejegyezzük az eseménynaplóba is
# a leállás tényét
servicemanager.LogMsg(
servicemanager.EVENTLOG_INFORMATION_TYPE,
servicemanager.PYS_SERVICE_STOPPED,
(self._svc_name_, "")
)
self.logger.info("Stopped")
except:
# Kivétel esetén a kivétel információit is
# külön ki kell írni a naplóba, hogy lehessen
# látni, mitől állt le.
self.logger.error('',exc_info = sys.exc_info())
# Ha parancssorból indítjuk, akkor a parancssori
# paraméterket egy előre megírt metódus feldolgozza.
if __name__=='__main__':
win32serviceutil.HandleCommandLine(Service)
|
|
|
|
|
|
|
|
Ez a szolgáltatás fő kódja. Több különböző osztályt definiálunk, amelyekkel egy egyszerű HTTP szervert hozunk
létre.
MyHTTPServer - ez egy szerver osztály, ami a ThreadingTCPServer-ből származik. A Python-ban
több előre beépített szerver osztályt találunk. A SocketServer modulban a TCPServer illetve az UDPServer
osztályok vannak definiálva, belőlük teljesen egyéni protokolt használó szervereket tudunk készíteni.
Ugyancsak itt találhatók a ThreadingMixIn illetve a ForkingMixIn osztályok, amelyek a kiszolgálás módját
határozzák meg. Hálózatos szervereknél egy szerverhez egy időben egyszerre több kliens is intézhet kérést.
Ez többféleképp lehet kezelni. A többszálú (multi threaded) szervereknél minden kéréshez egy különálló szálat
használunk föl, a kéréseket párhuzamosan szolgáljuk ki. Egy másik lehetőség, hogy minden kérelemhez új
processzt indítunk. Ezt fork-olásnak hívják (forking). A harmadik lehetőség, hogy egyszerre
csak egy klienst szolgálunk ki, a többiek addig várakoznak. (Van még egy technika, ahol egyszerre csak
1 klienst szolgálunk ki, de az épp kiszolgált kliensek között gyorsan váltogatunk - ezt a lehetőséget
itt most nem tárgyaljuk.) A fork-olás nem minden operációs renszeren támogatott, ezért mi most a többszálú
modellt választottuk. A két tulajdonság ötvözete (TCPServer és ThreadingMixIn) a ThreadingTCPServer osztály.
Ez valójában semmi több, mint egy üres osztály aminek két őse van (TCPServer és ThreadingMixIn). A TCPServer és az
UDPServer is egy közös ősből, a SocketServer-ből származik. Ha el akarsz mélyedni a Python alapú szerver
programozásban, akkor érdemes ezt áttanulmányozni.
MyHTTPRequestHandler - ez egy kezelő osztály. Minden kapcsolódó klienshez egy ilyen objektum lesz létrehozva.
A Python előre beépítve tartalmazza a SimpleHTTPRequestHandler osztályt, ami egy HTTP 1.1 szervert implementál.
Egy megadott könyvtárt képes publikálni, a benne levő összes állománnyal együtt. Mi itt csak annyit csináltunk,
hogy a naplózást végző metódust felülírtuk egy olyan verzióra, ami a saját naplózó rendszerünkbe írja az eseményeket.
Processor - Ez a szolgáltatást végző osztály. A Processor osztály konstruktorának paramétere egy Event
objektum. Ez az objektum jelzi azt, hogy a szolgáltatást le kell állítani. Ha valamely más szál ezt az objektumot
beállítja (Event.set), akkor a Processor értesül róla, hogy a leállítást meg kell kezdeni. A sikeres leállítás
után a Processor példány beállítja a stopped nevű Event-jét, ezzel visszaigazolva a sikeres leállást.
A Processor oszály Process nevű metódusa az, ami egy végtelen ciklusban kiszolgálja a bejövő kérelmeket.
Nem használhatjuk azonban a szerver (MyHTTPServer példány) serve_forever metódusát. Igaz ugyan, hogy ez már
tartalmazza végtelen ciklust, azonban nekünk arra
is kell figyelnünk, hogy érkezett-e leállítási kérelem. Egy darab kérelem kiszolgálását a handle_request
metódus segítségével végezhetjük el. Azonban ezzel is hasonló probléma van: ha nincs kapcsolódó kliens, akkor
a handle_request metódus blokkolja a futást (azaz addig vár, amíg be nem érkezik egy kérelem). Ezt
nem engedhetjük meg, mivel a blokkolás sokáig tarthat, és közben érkezhet egy leállítási kérelem. A megoldást
a select modul select függvénye kínálja. (Lásd: select modul).
A select függvény paraméterei között szerepel egy file lista. A select függvény keres egy olyan file-t, ami
olvasásra/írásra elérhetővé válik. Ha ilyet nem talál, akkor várakozik, de csak maximum annyi ideig, amennyit
megengedünk. A trükk ott van, hogy egy szerver socket is file-nak számít, és itt az "olvasás" művelet jelentése
az, hogy érkezett-e be kérelem. A mi esetünkben időkorlátnak 1 másodpercet írtunk be. Ha 1 másodpercen belül
nem érkezik be kérelem, akkor a select függvény egy üres listát ad vissza. Ha érkezik be kérelem, akkor a
visszaadja a file-t (szinte azonnal), ezután a handle_request metódus biztosan nem fog blokkolni. Így biztosítjuk
azt, hogy a leállítási kérelmet 1 másodpercen belül feldolgozzuk.
import time
import traceback
import thread,threading
import os,stat
import select
from Config import *
from ServiceLog import *
from SimpleHTTPServer import SimpleHTTPRequestHandler
from SocketServer import ThreadingTCPServer
class MyHTTPServer(ThreadingTCPServer):
def createlogger(self):
self.logger = getLogger("MyHTTPServer")
class MyHTTPRequestHandler(SimpleHTTPRequestHandler):
def log_message(self,format,*args):
self.server.logger.info(format % args)
class Processor(object):
"""Implement your service code here."""
def __init__(self,stop_requested):
"""Do not change this constructor.
The constructor creates these attributes:
stop_requested
- this is an Event. You should
periodically check it and stop your
service when set.
stopped
- after you stopped your service,
you must set this Event
logger
- this is a logger object. You MUST
log all events to loggers created
by the L{getLogger} function.
"""
self.stopped = threading.Event()
self.stopped.clear()
self.stop_requested = stop_requested
self.logger = getLogger("Processor")
# Custom initialization
self.startup()
def startup(self):
"""Place your own initialization code here"""
# Létrehozunk egy MyHTTPServer példányt
self.server = MyHTTPServer((Config.LISTEN_HOST,Config.LISTEN_PORT),MyHTTPRequestHandler)
# Létrehozzuk a naplózó objektumot
self.server.createlogger()
# Az aktuális könyvtárat átállítjuk (a szerver az aktuális
# könyvtár állományait publikálja)
os.chdir(Config.DOCUMENTROOT)
def shutdown(self):
"""Place your cleanup code here."""
pass
def Process(self):
"""This is the main loop that does the service.
Place your code here. Do not forget to periodically check
the stop_requested event and stop your service when requested."""
srvfd = self.server.fileno() # Szerver socket file
while not self.stop_requested.isSet(): # Amíg nincs leállítási kérelem...
# Maximum 1 másodpercet várunk,
# hogy kapcsolódjon valaki
ready = select.select([srvfd], [], [], 1)
if srvfd in ready[0]: # Ha valaki már kapcsolódni kíván...
self.server.handle_request() # ...akkor kiszolgáljuk őt egy új szálban
else:
pass # egyébként nem csinálunk semmit
self.shutdown() # A szerver leállítása itt történik
self.stopped.set() # Visszaigazoljuk a leállást
self.logger.info("Server stopped")
Ezzel befejeztük az alkalmazás kódolását. A szerver forráskódja
letölthető innen.
|
|
|
|
|
|
|
|
A teszteléshez hozzuk létre a DocumentRoot könyvtárat, és helyezzünk el benne néhány állományt illetve alkönyvtárat.
Ezután indítsunk el egy parancssort (Windows: cmd.exe Unix: az alapértelmezett shell) a project
könyvtárában, és adjuk ki a következő parancsot:
Application.py
Ezután indítsunk el egy böngészőt, és próbáljuk letölteni a webszerver gyökerét.
Az oldal letöltése után a szerver naplóban megjelennek az új kérelmek napló rekordjai.
A napló rekordok megjelennek a Log könyvtárban is. A tesztelés végén állítsuk le a szerver, a
Ctrl+C billentyűzet kombináció megnyomásával.
Ne felejtsük el, hogy ez egy több platformon működő program. A konfigurációs paraméterek
beállítása után további változtatás nélkül fut UNIX rendszereken is. Ha lehet, akkor mindig
törekedjünk arra, hogy az operációs rendszertől független szoftvert készítsünk.
Tesztelés FreeBSD 4.8 rendszeren:
|
|
|
|
|
|
Windows szolgáltatás installálása |
|
|
|
A webszerver Windows szolgáltatásként a Service.py program segítségével tudjuk installálni.
A lehetséges parancsori paraméterek megtekintéséhez adjuk ki a Service.py parancsot paraméter nélkül.
Mi azt szeretnék, hogy a szolgáltatás a Windows indításával egyidőben automatikusan elinduljon, ezért
a megfelelő paraméterekkel telepítjük, majd elindítjuk:
A Windows szolgáltatáskezelő ablakában is megjelenik:
Újra megpróbálhatjuk elérni böngészővel a szervert. A most telepített szolgáltatás a számítógép indítása
után automaikusan elindul még akkor is, ha senki nincs bejelentkezve a számítógépre.
|
|
|
|
|
|
A példa felhasználása: önfrissítő programok |
|
|
|
Ha egy nagyobb céghez kliens-szerver vagy többrétegű alkalmazást kell telepíteni,
akkor gyakran problémát jelent a kliensprogramok frissítése. Esetenként több tíz
vagy száz számítógépen kell egyidejűleg lecserélni a kliensprogramot, amit kézzel
egyenként elvégezni elég fáradságos munka. Ésszerű dolog ilyenkor olyan szoftvert
írni, ami egy központi helyről letölti a legfrissebb verziót, és a letöltés után
elindítja azt. Ez persze csak akkor hatékony, ha a változásokat tölti le.
A frissítéseket tároló központi gépen ebből a célból üzemeltethetünk egy webszervert,
amin a frissítések találhatók. A kliens oldal a webszerverről tölti le az új verziókat,
automatikusan. Egy ilyen szerver egyszerre több szoftver frissítését is képes ellátni.
A következő módon szervezzük a könyvtárakat:
- Minden szoftvernek lesz egy kijelölt helye, ahol a frissítéseket tároljuk.
Például, a "Cerebellum" nevű szoftver
frissítései a "/Cerebellum" könyvtárban kapnak helyet.
- A kijelölt könyvtáron belül minden verzióhoz tartozik egy külön könyvtár.
A könyvtár neve lehet a verziószám, vagy a dátum. Az a lényeg, hogy névsorrendbe
rendezve a legutolsó könyvtár tartalmazza a legfrissebb fájlokat.
- A verziót tartalmazó könyvtárokon belül egy tetszőleges könyvtár- és filestruktúra
elhelyezhető; ezen a könyvtárak és állományok letöltése jelenti magát a frissítést
Ezek után már csak egy olyan programra van szükség, ami:
- Letölti a verziók listáját, és megnézi hogy van-e új verzió
- Ha van új verzió, akkor letölti az állományokat és eltárolja az utolsó letöltött verzió nevét
- Végül elindítja az (előre megadható) programot
A fenti funkciókat megvalósító Windows-os program ingyenesen letölthető innen.
Letöltés után csomagoljuk ki az AutoUpdate.exe-t és az AutoUpdate.ini-t. Az ini állományban állítsuk be a szerver
címét, az adott alkalmazás verzióit tartalmazó könyvtár nevét, és az [AfterUpdate] szekcióban azt a parancsot,
amit a frissítés után futtani kell.
|
|
|
|
|
|
|
|
A frissítést végző kliens oldal csak Windows alatt működik. Gyakorlásképp írd meg Python-ban a kliens oldalt,
az urllib modult felhasználva.
Próbálj meg írni egy olyan chat szervert, ami a ThreadingTCPServer-ből származik, és saját protokolt használ.
Készíts hozzá egy olyan kezelőt, ami a SocketServer.BaseRequestHandler-ből származik, és van egy üzenetsora.
A Queue modul segítségével nagyon
egyszerűen lehet üzenetsorokat készíteni. Kliensként próbáld meg használni a telnet programot. Egy adott kliens
által elküldött szöveget továbbítsd az összes többinek.
|
|
|
|
|
Publikálva: 2005. június 24.
Szerző: Nagy László Zsolt
E-mail a szerzőnek
|