Többszálú szerver szolgáltatás Python nyelven
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é.

Előkészületek
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.

PATH beállítása

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.

A feladat
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.

Modulok
A szervert több, jól elkülöníthető modulra fogjuk szétbontani:
  1. Config.py - A szerver konfigurációja. Nincs benne számottevő kód, csak konstansok.
  2. ServiceLog.py - Naplózó eszköz. Egységesített módon teszi lehetővé a szerverrel kapcsolatot események naplózását.
  3. Application.py - Egy wrapper program ami a szervert alkalmazásként vagy UNIX szerverként futtatja.
  4. Service.py - Egy wrapper program that ami a szervert win32 szerverként futtatja.
  5. 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.

Config.py
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.

ServiceLog.py
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

Application.py
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:
  1. Beállítjuk a 'stopped' Event-et
  2. 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.")

Service.py
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)

Processor.py
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.

Tesztelés
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.

DocumentRoot feltöltése

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

Alkalmazás elindítva

Ezután indítsunk el egy böngészőt, és próbáljuk letölteni a webszerver gyökerét.

Tesztelés böngészővel

Az oldal letöltése után a szerver naplóban megjelennek az új kérelmek napló rekordjai.

Hozzáférések naplózva

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.

Alkalmazás leállítva

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:

Alkalmazás leállítva (FreeBSD)


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.

Service.py paraméterek

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:

Szolgáltatás installálva

A Windows szolgáltatáskezelő ablakában is megjelenik:

Windows szolgáltatáskezelő

Ú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


Könyvtár verziók

Ezek után már csak egy olyan programra van szükség, ami:
  1. Letölti a verziók listáját, és megnézi hogy van-e új verzió
  2. Ha van új verzió, akkor letölti az állományokat és eltárolja az utolsó letöltött verzió nevét
  3. 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.

Hogyan tovább?
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
 











Hírek


Szoftverek minõségbiztosítása
2009-01-13
Ajánlás közigazgatási szoftver rendszerek minõségbiztosításához.
Bővebben...

Poszterek!
2008-05-20
Partner cégünk megnyitotta online poszter áruházát. Több mint 1200 különbözõ poszter közül válogathat!
Bővebben...

SzlaMan frissítés
2006-09-11
Bõvült a SzlaMan program. Logikai számlatörlés, könyvelés jelzése, felhasználói csoportok kezelése.
Bővebben...

Cikkek



Honlap tanulmány
2008-05-20
Tekintse meg honlap tanulmányunkat a Java technológia magas szintû használatáról.
Bővebben...