Kommunikation via sockets
En introduktion till hur Javaprogram kan kommunicera över ett nätverk
med hjälp av TCP-sockets samt en titt på några övriga
klasser i paketet java.net.
Litteratur
- Avsnittet om nätverk i Java Tutorial, http://java.sun.com/docs/books/tutorial/networking/index.html
Titta främst på länkarna "Overview of Networking" och "All
About Sockets".
- Eriksson: Programutveckling med Java, kapitel 14 främst
sid 447-453. Tänk på att boken är skriven för JDK 1.1,
det är inte helt säkert att allt stämmer med JDK 1.3.
Mycket kort om adressering på internet
En IP-adress består av fyra byte och anger till vilken dator kommunikationen
ska ske. I stället för dessa fyra bytes kan ett alias anges. Vi
skriver exempelvis www.kth.se för att titta på KTH:s sajt, inte
130.237.32.50. Aliaset översätts automatiskt till siffernotation
av en DNS (Domain Name Service).
Det räcker dock inte att peka ut vilken dator som ska adresseras,
det gäller också att hitta rätt process på datorn.
Därför går det att ange vilken av dess portar som ska adresseras.
En port anges som ett 16-bitars tal, dvs ett tal mellan 0 och 65535. När
en process kommunicerar på nätet använder den en av dessa
portar. Vissa typer av server använder alltid samma portnummer för
att de ska vara lätta att hitta. Exempelvis förväntar vi oss
att hitta en HTTP-server på port 80 och en FTP-server på port
21. Portnummer under 1024 kallas välkända och bör inte användas
eftersom de kan vara bokade för någon sorts tjänst.
Klassen InetAddress
Klassen java.net.InetAddress symboliserar en IP-adress. Den har
inga publika konstruktorer, en instans kan skapas med någon av nedanstående
tre statiska metoder:
- getLocalHost() returnerar en InetAddress som representerar adressen
för local host.
- getByName(String host) returnerar en InetAddress som representerar
adressen för angiven host. Host kan ges antingen som ett namn (till
exempel www.isk.kth.se) eller som en adress (till exempel 130.237.209.14).
- getAllByName(String host) returnerar en array av InetAddress som representerar
alla hostens adresser.
Här kommer ett litet program som skriver ut IP-adresser med hjälp
av InetAddress, NetworkInfo.java.
TCP
Endpunkten i en TCP-förbindelse utgörs av en socket, det som skickas
från en socket i den ena änden av förbindelsen kan läsas
från socketen i den andra änden. I Java representeras socketar
av klasserna java.net.Socket och java.net.ServerSocket.
Vi ska nu steg för steg skriva ett Javaprogram som kommunicerar via
TCP med hjälp av sockets. Programmet består av en klient och en
server. Det enda som händer i programmet är att klienten skickar
en sträng till servern som skriver ut strängen.
- Vi börjar med serversidan. Det första steget är att
skapa en socket vilket görs med raden
ServerSocket ss = new ServerSocket(4711);
Detta skapar en socket som binds till port nummer 4711 på local host.
- Därefter anropas metoden accept() vilken lyssnar efter
anrop till socketens port. När den returnerar har ett anrop skett. accept()
returnerar en ny Socket över vilken kommunikationen kommer att
ske. Den ServerSocket som skapades först finns kvar och vi
kan anropa accept() på den igen för att ta emot fler
anrop från klienter som vill koppla upp sig. Anropet av accept()ser
ut så här: Socket s = ss.accept();
- Nästa steg är att få tag i en inström från
socketen så att vi kan läsa det som klienten sänder till
den. Det görs med metoden getInputStream() vilken returnerar
en InputStream. Eftersom vi ska läsa tecken vill vi hellre
ha en Reader, det får vi genom att koppla den nyss erhållna
InputStreamen till en InputStreamReader som översätter
en bytebaserad InputStream till en teckenbaserad Reader.
Detta skulle resultera i följande rad, new InputStreamReader(s.getInputStream()).
Men det vore smidigt att i stället använda strömmen BufferedReader
vilken innehåller den bekväma metoden readLine() som
läser in en rad i form av en String. Det åstadkoms på
följande sätt:
sockIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
- Det var servern, nu är det dags att skriva klienten. Steg ett
är att skapa en socket, vilket görs så här:
Socket s = new Socket("localhost", 4711);
När en Socket skapas anropar den automatiskt angiven port (4711
i det här fallet) på angiven host (local host i det här fallet).
- Sedan ska vi ha tag i en utström till socketen vilket vi får
genom att anropa metoden getOutputStream(). Den returnerar en OutputStream
men det vore enklare att använda en PrintWriter. Writer
eftersom det handlar om teckenbaserad I/O och PrintWriter eftersom
den innehåller de välkända och lättanvända metoderna
print() och println(). Detta resulterar i följande
rad:
PrintWriter sockOut = new PrintWriter(s.getOutputStream());
Då var det klart. Det vi skriver till klientens utström går
att läsa från serverns inström. Här kommer hela programmet,
det består av en serverdel, ReadAndPrint.java,
och en klientdel, Send.java.
Flertrådad server
Ovanstående server har oacceptabelt lång svarstid (förklaras
mer i avsnittet om meddelandesändning som kommer senare). Lösningen
är att göra den flertrådad. Vi bryr oss inte om att implementera
en trådpool utan nöjer oss med den flertrådning som skissas
i stycket "En tråd per meddelande". Detta resulterar i följande
variant av servern, MTReadAndPrint.java.
Varje gång accept() returnerar har en ny Socket skapats
till vilken en ny förbindelse har kopplats upp. Då startas nu
en ny tråd som läser från den nyss returnerade Socketen.
Detta innebär att alla förbindelser hanteras samtidigt.
URL-hantering
En URL (Uniform Resource Locator) identifierar en resurs
på nätet. Det kan vara till exempel en html-fil, en bild eller
en databas. URL:er hanteras i Java av klassen URL. Ett exempel på
en URL är http://www.ncsa.uiuc.edu:8080/demoweb/url-primer.html, där
"http" anger protokoll, "www.ncsa.uiuc.edu" anger host (dator), "8080" anger
port och "/demoweb/url-primer.html" pekar ut resursen på datorn. Denna
sista del kallas ofta path.
Klassen URL har dels konstruktorer där en hel URL kan ges
i form av en sträng, dels sådana där de olika delarna kan
anges var för sig. Det finns också konstruktorer med vars hjälp
en URL kan anges relativt en annan URL. Klassen innehåller bland annat
metoder som ger information om den representerade URL:en.