Mechanismus klient/server
V sedmém příkladu se seznámíme s podporou, jakou Objective C nabízí pro mechanismus klient/server. Vytvoříme server, který bude používat služeb třídy NXStringTable tak, jak jsme se s ní seznámili v prvním příkladu. I klient bude implementován podobně, jako v prvním příkladu -- bude se jednat o jednoduchý program, který umožní vkládat dvojice textových řetězců <klíč, hodnota> do jednoduché databáze a vyhledávat vložené hodnoty podle klíče.
// Objective C -- příklad 7/1
//
// architektura klient/server v prostředí Objective C: služby serveru
// (vyžaduje podporu OS, příklad z NeXTstepu)
// pro specifikaci sady služeb je ideálně vhodný mechanismus protokolů
@protocol StringServer
// protokol obsahuje seznam zpráv -- pro ukázku použijeme pouze dvě
| -(int)addKeyAndValue:(char*)str; | // vrací číslo položky |
-(const char*)getStringForKey:(char*)key;
@end
// end of file
1. Protokol -- sample7.h
Pro specifikaci rozhraní mezi dvěma objekty je ideální mechanismus protokolů -- ty nám umožňují popsat skupinu zpráv, aniž bychom museli zároveň fixovat třídu objektu, který je bude zpracovávat (jak by tomu bylo kdybychom chtěli použít na místě protokolu interface).
Pro ukázku definujeme velmi jednoduchý protokol, který bude obsahovat pouze dvě zprávy: 'addKeyAndValue:' zapíše do databáze dvojici hodnot (rozdělení klíče a hodnoty podle čárky bude záležitostí serveru, proto má zpráva pouze jeden textový parametr), a 'getStringForKey:', která vrátí hodnotu k zadanému klíči.
// Objective C -- příklad 7/2
//
// architektura klient/server v prostředí Objective C: server
// (vyžaduje podporu OS, příklad z NeXTstepu)
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <sys/file.h>
#import <objc/NXStringTable.h>
#import <remote/NXConnection.h>
#import "sample7.h"
// jméno protokolu v lomených závorkách informuje překladač, že nová třída
// musí implementovat všechny zprávy, zadané v protokolu
@interface Server:Object <StringServer>
{
id seznam;
}
// nebudeme implementovat žádné nové metody kromě těch popsaných
// již v protokolu
@end
// Porovnejte implementaci jednotlivých zpráv s prvním příkladem!
| #define STOREFILE | "sample7.data" |
@implementation Server
// musíme reimplementovat zprávy init a free pro inicializaci a uvlnění seznamu
-init
{
seznam=[NXStringTable new];
if (!access(STOREFILE,R_OK)) [seznam readFromFile:STOREFILE];
return self;
}
-free
{
[seznam writeToFile:STOREFILE];
[seznam free];
return [super free];
}
| -(int)addKeyAndValue:(char*)str; | // vrací číslo položky |
| { | |
[seznam insertKey:strdup(str) value:strdup(cp)];
return [seznam count];
}
-(const char*)getStringForKey:(char*)key
{
return [seznam valueForStringKey:key];
}
@end
void main()
{
| id server; | // objekt serveru |
server=[Server new];
// sdělíme operačnímu systému, že server je k dispozici a že se k němu
// lze připojit pod jménem "Sample7 server". To samozřejmě musí být
// služba operačního systému; NeXTstep ji nabízí prostřednictvím
// standardní třídy NXConnection. Pak server aktivujeme.
[[NXConnection registerRoot:server withName:"Sample7 server"] runInNewThread];
printf("Server aktivován ... klávesa 'q' jej ukončí\n");
while (getchar()!='q');
printf("Hotovo\n");
[server free];
}
// end of file
2. Implementace serveru -- sample7.m
Implementace serveru se téměř neliší od odpovídajících rutin v prvním příkladu; podíváme se proto podrobněji až na funkci 'main' na samém konci souboru.
V ní nejprve vytvoříme objekt, který bude sloužit jako server. Pak jej registrujeme v operačním systému pod jménem "Sample7 server" pomocí standardní třídy NXConnection. Zaregistrovaný server pak aktivujeme pomocí zprávy 'runInNewThread'; díky tomu program dál poběží a server bude pracovat nezávisle na něm v druhém threadu.
// Objective C -- příklad 7/3
//
// architektura klient/server v prostředí Objective C: klient
// (vyžaduje podporu OS, příklad z NeXTstepu)
#import <stdio.h>
#import <remote/NXConnection.h>
#import "sample7.h"
void main()
{
id server;
| char buffer[512]; | // vstupní textový buffer |
server=[NXConnection connectToName:"Sample7 server"];
// jednoduchá nápověda (uživatelské rozhraní programu by samozřejmě
// zasluhovalo vylepšení):
printf("klíč,hodnota / ?klíč / * / !\n");
for (;gets(buffer);) switch (*buffer) {
// vykřičník program ukončí
case '!': goto Konec;
// otazník vyhledá hodnotu k zadanému klíči
case '?': {
char const *cp=[server getStringForKey:buffer+1];
if (cp==NULL) printf("Není v seznamu!\n");
else printf("> %s\n",cp);
}
break;
// jinak vkládáme novou dvojici
default:
printf("Položka %s přidána na pozici %d\n",
buffer,[server addKeyAndValue:buffer]);
break;
}
Konec:
printf("hotovo\n");
}
// end of file
3. Implementace klienta -- sample7c.m
Na implementaci klienta je zajímavý pouze první příkaz -- vyžádáme si spojení se serverem jménem "Sample7 server" prostřednictvím třídy NXConnection. Pak již se serverem pracujeme stejně jako s kterýmkoli jiným objektem.