Základem komunikace s okolím prostřednictvím síťového rozhraní jsou proudy a sokety. O proudech jsme hovořili v předminulém dílu, dnes si představíme sokety, které implementuje třída Socket. S ní spolupracuje specializovaná proudová třída SocketStream.
Klient
Správa klienta je jednoduchá. Nejdříve se soket pro TCP připojení vytvoří standardním konstruktorem new (pro UDP připojení konstruktorem newUDP). Dále se provede připojení k serveru, přičemž se specifikuje adresa a port. Poté se se soketem asociuje proud a následně již můžeme přistoupit ke vzájemné komunikaci prostřednictvím běžných proudových operací.
| socket stream | socket := Socket new. socket connectTo: (NetNameResolver localHostAddress) port: 7654. stream := SocketStream on: socket. stream nextPutAll: 'Hello world'. stream close.
Při uzavření proudu dojde současně i k uzavření příslušného soketu.
Adresace
Práci s IP adresami a jmény počítačů nám usnadňuje třída NetNameResolver. Umožňuje zjišťovat informace jak o hostitelském systému, tak o vzdálených počítačích. IP adresy jsou čtyřprvkové instance třídy ByteArray. Vzájemné převody z/do různých formátů adres nejsou problém.
NetNameResolver addressForName: 'root.cz' -> a ByteArray(81 31 5 5) NetNameResolver addressForName: '81.31.5.5' -> a ByteArray(81 31 5 5) NetNameResolver localHostAddress -> a ByteArray(192 168 0 1) NetNameResolver localAddressString -> '192.168.0.1' NetNameResolver localHostName -> 'wallace.comtalk.net' NetNameResolver nameForAddress: #(81 31 5 5) asByteArray timeout: 60 -> 'centaurus.4web.cz'
Časové limity se pro většinu těchto metod dají určit explicitně. Standardně je použita jedna minuta.
Server
Jak se používá připojení na straně serveru, si ukážeme na triviálním příkladě webového serveru. Nejdříve si pro něj vytvoříme třídu. Je extrémně zjednodušený, takže nebudeme potřebovat ani žádné instanční proměnné.
Object subclass: #WebServer instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Networking'
V metatřídě si nadefinujeme konstruktor start (server se bude pouštět příkazem WebServer start)
start | queue connection | queue := ConnectionQueue portNumber: 80 queueLength: 50. [ [ connection := queue getConnectionOrNil. connection ifNotNil: [ self new serve: connection ]. Processor yield ] repeat ] fork.
Připojení serverového soketu probíhá tak, že se nechá naslouchat na příslušném portu (zpráva listenOn:). Jestliže se k serveru připojí klient, musíme jej obsloužit (nejlépe v separátním vlákně) a naslouchat dále. To nám usnadňuje třída ConnectionQueue, která v procesu s poměrně vysokou prioritou sbírá na určeném portu připojení a řadí je do fronty, odkud si je můžeme vybírat a postupně obsluhovat. V našem případě tak činíme v nekonečné smyčce běžící v separátním vlákně. Při vytvořeníConnectionQueue se specifikuje naslouchaný port a maximální počet připojení čekajících ve frontě. Příkaz Processor yield jsme použili pro lepší rozložení zátěže.
Obsluha klienta je prostá. Každého budeme obsluhovat ve zvláštním vlákně. Nad dodaným soketem si vytvoříme proud a vybereme si z něm HTTP hlavičku (končí prázdným řádkem). Podle obsahu hlavičky klientovi odešleme odpověď, což bývá většinou HTML stránka.
serve: aConnection
[
| stream head |
[
stream := SocketStream on: aConnection.
head := stream upToAll: (String crlf, String crlf).
stream nextPutAll: (self response: head).
] ensure: [ stream ifNotNil: [ stream close ] ].
] fork.
My mu vrátíme řetězec obsahující aktuální čas serveru.
response: head ^ 'Powered by Smalltalk', String crlf, 'Current time is: ', Time now asString.
Aby to dělalo něco alespoň trochu užitečného, zpřístupníme statické HTML stránky. Přijmeme od klienta HTTP hlavičku, z níž vykousneme druhé slovo, které představuje URL pro lokální zdroj. Tedy pokud uživatel zadal do svého prohlížeče např. adresu /clanek/2208, bude tato URL obsahovat řetězec ‚/clanek/2208‘. První slovo je druh požadavku (GET, POST atd.). My ještě odřízneme parametry stránky, které se uvádějí za oddělující otazník. Pokud uživatel nezadal žádný lokální zdroj, napsal tedy pouze http://www.root.cz, obdržíme v hlavičce ve specifikaci lokálního zdroje pouze znak'/'. V tom případě použijeme standardně stránku index.html. Z URL si vytvoříme fyzickou cestu ke zdroji (obsah stránek máme v adresáři /inetpub), prověříme existenci takového souboru a jeho obsah vrátíme uživateli.
response: head | url path | url := (head findTokens: ' ') second upTo: $?. (url = '/') ifTrue: [ url := '/index.html' ]. path := '/inetpub', url. (StandardFileStream isAFileNamed: path) ifFalse: [^ 'Resource not found',String crlf]. ^ FileStream readOnlyFileNamed: path .
V tomto okamžiku již klient může prohlížet statické stránky včetně obrázků apod. Náš server je sice poměrně snadno DoSnutelný a bezpečný jako procházka po Traalu bez ručníku na očích, ale jako demonstrační příklad snad vyhovuje.
Přirozeně se jedná o nošení dříví do lesa. Pro Squeak existuje několik webových serverů, z nichž nejvíce vyčnívá projekt Comanche (KomHttpServer), který toho skutečně nenabízí málo a slouží jako základ pro další pokročilé webové platformy.
HTTP klient
Ve Squeaku existuje program jménem Scamper, což je zcela samostatný webový prohlížeč. Nachází se ve velmi raném stádiu vývoje a korektně dokáže zobrazit jen velmi jednoduché stránky. Na surfování rovnou zapomeňte, ale i tak může posloužit přinejmenším jako pokladnice příkladů pro práci HTTP klienta, který si dokáže z Internetu ledasco stáhnout a nějak to přelouskat.
Pro obsluhu vzdálených zdrojů slouží třída HttpUrl. Nejjednoduššeji se konstruuje přímo z řetězce adresy metodou asUrl třídy String.
'/clanek/2208' asUrl
Třída HttpUrl má nejednu zajímavou metodu. Nejčastěji asi využijete metodu retrieveContents, která adresovaný zdroj stáhne a vrátí instanci třídy MIMEDocument. Z ní kromě dodatečných informací o typu dokumentu můžeme získat samozřejmě i její obsah metodou contents, vytvořit si nad ní proud a podobně. Stažení a uložení HTML stránky či libovolného jiného dokumentu do souboru by pak mohlo vypadat například takto:
(FileStream newFileNamed: 'page.html') nextPutAll: ('/clanek/2208' asUrl retrieveContents contents); close.
Třída HttpUrl toho umí samozřejmě i více. Občas se může hodit např. derelativizace odkazu z dané adresy. Je-li např. na stránce /clanek/2208 uveden odkaz na dokument ‚../index.html‘, pak metoda newFromRelativeText vrátí jeho absolutní adresu. Absolutní adresy zůstavají beze změny.
'/clanek/2208' asUrl newFromRelativeText: 'index.html' ->'/clanek/index.html' '/clanek/2208' asUrl newFromRelativeText: '/index.html' ->' /index.html'
HTML a XML
Pokud již stáhneme z Internetu nějaký dokument, často jej potřebujeme zanalyzovat. U HTML stránek nám výrazně pomůže např. třída HtmlTokenizer, která dokáže dokument ze vstupního proudu zpracovat tag po tagu, analyzovat u něj jednotlivé atributy atd. Například jednoduchý prográmek, který ze vstupního proudu s obsahem HTML stránky (proměnná page) získá všechny odkazy a uloží je do výsledné kolekce (proměnná result), odkud mohou být dle libosti dále derelativizovány a staženy, může vypadat například takto:
| result token anchor tokenizer |
result := OrderedCollection new.
tokenizer := HtmlTokenizer on: page.
[ token := tokenizer next. token = nil ] whileFalse: [
token isTag ifTrue: [
(token name = 'a') ifTrue: [
anchor := (token attribs at: #href ifAbsent: []).
anchor ifNotNil: [ result add: anchor].
].
].
].
Pro syntaktickou analýzu XML souborů standardní distribuce Squeaku prostředky nemá, nicméně existuje například analyzátor YAXO stáhnutelný ze SqueakMap pod stejnou licencí, jakou má Squeak. Je neméně jednoduše použitelný a i on rovněž slouží jako základ pro nejeden XML serializer a podobné nadstavby. Osobně jsem jej několikrát v praxi prověřil na XML souborech o velikostech až několik MB a s problémy jsem se nesetkal.
SMTP, POP3
Squeak obsahuje i třídy poskytující služby poštovního klienta. Pravděpodobně nejčastěji upotřebíte možnost zaslat jednoduchý textový e-mail např. s informacemi o stavu vašeho serveru, zaslání obsahu webového formuláře apod. Při odesílání dopisu je samozřejmě nutné specifikovat odchozí SMTP server.
SMTPClient deliverMailFrom: 'odesilatel@server.cz' to: #('prijemce@server.cz') text: 'From: odesilatel@server.cz To: prijemce@server.cz Subject: Predmet Obsah zprávy ' usingServer: 'smtp.etmail.cz'
Krom odesílání zpráv nečiní Squeaku žádné potíže pracovat jako POP3 klient (třída POP3Client). Squeak obsahuje i funkčního grafického poštovního klienta jménem Celeste, který si poradí i s odesíláním a přijímáním příloh, takže pokud tuto funkčnost budete ve svých projektech vyžadovat, můžete jej vesele vykrádat.
FTP
Squeak standardně využívá komunikaci s FTP servery pro stahování svých aktualizací apod. Základní činnosti zapouzdřuje třída FTPClient. Jejích služeb využívá třída s příjemnějším rozhraním jménem ServerDirectory. Obdržení výpisu adresáře na serveru může vypadat např. takto:
(ServerDirectory on: 'comtalk.net') user: 'Anonymous'; password: 'yourName@company.com'; getDirectory
Práce se soubory se pak provádí např. pomocí příkazu getFileNamed:, putFile:named:, getFileList apod.
Databáze
Smalltalk je sám o sobě objektová databáze, nicméně bez zaručené perzistence a transakcí. Ovšem ne náhodou tvoří základ pro profesionální objektové databázové systémy, jako je např. GemStone/S., který především ve spojitosti s VisualWorks nabízí vynikající platformu pro skutečně komplexní náročné informační systémy.
Ovšem i na Squeaku se rodí nejedna objektová databáze (Magma, MinneStore, OmniBase apod.) a některé z nich vypadají velmi slibně. Pokud se rozhodnete svěřit svá data nějaké běžné relační databázi, jistě uvítáte ovladače pro MySql, PostgreSQL, SQLite či ODBC. Ty jsou řešeny buď síťovou komunikací, nebo přes volání funkcí hostitelského systému (FFI).
Nutno podotknout, že i práce s relačními databázemi je ve Smalltalku velmi příjemná, protože díky nástrojům, jako je Inspector, máte na první pohled dokonalý přehled o struktuře a obsahu získaných dat, nehledě na možnost s nimi okamžitě dle libosti manipulovat a experimentovat.
V případech, kdy i relační databáze je příliš velký kanon, máte na výběr nejeden způsob řešení perzistence objektů. Např. extrémně jednoduchý, leč velmi pohodlně použitelný FileDictionary, či XML (de)serializer SIXX.
Bezpečnost
Pro Squeak existuje celá řada kryptografických algoritmů. Za zmínku stojí implementace hešování zpráv pomocí SHA obsažená ve standardní distribuci, která vám jistě usnadní autorizační mechanismy
SecureHashAlgorithm new hashMessage: 'heslo'. ->628022029064920457606303469569901564949872826602
Squeak v současnosti nenabízí nativní podporu pro SSL. V případě potřeby zabezpečeného připojení se doporučuje využívat např. Stunnel.
Některá zabezpečení lze řešit přímo na úrovni virtuálního stroje. Např. lze úplně zakázat přístup k souborům, síti apod. To může být v mnoha případech účinější než nastavení práv hostitelského systému. V extrémních případech vám samozřejmě nic nebrání si zdrojové soubory VM upravit dle libosti tak, aby přesně odpovídaly vašim bezpečnostním potřebám.
Další projekty
Na co Squeak teprve čeká, je kvalitní podpora webových služeb. SOAP již implementován je, WSDL se připravuje. V případě, že vás láká práce s webovými službami ve Smalltalku, budete muset prozatím sáhnout po některé komerční implementaci.
Jak jsme si řekli, nad webovým serverem Comanche je postaveno několik aplikací a frameworků. Nejpoužívanější z nich jsou HttpView2, SeaSide a Swiki.
HttpView2 je jednoduchý intuitivní framework poskytující základní prostředí pro tvorbu dynamických webových stránek. Stránky jsou většinou generovány přímo ze zdrojové metody pomocí zasílání zpráv konstrukčnímu objektu HTML stránky.
Podstatně propracovanější je SeaSide, což je mocné objektové prostředí pro tvorbu webových aplikací podporující sessions, view-states apod.
Swiki pak je již hotová webová aplikace pro uživatelsky editovatelné stránky s podporou vyhledávání, historie, zabezpečení, změn vzhledu apod. Její velkou předností je, že ji může okamžitě používat i člověk, který o Smalltalku v životě neslyšel a nikdy v něm neviděl jediný napsaný řádek kódu.
Webové technologie ve Squeaku jsou velmi zajímavá a progresivní problematika a dalece přesahují rozsah tohoto úvodního seriálu. Spíše by si zasloužily seriál samostatný. To jsem se už vůbec nezmínil o možnostech distribuovaných objektů apod.
Potenciál, který při budování webových aplikací Smalltalk má, si jistě snadno domyslí každý, kdo si uvědomí, jak snadno v něm jde např. ze zdrojové stránky vydolovat kód, vytvořit pro tuto stránku samostatnou třídu, přeložit a přidat do ní metody, vytvářet a rušit její instance, případně ji zase ze systému smazat atd. Podobně je např. v Morphicu řešena práce s tzv. hráči (Players) pro návrh uživatelského rozhraní. O možnostech tvorby dynamických stránek na bázi prototypů ani nemluvě. To je oblast, kde si staticky typované vysokoúrovňové jazyky už ani neškrtnou.