Tekniska Högskolan i Linköping
Institutionen för Datavetenskap (IDA)
Torbjörn Jonsson
2002-11-19
Nätverk och processer
I denna laboration kommer vi att implementera ett enkelt konferenssystem (en ”chat”) i ett
antal delsteg. Till att börja med kommer vi att begränsa oss till två personer som ”pratar”
med varandra och i slutet skall systemet klara av många personer som ”pratar” samtidigt.
Mål
Du ska efter denna laboration kunna:
• använda ”sockets” för enkel kommunikation mellan program på eventuellt olika datorer
• förstå hur man kan få flera processer att köra samtidigt i ett program
• förstå hur man kommunicerar mellan processer
• förstå hur man kan skapa en datatyp som flera processer kan använda utan att data blir
inkonsistent (att processerna skriver över varandras information)
• sätta dig in i ett redan befintligt bibliotek av paket
• förstå vad klient och server är och hur man bygger upp sådana
• förstå att det behövs ett kommunikationsgränssnitt (protokoll) mellan olika program
Tonvikt läggs på:
• hanteringen av ”sockets” samt lösning av problem i klient och server
• förståelse av processer
• att modularisera problemet/programkoden så att man kan vidareutveckla sitt program när nya
problem skall lösas
Uppgift
Du skall skriva två program. Det ena programmet skall vara en klient som hanterar en användare och
ser till att denna kan kommunicera med någon annan användare och det andra programmet skall vara
en server som sköter kommunikationen mellan användarprogrammen.
Serverprogrammet skall då det är helt färdigt klara av att hantera många samtidiga användare som
”chattar” med varandra. I de första deluppgifterna är servern ganska trivial (bland annat att den bara
hanterar två personer) och senare kommer den att utvecklas till att klara mer komplexa saker.
Klientprogrammet skall till att börja med klara av att hantera användaren (kunna läsa in text och visa
den på skärmen) och skicka den text som matas in till servern samt ta emot text från servern (som
någon annan skickat till denna användare) och skriva ut den på skärmen. I detta tidiga skede kan vi
anta att användarna alltid måste skriva text varannan gång. I senare delmoment skall klienten klara av
att hantera scenariot att alla användare kan mata in text när som helst.
Det kan vara bra att se ett meddelande som inte bara texten som skickas utan att även informationen
om vem som skickade meddelandet är en del av meddelandet (om inte annat för utskrifterna).
Ett tips som kan vara bra att få redan i detta tidiga stadium är att det kan vara bra att göra iordning
paket som innehåller rutiner som är bra att ha för både server och klient så att man slipper skriva
samma kod på flera ställen (i detta fall i två olika program). Paket har ju den enorma fördelen att man
kan inkludera det från olika program och rättar man i paketet är det bara att kompilera om de båda
programmet. Mycket skönt alltså.
Sida 23
2002-11-19
Del A
Denna övning är i första hand till för att komma igång med hantering av ”sockets”, d.v.s. att hantera
kommunikationen mellan två program via nätverk. På kurshemsidan finns lite information och några
testprogram som kan vara bra att ha angående detta. Även information om hur man gör för att
kompilera sitt program när man skall inkludera ”socket”-paketet finns på hemsidan.
Serverprogrammet
Till att börja med skall servern klara av att hantera två uppkopplingar via ”sockets” och scenariot är
att de två användarna (klienterna) skickar data varannan gång. Servern (vi kallar den S) skall börja
med att vänta på två uppkopplingar från två klienter. Därefter skall S tala om för den först
uppkopplade klienten (kallar vi för K1) att den får börja kommunicera. S väntar på ett meddelande
från K1 och skickar detta vidare till den andra klienten (kallar vi K2). K2 skickar ett meddelande till
S och S skickar det vidare till K1. Detta upprepas tills det att K1 eller K2 kopplar ner sin förbindelse.
Beskrivningen ovan kan göras på ett mer stilistiskt sätt och detta visar vi i kapitlet ”Protokoll” på
nästa sida.
OBS! Det är väsentligt att S, K1 och K2 inte kraschar om något av programmen bryter kontakten
(kan bero på att programmet avslutas eller att man stänger förbindelsen). Absolut väsentligt är att S
inte kraschar.
När någon av klienterna kopplar ner förbindelsen skall S meddela detta till den kvarvarande klienten.
Ni får själva välja om den kvarvarande klienten också skall kopplas ner. Om någon ny klient kopplar
upp sig kan åtminstone två fall vara möjliga. Dels att den nya klienten får ”chatta” med den
kvarvarande klienten (om denna inte kopplas ner) och dels att den nya klienten får vänta tills det
kommer ytterligare en klient (om det inte finns någon kvarvarande klient). Vi rekommenderar den
första varianten.
Klientprogrammet
Klientprogrammet (vi kallar det K) skall till att börja med vara ganska enkelt. K skall koppla upp sig
mot servern (som kallas S) och vänta på besked om att det är dags att börja skicka meddelanden. Om
programmet skall börja med att skicka ett meddelande skall K först läsa in en text från tangentbordet
skriva ut den på skärmen och skicka den till S. Därefter skall K vänta på ett meddelande från S och
skriva ut detta på skärmen. Inläsning av nytt meddelande skall ske och sen är det bara att fortsätta på
det sättet tills det att användaren vill avsluta programmet.
Just nu tar vi inte hänsyn till hur användaren upplever kommunikationen med programmet utan gör
det på enklaste sätt. Ett exempel på hur det skulle kunna se på skärmen är (vi antar att det är den
andre användaren, som vi kallar ”carma”, som börjar ”prata” och att du har som namn ”torjo”):
carma:
torjo:
carma:
torjo:
Lite text från den andre användaren.
Ett svar på ovanstående.
Ytterligare information.
Nu skriver vi lite mer text som skall skickas.
Den text som är markerad med fetstil i ovanstående behöver givetvis inte skrivas så i ert program (det
kommer mer om detta i del B i laborationen) utan vi har endast markerat den inmatning ”du” gjort på
detta sätt. Det viktiga i dednna del är att komma igång med kommunikationen.
Sida 24
2002-11-19
Protokoll
Ett protokoll är en beskrivning över hur två (eller
flera) enheter kommunicerar. I vårt fall kan vi se
protokollet mellan en server och en klient alternativt
(som vi visar i figuren) mellan servern och de båda
klienterna. Det bör observeras att det program som
motsvarar de båda klienterna (det är bara ett program
i vårt fall) måste klara båda varianterna (både att vara
första klienten och den andra klienten) beroende på
vilken ordning uppkopplingen till servern sker.
Vi antar att klienterna kopplar upp sig mot servern
och att den klient som kommer först kallas klient 1.
Den klient som kommer därefter kallar vi klient 2
(man kan tänka sig en mängd olika scenarion, men vi
låter detta bli er uppgift).
Klient 1
Server
Klient 2
Uppkoppling
”Name?”
”carma”
”No: 1”
Uppkoppling
”Name?”
”torjo”
”No: 2”
”Message?”
”Lite text ...”
”carma”
”Lite text ...”
”Message?”
Observera att själva uppkopplingen egentligen inte är
någon kommunikation utan bara ett upprättande av en
förbindelse. Den kommunikation som sker utförs
m.h.a. strängar (i detta fall) och markeras genom att
de data som skickas står inom ””.
I protokollet ser vi att kommunikationen kommer att
upprepas i ”oändlighet”, men normalt sett finns det
ett slut på kommunikationen. Då en av klienterna
”kopplar ner” sin förbindelse måste den andra
klienten meddelas om detta. I slutet av protokollet ser
vi hur klient 1 kopplar ner sin förbindelse och vad
som inträffar.
”Annan text ...”
”torjo”
”Annan text ...”
”Message?”
Nedkoppling
”carma”
”Loggat ut!”
Del B
I denna del är det meningen att ni skall bekanta er med det extra biblioteket som vi kallar för TJabiblioteket. Detta innehåller en del smått och gott vad det gäller skärm-, tangentbords-, fil- och
listhantering och även andra saker som kan vara trevlig att ha. Det bör observeras att biblioteket inte
är avsett för annat bruk än undervisning. Mer information om biblioteket finns på kurshemsidorna.
I del A brydde vi oss inte om hur interaktionen med användaren såg ut. Detta gör att det kan bli
ganska trist för användaren och det skall vi råda bot på nu. För att användaren skall veta vad den
matar in skall fönstret där klientprogrammet exekveras delas in i två delar, en inmatningsdel och en
meddelandedel. Lämpligt är att den övre halvan av fönstret används till utskrifter av meddelanden
och den nedre halvan till inmatning. Ett exempel på hur det skulle kunna se ut på skärmen är:
carma: Lite text från den andre användaren.
torjo: Ett svar på ovanstående.
carma: Ytterligare information.
------------------------------------------------------------Meddelande: Nu skriver vi lite mer text som skall skickas.
Sida 25
2002-11-19
För att lösa detta krävs att man kan positionera sig inom terminalfönstret och rutiner för detta finns i
TJa-biblioteket. Det man dessutom måste lösa är hur man kan rulla texten i den övre delen i fönstret
utan att påverka den nedre delen. Man kanske måste lagra informationen som syns på skärmen i sitt
program för att kunna lösa detta.
I denna del är det bra om ni försöker komma fram till hur man använder TJa-bibliotekets olika delar
så var inte rädda att prova och se vad som händer när man gör olika saker. Använd gärna lite olika
färger för att pigga upp användaren :-).
Del C
Nu skall vi lägga till lite funktionalitet i klienterna och samtidigt måste servern uppdateras för att
klara dessa nya krav. Vi skall låta de båda användarna skriva meddelanden i sin egen takt och detta
kräver att vi tar bort begränsningen att servern låter klienterna skicka meddelanden varannan gång.
För att lösa detta måste vi införa något som vi kan kalla processer i servern (och även i klienterna).
På kurshemsidan finns exempel på hur man skapar processer i ett program.
Protokoll
Protokollet mellan servern och de båda klienterna blir
lite modifierat när vi nu kan ”prata” samtidigt i båda
riktiningarna. Uppstarten ser likadan ut, men när
själva dialogen kommer igång blir det lite nyheter.
Klient 1
Server
Klient 2
Uppkoppling
”Name?”
”carma”
I protokollet ser vi att dialogen kan komma att utföras
i ”slumpmässig” ordning. Detta leder till att vi inte
heller behöver tala om att det är dags att skriva för
den aktuella klienten. Det är alltid ok att skicka ett
meddelande i detta scenario.
”No: 1”
Uppkoppling
”Name?”
”torjo”
”No: 2”
Observera att vi inte riktigt kan säga vilken ordning
olika delar kommer att utföras. Det skulle kunna vara
så att de två klienterna skickar sina data samtidigt och
att serverna skickar ut respektive meddelande i
princip samtidigt till de båda klienterna. Detta
beroende på att vi infört processhanteringen i både
klient och server. Det enda som är helt säkert är att
servern tar emot ett meddelande och skickar ut namn
och meddelande till den andre klienten.
”Lite text ...”
”carma”
”Lite text ...”
”Annan text ...”
”torjo”
”Annan text ...”
Serverprogrammet
Vi kommer nu att behöva två processer i S (servern).
Den ena processen (P1) skall hantera data från K1
(klient 1) till K2 (klient 2) den andra processen (P2)
skall ta hand om data från K2 till K1.
S skall vänta på att K1 och K2 kopplar upp sig. Efter
detta skall S skapa P1 och P2. När K1 eller K2
kopplas ned skall P1 och P2 avslutas. S skall fortsätta
att vänta på nya uppkopplingar enligt tidigare.
Sida 26
Server
K1
P2
P1
K2
2002-11-19
Klientprogrammet
I klientprogrammet kommer vi att behöva en
process (L) som hanterar inkommande data från S.
L skall läsa data från S och skriva detta på skärmen,
i den övre delen av termialfönstret på samma sätt
som i del B. Huvudprogrammet (HP) skall sköta
hanteringen av inmatning från tangentbord och
utskrift av detta i den nedre delen av fönstret (precis
som tidigare) samt att skicka inmatningen till S.
Klient
Skärm
L
S
HP
En liten otrevlig sak som kan inträffa är att inmatningstexten (från HP) och meddelanden (från L)
kan råka skrivas i fel del av terminlfönstret (antagligen råkar ni inte ut för detta, men det är i princip
möjligt) beroende på att vi nu har två processer som arbetar parallellt. Detta är dock inget vi bryr oss
om i detta delmoment.
Del D
I denna del skall vi ta hand om problemet att det i klienten (K) kan inträffa att användaren matar in
data som skrivs ut i den nedre delen av fönstret samtidigt som det kommer in data från servern (S)
som också skrivs på skärmen. Vad inträffar då? Jo, när vi matar in text skall ju markören befinna sig i
nedre delen av fönstret och om det då samtidigt är så att processen L (som hämtar data från S) vill
skriva på skärmen flyttas ju markören till det övre fönstret och inmatningen visas i fel delfönster. I
denna deluppgift skall vi se hur man kan lösa problemet m.h.a. ytterligare en process.
Klientprogrammet
Vi kommer att behöva ytterligare en process
Klient
(U) som sköter all hantering av utskrift till
skärm. U kommer att behöva ta emot data
från både HP och L. Detta gör att L måste
L
veta om att U finns och detta leder till att U
U
Skärm
HP
måste skapas innan L skapas. Dessutom
måste det finnas möjlighet att kommunicera
mellan de olika processerna som nu körs
parallellt. Detta görs m.h.a. något som kallas ”entry” i processen som skall ta emot data. Att
”anropa” ett ”entry” är ungefär som att anropa en procedur, d.v.s. man kan skicka med data som
parametrar till ett sådant ”entry”. Mer om detta finns på kurshemsidan.
Vår nya process U behöver ha flera olika ”entryn”. Några exempel skulle kunna vara ”New_Line”,
”Put” och ”Put_Line”. Dessa måste ha minst en parameter och det är vilken del av fönstret skall
operationen utföras på. Kanske behöver man ha olika ”entryn” för att kunna skriva ut strängar
respektive tecken. Kanske vill man kunna styra lite vilken färg som skrivs ut på olika ställen m.m.
Den nya processen U kan göras hur stor som helst, men gör det som är lagom för att få programmet
att fungera. Lägg inte ner alltför mycket tid på detaljer.
Egentligen bör man även ha en process (som vi kan kalla W) som hanterar data som skall skickas
från klienten till servern. Det är inte nödvändigt att göra denna process, men detta ger en lite mer
symetrisk syn på inkommande och utgående meddelanden till och från klienten.
Sida 27
2002-11-19
Del E
Det vi har kvar att göra är ny att se till att servern klarar av att hantera fler klienter än två. Detta gör vi
genom att ändra strategin lite grann. Vi skapar inte två processer P1 och P2 som hanterar läsning och
skrivning som vi gjorde tidgare. Detta låser oss till att det bara blir två personer som kan ”prata” med
varandra.
Serverprogrammet
För att lösa ovanstående skapar vi en liten datastruktur
i servern som lagrar inkommande meddelanden (en
lista kanske). Dessutom skapar vi en ny process (L) för
varje ny klient som kopplar upp sig. Dessa processer
skall endast vänta på data från sin klient och lagra
dessa data i datastrukturen.
Ett problem man stöter på är att många processer
samtidigt kan vilja lägga in nya meddelanden i
datastrukturen. Detta leder till att processerna kan råka
skriva över varandras data. Datastrukturen kan bli
inkonsistent. För att lösa problemet kan vi använda oss
av en liten speciell konstruktion som finns i Ada som
kallas ”protected type”. Läs mer om detta på
kurshemsidan eller i boken.
Datastrukturen med meddelanden är på ovanstående
sätt skyddad så att inte flera samtidigt kan skriva i den.
Man skulle kunna tänka sig att man skapar en speciell
process som hanterar datastrukturen också, men i Ada
är alltså inte detta nödvändigt.
Server
Datastruktur
(alla klienter)
L
L
K1
K2
Datastruktur
(meddelanden)
B
L
K3
Huvudprogrammets uppgift är i princip att se till att skapa nya processer som sköter olika delar av
problemet och sen kan det sköta någon del själv. I detta fall skulle huvudprogrammet kunna vara det
som sköter hanteringen av att vänta på nya uppkopplingar och skapa nya L-processer.
Ett problem som då kan uppstå är att det måste vara någon process som vet om ALLA uppkopplade
processer (B i figuren). Detta för att kunna skicka vidare alla de lagrade meddelandena till de andra
klienterna. Detta går att lösa genom att ha ytterligare en lista där man lagrar information om vilka
som för tillfället är uppkopplade. B är den sista processen vi inför och B hämtar data ur både
datastrukturen med meddelanden och datastrukturen som innehåller listan med alla uppkopplade
klienter för att kunna skicka ut inkomna meddelanden.
En liten modifiering (för att underlätta hanteringen för servern) är att B alltid skickar alla inkomna
meddelanden till ALLA klienter (även den som skickade meddelandet). Detta innebär en liten
modifiering i klienten, men den ändringen är kanske mindre än arbetet att lösa problemet i servern.
Hur detta utförs är en uppgift som ni löser.
Sida 28