{"id":399,"date":"2011-11-03T14:36:19","date_gmt":"2011-11-03T12:36:19","guid":{"rendered":"http:\/\/www.kristiansborg.dk\/bibliotek\/?page_id=399"},"modified":"2011-11-03T14:42:05","modified_gmt":"2011-11-03T12:42:05","slug":"kapitel-6-remote-method-invocation-rmi","status":"publish","type":"page","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/","title":{"rendered":"Kapitel 6 Remote Method Invocation (RMI)"},"content":{"rendered":"<p>Det er relativt nemt at f\u00e5 programmer p\u00e5 to computere forbundet i netv\u00e6rk til at kommunikere ved hj\u00e6lp af Java. Det er alts\u00e5 ikke at etablere kommunikationen, der er kompliceret B det er at bruge den fornuftigt. Samtale er ikke meget v\u00e6rd, hvis ikke begge parter snakker et sprog, den anden kan forst\u00e5. Det betyder, at der er behov for en eller anden form for protokol, der specificerer, hvad klienten mener, n\u00e5r den sender en vis besked. Protokollen skal ogs\u00e5 angive, hvilke svarmuligheder serveren har B det skal v\u00e6re muligt at kommunikere en fornuftig fejlmeddelelse tilbage til klienten, hvis den \u00f8nskede service ikke var tilg\u00e6ngelig.<\/p>\n<div style=\"border: 1px solid red; color: red\">\n<b>Du l\u00e6ser en gammel bog<\/b><br \/>\nDen bog, du l\u00e6ser her, er fra 1998, og mange ting kan have \u00e6ndret sig siden da.<br \/>\nVi h\u00e5ber, at du stadig kan finde relevant information i den.<br \/>\nHvis du vil l\u00e6se aktuelle oplysninger om de avancerede dele af Java, anbefaler vi<br \/>\nbogen <a href=\"http:\/\/clk.tradedoubler.com\/click?p(197229)a(2029446)g(19172914)url(http:\/\/www.adlibris.com\/dk\/product.aspx?isbn=0132354799)\"  target=\"_blank\">Core Java &#8211; Advanced Features<\/a><img decoding=\"async\" src=\"http:\/\/impdk.tradedoubler.com\/imp?type(inv)g(19172914)a(2029446)\" \/>\n<\/div>\n<p>S\u00e6dvanligvis h\u00e5ndteres fejlsituationer i Java ved hj\u00e6lp af exceptions. Hvis kommunikationen foreg\u00e5r gennem streams, er det ikke muligt at sende en exception, hvis der opst\u00e5r en fejl p\u00e5 serveren. Hvis serveren selv kaster en exception, vil klienten ikke f\u00e5 det at vide, og fejlen vil blive udskrevet p\u00e5 serverens konsol. Den eneste l\u00f8sning er at sende en tekstbesked, der angiver, at der er sket en exception, og derefter f\u00e5 klientprogrammet selv til at instantiere og kaste en exception. Dette undergraver imidlertid selve ideen med exceptions B fordelen skulle jo netop v\u00e6re, at man var fri for at kontrollere, at en bestemt operation var g\u00e5et godt.<\/p>\n<p>&nbsp;<\/p>\n<p>Et andet problem er opbygningen af serveren. Her vil der typisk v\u00e6re en funktion, der h\u00e5ndterer den indledende kommunikation med klienten. Denne funktion finder ud af, hvilken service klienten gerne vil have udf\u00f8rt, og herefter kalder den den tilsvarende funktion. Denne funktion beder om eventuelle parametre, og sender svar tilbage til klienten.<\/p>\n<p>&nbsp;<\/p>\n<p>Denne fremgangsm\u00e5de giver to problemer. Dels er det ikke designm\u00e6ssigt p\u00e6nt at have en funktion, der blot best\u00e5r af en r\u00e6kke if-s\u00e6tninger, der afg\u00f8r, hvilken funktion, der skal kaldes bagefter, men det er ogs\u00e5 irriterende at have netv\u00e6rkskommunikationen spredt udover hele programmet.<\/p>\n<p>&nbsp;<\/p>\n<p>Ogs\u00e5 for klienten ser det skidt ud. Igen bliver netv\u00e6rkskommunikationen spredt ud over hele programmet, og hver gang man skal have udf\u00f8rt en kommando hos serveren, skal man instantiere en Socket, streams og s\u00e5 videre.<\/p>\n<p>&nbsp;<\/p>\n<p>Det ville v\u00e6re nemmere, hvis man bare kunne instantiere et objekt p\u00e5 serveren, og herefter lade klienten kalde dem, som l\u00e5 de p\u00e5 den lokale maskine. Dette kan man ved at bruge RMI B n\u00e6sten. Der er lige et par kunstgreb, der skal g\u00f8res, men alt i alt er det v\u00e6sentligt lettere end den s\u00e6dvanlige kommunikation.<\/p>\n<p><strong><br \/>\n<\/strong><\/p>\n<h1>Serveren<\/h1>\n<p>&nbsp;<\/p>\n<p>Som n\u00e6vnt kan man ved RMI lade serveren instantiere et objekt, og herefter lade klienten kalde dets metoder. Den klasse, objektet tilh\u00f8rer, skal imidlertid opfylde nogle bestemte krav. For eksempel skal alle de metoder, der skal kunne tilg\u00e5s fra klienten, l\u00e6gges ud i et interface, som klassen naturligvis skal implementere.<\/p>\n<p>&nbsp;<\/p>\n<p>Interfacet skal nedarve fra java.rmi.Remote. Dette sikrer, at man kan behandle alle RMI-objekter abstrakt ved blot at referere til dem som havende typen java.rmi.Remote. Derudover skal man v\u00e6re opm\u00e6rksom p\u00e5, at alle metoder skal have java.rmi.RemoteException i deres throws-sektion udover de andre exceptions, de kan kaste.<\/p>\n<p>&nbsp;<\/p>\n<p>Nedenst\u00e5ende eksempel viser et s\u00e5dant interface. Det angiver fire matematiske funktioner. I praksis vil man nok v\u00e6lge nogle lidt mere komplicerede funktioner, for den tid, der bruges p\u00e5 kommunikationen, er mangefold st\u00f8rre end den tid, selve udregningen tager.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public interface MathInterface extends Remote<\/p>\n<p>{<\/p>\n<p>public float add (float a, float b) throws RemoteException;<\/p>\n<p>public float sub (float a, float b) throws RemoteException;<\/p>\n<p>public float mul (float a, float b) throws RemoteException;<\/p>\n<p>public float div (float a, float b) throws RemoteException,<\/p>\n<p>ArithmeticException;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r interfacet er lavet, kan man begynde at skrive selve implementateringen. Den klasse, der implementerer interfacet skal nedarve fra java.rmi.server.UnicastRemoteObject. Derudover skal den have en constructor B ogs\u00e5 selvom denne er tom. Constructoren skal B ligesom de \u00f8vrige funktioner B have java.rmi.RemoteException i throws-sektionen. Dette eksempel viser implementeringen af interfacet.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>import java.rmi.server.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class MathServer extends UnicastRemoteObject implements MathInterface<\/p>\n<p>{<\/p>\n<p>public MathServer () throws RemoteException<\/p>\n<p>{<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public float add (float a, float b) throws RemoteException<\/p>\n<p>{<\/p>\n<p>return a + b;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>public float sub (float a, float b) throws RemoteException<\/p>\n<p>{<\/p>\n<p>return a &#8211; b;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public float mul (float a, float b) throws RemoteException<\/p>\n<p>{<\/p>\n<p>return a * b;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public float div (float a, float b) throws RemoteException,<\/p>\n<p>ArithmeticException<\/p>\n<p>{<\/p>\n<p>return a \/ b;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Nu er selve den klasse, der skal g\u00f8res tilg\u00e6ngelig skrevet. Nu skal der blot skrives en klasse, der kalder de funktioner, der registrerer klassen som offentlig tilg\u00e6ngelig. Den kode kunne naturligvis have ligget i MathServer=s main-funktion, men her er det alts\u00e5 valgt at lave en selvst\u00e6ndig klasse til det. Nedenst\u00e5ende eksempel viser, hvordan et MathServer-objekt instantieres og registreres.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>import java.net.MalformedURLException;<\/p>\n<p>&nbsp;<\/p>\n<p>public class Server<\/p>\n<p>{<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>System.setSecurityManager (new RMISecurityManager());<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>MathServer obj = new MathServer ();<\/p>\n<p>}<\/p>\n<p>catch (RemoteException re)<\/p>\n<p>{<\/p>\n<p>System.out.println (re.toString());<\/p>\n<p>System.exit (1);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>Naming.bind (&#8220;\/\/void-main\/Math&#8221;, obj);<\/p>\n<p>System.out.println (&#8220;Udf\u00f8rt&#8221;);<\/p>\n<p>}<\/p>\n<p>catch (MalformedURLException mue)<\/p>\n<p>{<\/p>\n<p>System.out.println (mue.toString());<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>catch (UnknownHostException uhe)<\/p>\n<p>{<\/p>\n<p>System.out.println (uhe.toString());<\/p>\n<p>}<\/p>\n<p>catch (AlreadyBoundException abe)<\/p>\n<p>{<\/p>\n<p>System.out.println (abe.toString());<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Det f\u00f8rste, der sker i eksemplet er, at der bliver installeret en security manager. Det kan enten v\u00e6re RMISecurityManager, eller en man selv har defineret. Installerer man ikke en security manager, er det ikke tilladt at instantiere RMI-klasser.<\/p>\n<p>Dern\u00e6st instantieres et objekt af klassen MathServer. Som n\u00e6vnt tidligere, kan det medf\u00f8re en RemoteException, og derfor bliver den fanget her. Opst\u00e5r denne exception afbrydes programmet, for s\u00e5 er der ikke megen id\u00e9 i at fors\u00f8ge at registrere objektet.<\/p>\n<p>Hvis objektet bliver instantieret korrekt, er det n\u00e6ste skridt at g\u00f8re det tilg\u00e6ngeligt for klienterne. Det g\u00f8res ved at registrere det i Javas Naming-service ved hj\u00e6lp af metoden bind, der er en statisk metode i klassen Naming. Det f\u00f8rste parameter er en tekststreng, der angiver den URL, objektet skal kendes under. Klienterne skal kende denne URL for at kunne tilg\u00e5 objektet. Den f\u00f8rste del af URL=en er computerens navn B i dette tilf\u00e6lde void-main. Herefter skrives det navn, objektet skal v\u00e6re kendt under. Det beh\u00f8ver ikke at have nogen tilknytning til klassens eller objektets navn i programmet. Metodens andet parameter er en reference til det objekt, der skal bindes. Dette objekt skal v\u00e6re en sub-klasse af java.rmi.server.UnicastRemoteObject.<\/p>\n<p>&nbsp;<\/p>\n<p>Da det f\u00f8rste parameter som n\u00e6vnt er en URL, kan bind-metoden resultere i en java.net.MalformedURLException. Den kan ogs\u00e5 medf\u00f8re en java.rmi.UnknownHostException, hvis navnet p\u00e5 computeren ikke er gyldigt. Endeligt kan funktionskaldet medf\u00f8re en java.rmi.AlreadyBoundException, hvis der allerede er bundet et objekt med det navn, man angiver. Hvis man ved, at man har bundet objektet tidligere, men gerne vil opdatere det, kan man bruge Naming.rebind i stedet for Naming.bind.<\/p>\n<p>Et objekt, der nedarver fra java.rmi.server.UnicastRemoteObject\u00a0beh\u00f8ver ikke at blive registreret i naming-servicen. Hvis en anden funktion returnerer objektet, kan klienter sagtens kalde funktioner p\u00e5 det, selvom det ikke er registreret. Problemet er bare at f\u00e5 fat i den f\u00f8rste instans af et objekt. Dette objekt kan ikke returneres ved et funktionskald, for man har ikke noget objekt at kalde funktionen p\u00e5. I stedet kan naming-servicen bruges som vist i dette eksempel. Bem\u00e6rk, at selvom man skal angive navnet p\u00e5 en computer i bind-metoden, er det kun muligt at bruge naming-servicen p\u00e5 den lokale maskine. Dette er gjort for at sikre, at man ikke kan tilslutte sig en anden computers naming-service, registrere et af sine egne objekter i stedet for det, der tidligere var registreret, og derefter f\u00e5 klienter til at tro, at de bruger det originale objekt.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r kildeteksterne er skrevet kan de kompileres med den vanlige Java-compiler. Derefter skal den klasse, der skal v\u00e6re offentligt tilg\u00e6ngelig, kompileres med en speciel RMI-compiler. Hvis man har Suns JDK hedder den rmic. Det vil alts\u00e5 sige, at man fra kommandoprompten skal skrive rmic MathServer for at f\u00e5 klassen MathServer gennem RMI-compileren. N\u00e5r rmic er f\u00e6rdig har den genereret to nye klasser: MathServer_Stub og MathServer_Skel. Stub-klassen skal l\u00e6gges sammen med klient-programmet B det er den, der h\u00e5ndterer netv\u00e6rkskommunikationen med Skeleton-klassen, der l\u00e6gges p\u00e5 serveren. Set fra programm\u00f8rens synspunkt er dette ikke s\u00e5 vigtigt B man f\u00e5r aldrig selv brug for at instantiere eller p\u00e5 anden m\u00e5de benytte stub- og skeleton-klasserne i sit program.<\/p>\n<p>&nbsp;<\/p>\n<p>Nu hvor programmet er kompileret og alle klassefiler genereret, er det klar til udf\u00f8rsel. Inden programmet startes skal man dog starte RMI-registreringsdatabasen. Det er program, der f\u00f8lger med Suns JDK. Programmet startes med kommandoen rmiregistry. Man skal huske, enten at starte rmiregistry fra det bibliotek, hvor klassefilerne ligger eller at inkludere klassefilerne i sin classpath.<\/p>\n<p>&nbsp;<\/p>\n<h1>Klienten<\/h1>\n<p>&nbsp;<\/p>\n<p>N\u00e5r server-programmet er f\u00e6rdig, kan man begynde at skrive klienten. Klienten har to RMI-relaterede opgaver. Den skal for det f\u00f8rste finde det rette objekt, og for det andet skal den kalde metoder p\u00e5 objektet.<\/p>\n<p>&nbsp;<\/p>\n<p>Den f\u00f8rste opgave l\u00f8ses ved hj\u00e6lp af naming-servicen. Klienten kender servermaskinens navn og ogs\u00e5 det navn, objektet er blevet registreret som. Disse oplysninger sendes videre til naming-servicen ved hj\u00e6lp af lookup-metoden, der er en statisk funktion i Naming-klassen. Denne funktion returnerer en instans af RMI-objektet. Alle referencer til RMI-objekter, der bruges som parametre eller returv\u00e6rdier, skal v\u00e6re referencer til interfacet og ikke til selve objektet. Derfor skal klientens reference til MathServer v\u00e6re af typen MathInterface. Da Naming.lookup\u00a0er en generel funktion, skal returv\u00e6rdien castes.<\/p>\n<p>&nbsp;<\/p>\n<p>lookup-funktionen kan ende med en del exceptions. Den f\u00f8rste er java.net.MalformedURLException, der opst\u00e5r, hvis adressen p\u00e5 objektet er formuleret forkert. Dern\u00e6st kan java.rmi.NotBoundException opst\u00e5. Det sker, hvis det objekt, man fors\u00f8ger at f\u00e5 kontakt med, ikke er bundet p\u00e5 serveren. Endeligt kan java.rmi.RemoteException\u00a0blive opst\u00e5, hvis der opst\u00e5r en generel kommunikationsfejl.<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r f\u00f8rst naming-servicen har fundet objektet og returneret en reference til det, er det klart til brug. Man skal bare huske, at ethvert kald af en funktion i et RMI-objekt kan medf\u00f8re en java.rmi.RemoteException B derfor skal man enten erkl\u00e6re den i funktionernes throws-liste eller catch=e dem hver gang. Nedenst\u00e5ende klasse viser, hvordan MathServer kan bruges.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class MathClient<\/p>\n<p>{<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>&nbsp;<\/p>\n<p>{<\/p>\n<p>MathInterface math = null;<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>math = (MathInterface) Naming.lookup (&#8220;\/\/void-main\/Math&#8221;);<\/p>\n<p>}<\/p>\n<p>catch (java.net.MalformedURLException mue)<\/p>\n<p>{<\/p>\n<p>System.out.println (mue.toString());<\/p>\n<p>System.exit (1);<\/p>\n<p>}<\/p>\n<p>catch (NotBoundException nbe)<\/p>\n<p>{<\/p>\n<p>System.out.println (nbe.toString());<\/p>\n<p>System.exit (2);<\/p>\n<p>}<\/p>\n<p>catch (RemoteException re)<\/p>\n<p>{<\/p>\n<p>System.out.println (re.toString());<\/p>\n<p>System.exit (3);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>System.out.println (&#8220;1 + 2 er &#8221; + math.add (1,2));<\/p>\n<p>System.out.println (&#8220;3 &#8211; 4 er &#8221; + math.sub (3,4));<\/p>\n<p>System.out.println (&#8220;5 * 6 er &#8221; + math.mul (5,6));<\/p>\n<p>System.out.println (&#8220;7 \/ 8 er &#8221; + math.div (7,8));<\/p>\n<p>}<\/p>\n<p>catch (RemoteException re)<\/p>\n<p>{<\/p>\n<p>System.out.println (re.toString());<\/p>\n<p>System.exit (4);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<h1>Fordeling af filer<\/h1>\n<p>&nbsp;<\/p>\n<p>N\u00e5r b\u00e5de serveren og klienten er skrevet og kompileret, er programmet parat til at blive delt ud p\u00e5 forskellige maskiner. P\u00e5 serveren skal implementeringen af server-klassen ligge sammen med interfacet, og den skeleton-klasse, der blev genereret af rmi-compileren. P\u00e5 klienten skal klient-programmet naturligvis ligge. Det skal interfacet ogs\u00e5 og den stub-klasse, rmi-compileren genererede. En samlet oversigt over placeringen af filer ses nedenst\u00e5ende tabel<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td width=\"326\" valign=\"top\"><em>Fil<\/em><\/td>\n<td width=\"326\" valign=\"top\"><em>Placering<\/em><\/td>\n<\/tr>\n<tr>\n<td width=\"326\" valign=\"top\">Implementering   af server-klasse<\/td>\n<td width=\"326\" valign=\"top\">Server<\/td>\n<\/tr>\n<tr>\n<td width=\"326\" valign=\"top\">Skeleton-klasse<\/td>\n<td width=\"326\" valign=\"top\">Server<\/td>\n<\/tr>\n<tr>\n<td width=\"326\" valign=\"top\">Interface<\/td>\n<td width=\"326\" valign=\"top\">Server   og klient<\/td>\n<\/tr>\n<tr>\n<td width=\"326\" valign=\"top\">Stub-klasse<\/td>\n<td width=\"326\" valign=\"top\">Klient<\/td>\n<\/tr>\n<tr>\n<td width=\"326\" valign=\"top\">Implementering   af klient-klasse<\/td>\n<td width=\"326\" valign=\"top\">Klient<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h1>En nameserver<\/h1>\n<p>&nbsp;<\/p>\n<p>RMI g\u00f8r det muligt at distribuere sin applikation over adskillige maskiner. Desv\u00e6rre er det uheldigt, at adressen p\u00e5 samtlige servere skal v\u00e6re kendt af klienten. Dette vil betyde \u00e6ndringer i samtlige klienter, hvis bare en af serverne skiftede adresse. For at l\u00f8se dette problem kan man bruge en nameserver.<\/p>\n<p>Nameserverens opgave er at registrere hvilke servere, der stiller hvilke services til r\u00e5dighed. Det vil sige, at alle servere skal fort\u00e6lle nameserveren, at de udbyder en service. Dern\u00e6st kan en klient bede nameserveren om en reference til et givent objekt eller en given service, hvorefter nameserveren vil bede den rigtige server om en reference til objektet, der sendes videre til klienten. Der er tydeligvis nogle tidsm\u00e6ssige omkostninger ved at bruge en nameserver, men nogle gange kan det v\u00e6re den eneste udvej.<\/p>\n<p>I dette afsnit vil et nameserver-eksempel blive gennemg\u00e5et. Der er tale om en relativt primitiv nameserver B der er mange sikkerhedsaspekter, der ikke er taget h\u00f8jde for. Dette er gjort fuldt bevidst, da disse aspekter ikke har meget med RMI at g\u00f8re og blot vil g\u00f8re kildeteksten mere kompliceret at l\u00e6se og forst\u00e5. Eksemplet afsluttes imidlertid med et afsnit, der kort beskriver, hvordan de n\u00f8dvendige udvidelser kan implementeres.<\/p>\n<p>&nbsp;<\/p>\n<h2>Krav til nameserveren<\/h2>\n<p>&nbsp;<\/p>\n<p>Nameserveren skal have f\u00f8lgende funktionalitet:<\/p>\n<ul>\n<li>Servere skal kunne registrere deres services hos nameserveren.<\/li>\n<li>Servere skal kunne fjerne deres services fra nameserveren, n\u00e5r de ikke l\u00e6ngere bliver udbudt.<\/li>\n<li>Klienter skal kunne tilg\u00e5 objekter p\u00e5 en hvilken som helst server gennem nameserveren. N\u00e5r f\u00f8rst klienten har f\u00e5et en reference til objektet p\u00e5 den rigtige server, skal nameserverens rolle v\u00e6re udspillet.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>Disse krav kan opfyldes af funktionerne i nedenst\u00e5ende interface:<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>import java.net.MalformedURLException;<\/p>\n<p>&nbsp;<\/p>\n<p>public interface RMINameServer extends Remote<\/p>\n<p>{<\/p>\n<p>public void bind(String host, String name) throws RemoteException,<\/p>\n<p>AlreadyBoundException;<\/p>\n<p>&nbsp;<\/p>\n<p>public void unbind (String host, String name) throws RemoteException;<\/p>\n<p>&nbsp;<\/p>\n<p>public void rebind (String host, String name) throws RemoteException;<\/p>\n<p>&nbsp;<\/p>\n<p>public Object lookup (String name) throws RemoteException,<\/p>\n<p>MalformedURLException, NotBoundException;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<h2>Funktioner i nameserveren<\/h2>\n<p>&nbsp;<\/p>\n<p>bind-funktionen bruges, n\u00e5r en server skal registreres i nameserveren. Alle servere skal som bekendt registreres i nameserveren. Den f\u00f8rste parameter er navnet p\u00e5 server-computeren. Det kan enten v\u00e6re serverens IP-nummer eller computerens navn p\u00e5 netv\u00e6rket. name er det navn, objektet er bundet under p\u00e5 serveren \u2013 objektet vil p\u00e5 nameserveren blive bundet p\u00e5 det samme navn. Klienterne bruger dette navn til at tilg\u00e5 objektet.<\/p>\n<p>&nbsp;<\/p>\n<p>unbind-funktionen bruges, n\u00e5r en server oph\u00f8rer med at stille en bestemt service til r\u00e5dighed. Igen gives server-computerens navn med som parameter sammen med objektets navn.<\/p>\n<p>rebind er en udvidelse af bind-funktionen. Den opdaterer den reference, der er til det angivne objekt. Hvis objektet ikke allerede er bundet i nameserveren, vil det blive bundet, som var der foretaget et kald af bind i stedet for rebind.<\/p>\n<p>&nbsp;<\/p>\n<p>lookup bruges af klienterne til at f\u00e5 en reference til et givent objekt. Det navn, der gives med, er det navn, som serverne har valgt at blive bundet p\u00e5.<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses, er funktionerne stor set magen til de, der findes i Naming-klassen.<\/p>\n<p>Implementeringen af RMINameServer-interfacet findes i klassen ConcreteNameServer:<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>import java.rmi.server.*;<\/p>\n<p>import java.util.*;<\/p>\n<p>import java.net.MalformedURLException;<\/p>\n<p>&nbsp;<\/p>\n<p>public class ConcreteNameServer extends UnicastRemoteObject implements RMINameServer<\/p>\n<p>{<\/p>\n<p>protected Vector bindings;<\/p>\n<p>&nbsp;<\/p>\n<p>public ConcreteNameServer () throws RemoteException<\/p>\n<p>{<\/p>\n<p>bindings = new Vector();<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void bind (String host, String name) throws RemoteException,<\/p>\n<p>AlreadyBoundException<\/p>\n<p>{<\/p>\n<p>Binding b = new Binding (host, name);<\/p>\n<p>&nbsp;<\/p>\n<p>if (bindings.contains (b))<\/p>\n<p>throw new AlreadyBoundException (host + &#8220;\/&#8221; + name);<\/p>\n<p>&nbsp;<\/p>\n<p>bindings.addElement (b);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void unbind (String host, String name) throws RemoteException<\/p>\n<p>{<\/p>\n<p>Binding b = new Binding (host, name);<\/p>\n<p>bindings.removeElement (b);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void rebind (String host, String name) throws RemoteException<\/p>\n<p>{<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>unbind (host,name);<\/p>\n<p>bind (host, name);<\/p>\n<p>}<\/p>\n<p>catch (AlreadyBoundException abe)<\/p>\n<p>{<\/p>\n<p>\/\/ Just catch it and ignore it<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public Object lookup (String name) throws RemoteException,<\/p>\n<p>MalformedURLException, NotBoundException<\/p>\n<p>{<\/p>\n<p>for (int i = 0; i &lt; bindings.size(); i++)<\/p>\n<p>{<\/p>\n<p>if (((Binding) bindings.elementAt (i)).name.equals (name))<\/p>\n<p>{<\/p>\n<p>return Naming.lookup (&#8220;rmi:\/\/&#8221; + ((Binding) bindings.elementAt (i)).host<\/p>\n<p>+ &#8220;\/&#8221; + ((Binding) bindings.elementAt (i)).name);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>throw new NotBoundException (name);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Nameserveren bruger klassen Binding til at registrere et server\/objekt-par. Listen over bindinger gemmes herefter i en vektor (java.util.Vector). Binding-klassen er specielt udviklet til nameserveren:<\/p>\n<p>&nbsp;<\/p>\n<p>public class Binding<\/p>\n<p>{<\/p>\n<p>public String host;<\/p>\n<p>public String name;<\/p>\n<p>&nbsp;<\/p>\n<p>public Binding ()<\/p>\n<p>&nbsp;<\/p>\n<p>{<\/p>\n<p>name = host = null;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public Binding (String host, String name)<\/p>\n<p>{<\/p>\n<p>this.host = host;<\/p>\n<p>this.name = name;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Med de klasser, der indeholder nameserverens funktionalitet, mangler man bare den programkode, der opretter en instans af ConcreteNameServer-klassen og binder denne i registreringsservicen, s\u00e5 klienter og servere har adgang til den:<\/p>\n<p>&nbsp;<\/p>\n<p>import java.rmi.*;<\/p>\n<p>import java.rmi.server.*;<\/p>\n<p>import java.net.MalformedURLException;<\/p>\n<p>&nbsp;<\/p>\n<p>public class NameServerApp<\/p>\n<p>{<\/p>\n<p>public static void main(String args[]) throws MalformedURLException,<\/p>\n<p>RemoteException<\/p>\n<p>{<\/p>\n<p>System.out.println (&#8220;Initializing&#8221;);<\/p>\n<p>ConcreteNameServer nameServer = new ConcreteNameServer();<\/p>\n<p>System.setSecurityManager (new RMISecurityManager ());<\/p>\n<p>Naming.bind (&#8220;NameServer&#8221;, nameServer);<\/p>\n<p>System.out.println (&#8220;Server ready&#8221;);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<h2>Et client\/server eksempel med nameserver<\/h2>\n<p>&nbsp;<\/p>\n<p>For at vise, hvordan henholdsvis klienten og serveren anvender en nameserver, beskrives her, hvilke \u00e6ndringer, der skal foretages i det Math-eksempel, der blev gennemg\u00e5et tidligere i kapitlet. Interfacet MathInterface og klassen MathServer skal ikke \u00e6ndres. Disse vedr\u00f8rer kun implementeringen af funktionaliteten p\u00e5 serveren, og den skal naturligvis v\u00e6re den samme, hvadenten der bruges en nameserver eller ej. Der skal til geng\u00e6ld foretages \u00e6ndringer i den klasse, der opretter MathServer-objektet og binder det \u2013 denne klasse skal nu ogs\u00e5 s\u00f8rge for at registrere klassen i nameserveren. Det betyder, at server-klassens main-funktion kommer til at se s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>System.setSecurityManager (new RMISecurityManager());<\/p>\n<p>MathServer obj = null;<\/p>\n<p>try<\/p>\n<p>&nbsp;<\/p>\n<p>{<\/p>\n<p>obj = new MathServer ();<\/p>\n<p>}<\/p>\n<p>catch (RemoteException re)<\/p>\n<p>{<\/p>\n<p>System.out.println (re.toString());<\/p>\n<p>System.exit (1);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>Naming.bind (&#8220;\/\/void-main\/Math&#8221;, obj);<\/p>\n<p>System.out.println (&#8220;Objekt bundet lokalt&#8221;);<\/p>\n<p>}<\/p>\n<p>catch (MalformedURLException mue)<\/p>\n<p>{<\/p>\n<p>System.out.println (mue.toString());<\/p>\n<p>}<\/p>\n<p>catch (UnknownHostException uhe)<\/p>\n<p>{<\/p>\n<p>System.out.println (uhe.toString());<\/p>\n<p>}<\/p>\n<p>catch (AlreadyBoundException abe)<\/p>\n<p>{<\/p>\n<p>System.out.println (abe.toString());<\/p>\n<p>}<\/p>\n<p>catch (RemoteException rr)<\/p>\n<p>{<\/p>\n<p>System.out.println (rr.toString());<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>RMINameServer nameserver = null;<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>nameserver = (RMINameServer) Naming.lookup (&#8220;\/\/nameserver\/NameServer&#8221;);<\/p>\n<p>}<\/p>\n<p>catch (RemoteException rx)<\/p>\n<p>{<\/p>\n<p>System.out.println (rx.toString());<\/p>\n<p>}<\/p>\n<p>catch (MalformedURLException mux)<\/p>\n<p>{<\/p>\n<p>System.out.println (mux.toString());<\/p>\n<p>}<\/p>\n<p>catch (NotBoundException nbx)<\/p>\n<p>{<\/p>\n<p>System.out.println (nbx.toString());<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>nameserver.bind (&#8220;void-main&#8221;,&#8221;Math&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>catch (RemoteException rex)<\/p>\n<p>{<\/p>\n<p>System.out.println (rex.toString());<\/p>\n<p>}<\/p>\n<p>catch (AlreadyBoundException abex)<\/p>\n<p>{<\/p>\n<p>System.out.println (abex.toString());<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Ogs\u00e5 klient-programmet skal \u00e6ndres, men dog ikke i samme omfang som serveren. Blot skal klienten nu sp\u00f8rge nameserveren om at f\u00e5 en reference til MathServer-objektet i stedet for selve serveren. Det betyder, at main-funktionen i MathClient-klassen nu kommer til at se s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>public static final void main (String args[])<\/p>\n<p>{<\/p>\n<p>MathInterface math = null;<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>math = (MathInterface) Naming.lookup (&#8220;\/\/nameserver\/Math&#8221;);<\/p>\n<p>}<\/p>\n<p>catch (java.net.MalformedURLException mue)<\/p>\n<p>{<\/p>\n<p>System.out.println (mue.toString());<\/p>\n<p>System.exit (1);<\/p>\n<p>}<\/p>\n<p>catch (NotBoundException nbe)<\/p>\n<p>{<\/p>\n<p>System.out.println (nbe.toString());<\/p>\n<p>System.exit (2);<\/p>\n<p>}<\/p>\n<p>catch (RemoteException re)<\/p>\n<p>{<\/p>\n<p>System.out.println (re.toString());<\/p>\n<p>System.exit (3);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>System.out.println (&#8220;1 + 2 er &#8221; + math.add (1,2));<\/p>\n<p>System.out.println (&#8220;3 &#8211; 4 er &#8221; + math.sub (3,4));<\/p>\n<p>System.out.println (&#8220;5 * 6 er &#8221; + math.mul (5,6));<\/p>\n<p>System.out.println (&#8220;7 \/ 8 er &#8221; + math.div (7,8));<\/p>\n<p>}<\/p>\n<p>catch (RemoteException re)<\/p>\n<p>{<\/p>\n<p>System.out.println (re.toString());<\/p>\n<p>System.exit (4);<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<h2>Forbedringer til nameserveren<\/h2>\n<p>&nbsp;<\/p>\n<p>Som n\u00e6vnt tidligere er det muligt at foretage mange forbedringer til den nameserver, der er blevet skitseret her.<\/p>\n<p>&nbsp;<\/p>\n<p>Sikkerheden i systemet er ikke-eksisterende. Det er muligt for en hvilken som helst server at unbind\u2019e en anden servers objekt og i stedet inds\u00e6tte sit eget objekt i det samme navn. Denne risiko kan man minimere ved at lade bind- og rebind-funktionerne returnere et unikt id. Dette id skal sendes med til unbind og rebind for at sikre, at det er den samme server, der bind\u2019er og unbind\u2019er objekter.<\/p>\n<p>Et andet problem er, at serverne <em>skal<\/em> kalde unbind-funktionen for at vise, at de ikke l\u00e6ngere stiller deres service til r\u00e5dighed. Desv\u00e6rre er det jo engang s\u00e5dan, at computere af og til g\u00e5r ned uden videre, og man kan derfor risikere, at nameserveren har referencer til computere, der er g\u00e5et ned. Denne problematik kan afhj\u00e6lpes ved hj\u00e6lp af time-outs. Ved at lade serveren kontrollere, om der stadig er kontakt til alle serverne med j\u00e6vne mellemrum, kan man formindske antallet af \u201dd\u00f8de\u201d servere drastisk. Det kan g\u00f8res ved at kr\u00e6ve, at alle objekter skal inkludere et interface, der indeholder funktionen ping(). Det eneste, denne funktion skal g\u00f8re, er at returnere true for at vise, at objektet stadig eksisterer, og at server-computeren stadigv\u00e6k k\u00f8rer. Hvis nameserveren ikke modtager svaret fra funktionen inden for et bestemt tidsrum, skal det p\u00e5g\u00e6ldende objekt fjernes fra listen over registrerede objekter. Hvor lang tid serverne skal have til at svare, afh\u00e6nger af netv\u00e6rkets hastighed og belastning. Hvor ofte nameserveren skal kontrollere om de \u00f8vrige servere er i live, er en afvejning mellem, hvor meget man \u00f8nsker at belaste netv\u00e6rket, nameserveren og de \u00f8vrige servere, og hvor mange \u201dd\u00f8de\u201d servere, man vil acceptere, der eksisterer p\u00e5 nameserveren.<\/p>\n<p>&nbsp;<\/p>\n<h1>Brugerdefinerede RMI-sockets<\/h1>\n<p>&nbsp;<\/p>\n<p>RMI \u00e5bner mulighed for, at man selv kan definere sine sockets. Der kan v\u00e6re mange grunde til, at man selv \u00f8nsker at definere sine sockets. De to mest udbredte er<\/p>\n<ul>\n<li>Man vil have klient og server til at bruge en protokol, der krypterer eller komprimerer data.<\/li>\n<li>Man vil anvende forskellige sockets til forskellige forbindelser.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>En mulig og s\u00e6rdeles popul\u00e6r anvendelse af brugerdefinerede RMI-sockets er SSL. Ved at f\u00e5 RMI til at kommunikere ved hj\u00e6lp af SSL-protokollen kan man sikre sikker, krypteret kommunikation mellem klienten og serveren.<\/p>\n<p>&nbsp;<\/p>\n<p>Man definerer sin egen socket ved at anvende en java.rmi.server.RMISocketFactory. Ved at g\u00f8re det, f\u00e5r man mulighed for at anvende en anden protokol end den TCP\/IP, RMI normalt anvender. RMI anvender nemlig normalt den socket, der er defineret i java.net.Socket.<\/p>\n<p>&nbsp;<\/p>\n<p>Fra og med JDK version 1.2 er det muligt at definere forskellige former for sockets til forskellige forbindelser \u2013 det g\u00f8r man ved hj\u00e6lp af klassen java.rmi.server.SocketType. I dette afsnit vil det blive gennemg\u00e5et, hvordan man opretter en RMISocketFactory, der producerer en enkelt type<\/p>\n<p>&nbsp;<\/p>\n<p>socket, og hvordan man laver en RMISocketFactory, der producerer flere forskellige typer.<\/p>\n<p>&nbsp;<\/p>\n<p>Inden det gennemg\u00e5s, hvordan man bruger brugerdefinerede sockets sammen med RMI, er det imidlertid en god id\u00e9 at se p\u00e5, hvordan man opretter en brugerdefineret socket.<\/p>\n<p><script type=\"text\/javascript\"><!--\ngoogle_ad_client = \"ca-pub-3400894606969585\";\n\/* Bibliotek - ajp *\/\ngoogle_ad_slot = \"3560879290\";\ngoogle_ad_width = 336;\ngoogle_ad_height = 280;\n\/\/-->\n<\/script><br \/>\n<script type=\"text\/javascript\"\nsrc=\"http:\/\/pagead2.googlesyndication.com\/pagead\/show_ads.js\">\n<\/script><br \/>\n&nbsp;<\/p>\n<h2>Oprettelse af en brugerdefineret socket<\/h2>\n<p>&nbsp;<\/p>\n<p>N\u00e5r man vil behandle data f\u00f8r man transporterer over netv\u00e6rket, og\/eller efter man har modtaget dem, kan man g\u00f8re det p\u00e5 to m\u00e5der. Man kan enten bruge en almindelig Java socket (java.net.Socket) og behandle dataene i selve applikationen, eller man kan definere sin egen socket, der foretager behandlingen. I dette afsnit vil den sidste fremgangsm\u00e5de blive gennemg\u00e5et.<\/p>\n<p>&nbsp;<\/p>\n<p>Overordnet kan den proces at oprette en socket opdeles i fire trin:<\/p>\n<p>&nbsp;<\/p>\n<ol>\n<li>Opret en klasse, der nedarver fra fra en output stream-klasse (for eksempel java.io.FilterOutputStream) for at give socket\u2019en en stream til output. Redefin\u00e9r de metoder, der ikke kan bruges, som de er.<\/li>\n<li>Opret en klasse, der nedarver fra en input stream-klasse (for eksempel java.io.FilterInputStream) for at give socket\u2019en en stream til input. Redefin\u00e9r de metoder, der ikke kan bruges, som de er.<\/li>\n<li>Opret en klasse, der nedarver fra java.net.Socket. Implement\u00e9r de n\u00f8dvendige constructors, og redefin\u00e9r getInputStream, getOutputStream og close.<\/li>\n<li>Opret en klasse, der nedarver fra java.net.ServerSocket. Implement\u00e9r constructoren og redefin\u00e9r accept-metoden, s\u00e5 den opretter en socket af den rigtige type.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>Hvilke output og input stream-klasser man v\u00e6lger at nedarve fra, afh\u00e6nger af den applikation man programmerer, og den type kommunikation, socketen skal bruges til.<\/p>\n<p>&nbsp;<\/p>\n<p>For at lette gennemgangen af de ovenn\u00e6vnte punkter vil der blive taget udgangspunkt i et praktisk eksempel, hvor der bliver oprettet en socket til h\u00e5ndtering af krypteret kommunikation. Selve krypteringen er ikke medtaget i dette eksempel af hensyn til overskueligheden. I stedet kaldes funktionerne encrypt og decrypt p\u00e5 klassen Encryption. I en virkelig applikation skulle denne klasse foretage henholdvis kryptering og dekryptering. I dette eksempel er den blot defineret s\u00e5ledes:<\/p>\n<p>&nbsp;<\/p>\n<p>public class Encryption<\/p>\n<p>{<\/p>\n<p>public static int encrypt (int b)<\/p>\n<p>{<\/p>\n<p>\/\/ kryptering foreg\u00e5r her<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>return b;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public static int decrypt (int b)<\/p>\n<p>{<\/p>\n<p>\/\/ dekryptering foreg\u00e5r her<\/p>\n<p>&nbsp;<\/p>\n<p>return b;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Herefter er det muligt at definere en output stream, der skriver krypteret data. Denne defineres s\u00e5ledes:<\/p>\n<p>&nbsp;<\/p>\n<p>import java.io.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class EncryptionOutputStream extends FilterOutputStream<\/p>\n<p>{<\/p>\n<p>public EncryptionOutputStream (OutputStream os)<\/p>\n<p>{<\/p>\n<p>super (os);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void write(int b) throws IOException<\/p>\n<p>{<\/p>\n<p>out.write (Encryption.encrypt(b));<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public void write(byte b[], int off, int len) throws IOException<\/p>\n<p>{<\/p>\n<p>for (int i = 0; i &lt; len; i++)<\/p>\n<p>out.write (Encryption.encrypt(b[off + i]));<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses, nedarver klassen fra java.io.FilterOutputStream. At det netop skulle v\u00e6re FilterOutputStream og ikke en anden output stream, der skulle nedarves fra, er et valg foretaget med den begrundelse, at FilterOutputStream passer bedst til den kommunikation, socketen skal bruges til.<\/p>\n<p>&nbsp;<\/p>\n<p>For at kunne kryptere dataene bliver FilterOutputStream\u2019s metoder til at skrive data redefineret. Det drejer sig om de to write-metoder. Den f\u00f8rste metode, public void write(int b), krypterer og skriver en enkelt byte. Den anden metode, public void write(byte b[], int off, int len), skriver en r\u00e6kke krypterede bytes fra arrayet b. Antallet af bytes, der skal skrives, afg\u00f8res af len-variablen, mens off angiver, positionen p\u00e5 den f\u00f8rste byte i arrayet, der skal skrives.<\/p>\n<p>Nu hvor output stream\u2019en er p\u00e5 plads, er det p\u00e5 tide at f\u00e5 lavet en input stream til socket\u2019en. I tr\u00e5d med output stream\u2019en er det valgt at bruge en FilterInputStream til at h\u00e5ndtere indl\u00e6sningen af data. Det betyder, at EncryptionInputStream kan skrives som f\u00f8lger:<\/p>\n<p>import java.io.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class EncryptionInputStream extends FilterInputStream<\/p>\n<p>{<\/p>\n<p>public EncryptionInputStream (InputStream is)<\/p>\n<p>{<\/p>\n<p>super (is);<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public int read () throws IOException<\/p>\n<p>{<\/p>\n<p>return (Encryption.decrypt(in.read()));<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public int read (byte b[], int off, int len) throws IOException<\/p>\n<p>{<\/p>\n<p>if (len &lt;= 0)<\/p>\n<p>return 0;<\/p>\n<p>&nbsp;<\/p>\n<p>int i;<\/p>\n<p>int bytesRead;<\/p>\n<p>&nbsp;<\/p>\n<p>for (bytesRead = 0; bytesRead &lt; len &amp;&amp; in.available() &gt; 0; bytesRead++)<\/p>\n<p>{<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>i = read();<\/p>\n<p>}<\/p>\n<p>catch (EOFException eof)<\/p>\n<p>{<\/p>\n<p>return bytesRead;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>b[off + bytesRead] = (byte) Encryption.decrypt(i);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>return bytesRead;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Ligesom i EncryptionOutputStream bliver der redefineret to metoder. Her drejer det sig blot om read i stedet for write.<\/p>\n<p>&nbsp;<\/p>\n<p>Den f\u00f8rste read-funktion bruges til at l\u00e6se en enkelt int fra input stream\u2019en. Den anden l\u00e6ser flere bytes efter samme system som den write-metode, der skrev flere bytes. Denne read-metode returnerer antallet af bytes, der bliver indl\u00e6st. Dette antal kan v\u00e6re forskellig fra len, hvis der opst\u00e5r en EOFException, hvilket sker, hvis der ikke er tilstr\u00e6kkeligt data i stream\u2019en, eller hvis der ikke kan l\u00e6ses flere data fra stream\u2019en uden at blokere \u2013 det vil sige, at stream\u2019en er tom. Man kan se, hvor mange data, der er tilg\u00e6ngelige i stream\u2019en, ved at kalde available-funktionen.<\/p>\n<p>&nbsp;<\/p>\n<p>Nu hvor man har defineret b\u00e5de en output stream og en input stream, kan man definere selve socketen. Denne kaldes i dette eksempel EncryptionSocket:<\/p>\n<p>&nbsp;<\/p>\n<p>import java.io.*;<\/p>\n<p>import java.net.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class EncryptionSocket extends Socket<\/p>\n<p>{<\/p>\n<p>private InputStream in;<\/p>\n<p>&nbsp;<\/p>\n<p>private OutputStream out;<\/p>\n<p>&nbsp;<\/p>\n<p>public EncryptionSocket ()<\/p>\n<p>{<\/p>\n<p>super ();<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public EncryptionSocket (String host, int port) throws IOException<\/p>\n<p>{<\/p>\n<p>super (host, port);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public InputStream getInputStream () throws IOException<\/p>\n<p>{<\/p>\n<p>if (in == null)<\/p>\n<p>in = new EncryptionInputStream(super.getInputStream());<\/p>\n<p>&nbsp;<\/p>\n<p>return in;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public OutputStream getOutputStream () throws IOException<\/p>\n<p>{<\/p>\n<p>if (out == null)<\/p>\n<p>out = new EncryptionOutputStream(super.getOutputStream());<\/p>\n<p>&nbsp;<\/p>\n<p>return out;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public synchronized void close () throws IOException<\/p>\n<p>{<\/p>\n<p>OutputStream o = getOutputStream();<\/p>\n<p>o.flush();<\/p>\n<p>super.close();<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>EncryptionSocket nedarver fra java.net.Socket. For at sikre at man kan bruge denne socket ligesom alle andre sockets \u2013 og i s\u00e6rdeleshed ligesom java.net.Socket \u2013 m\u00e5 man redifinere metoderne getInputStream og getOutputStream, s\u00e5 disse ikke blot returnerer en almindelig input og output stream, men henholdsvis EncryptionInputStream og EncryptionOutputStream.<\/p>\n<p>&nbsp;<\/p>\n<p>B\u00e5de getInputStream og getOutputStream kontrollerer, om der i forvejen eksisterer en input stream eller output stream. Er dette tilf\u00e6ldet, returneres denne. Eksisterer der ikke i forvejen en stream, bliver der oprettet en, som derefter returneres.<\/p>\n<p>&nbsp;<\/p>\n<p>Den sidste metode i EncryptionSocket er close. Denne metode s\u00f8rger for at t\u00f8mme output streamen og kalder s\u00e5 videre til superklassens close-metode.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>Det sidste, der skal g\u00f8res, f\u00f8r den brugerdefinerede socket er klar til brug, er at definere en ServerSocket. Denne kaldes EncryptionServerSocket, og definitionen af den ses herunder.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.io.*;<\/p>\n<p>import java.net.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class EncryptionServerSocket extends ServerSocket<\/p>\n<p>{<\/p>\n<p>public EncryptionServerSocket (int port) throws IOException<\/p>\n<p>{<\/p>\n<p>super (port);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public Socket accept () throws IOException<\/p>\n<p>{<\/p>\n<p>Socket s = new EncryptionSocket();<\/p>\n<p>implAccept(s);<\/p>\n<p>return s;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Igen skal der redefineres en metode. Denne gang er det accept. I stedet for at returnere en almindelig socket, skal funktionen nu returnere en EncryptionSocket.<\/p>\n<p>&nbsp;<\/p>\n<p>S\u00e5ledes er det muligt at definere sin egen socket. Det skal dog understreges, at dette eksempel er simplere end de fleste, da Encryption-protokollen ligger ovenp\u00e5 TCP\/IP. Det betyder, at protokollen kan bruge de fleste af de metoder, der allerede er defineret i input og output streams og sockets. Havde det v\u00e6ret en helt anden protokol, der skulle have v\u00e6ret implementeret, ville det have kr\u00e6vet en st\u00f8rre m\u00e6ngde programmering og redefinering af de fleste allerede eksisterende metoder.<\/p>\n<h2>En RMISocketFactory, der producerer en enkelt type socket<\/h2>\n<p>&nbsp;<\/p>\n<p>Der er fire trin, der skal gennemg\u00e5s for at kunne oprette en RMISocketFactory, der producerer en enkelt sockettype:<\/p>\n<p>&nbsp;<\/p>\n<ol>\n<li>V\u00e6lg, hvilken type socket, der skal produceres.<\/li>\n<li>Opret en klasse, der nedarver fra klassen RMISocketFactory.<\/li>\n<li>Redefin\u00e9r metoden createSocket.<\/li>\n<li>Redefin\u00e9r metoden createServerSocket.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>Hvilken sockettype man v\u00e6lger at implementere, afh\u00e6nger naturligvis af den applikation, man skriver. Hvis applikationen arbejder med s\u00e6rdeles f\u00f8lsomme data, er det en god id\u00e9 at bruge en socket, der krypterer data, mens applikationer, der arbejder med store datam\u00e6ngder, med fordel vil kunne bruge en socket, der tillader datakomprimering. Man kan enten v\u00e6lge at anvende en socket, man har programmeret tidligere, programmere en ny<br \/>\nsocket specielt til lejligheden eller bruge en socket, der leveres af en tredjepart. I dette eksempel vil der blive arbejdet videre med den EncryptionSocket, der blev defineret i det foreg\u00e5ende afsnit.<\/p>\n<p>&nbsp;<\/p>\n<p>N\u00e5r socketvalget er foretaget skal der oprettes en klasse, der nedarver fra RMISocketFactory. I dette tilf\u00e6lde kaldes den EncryptionRMISocketFactory. Denne er defineret som f\u00f8lger.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.io.*;<\/p>\n<p>import java.net.*;<\/p>\n<p>import java.rmi.server.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class EncryptionRMISocketFactory extends RMISocketFactory<\/p>\n<p>{<\/p>\n<p>public Socket createSocket (String host, int port) throws IOException<\/p>\n<p>{<\/p>\n<p>EncryptionSocket socket = new EncryptionSocket (host, port);<\/p>\n<p>return socket;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public ServerSocket createServerSocket (int port) throws IOException<\/p>\n<p>{<\/p>\n<p>EncryptionServerSocket serverSocket = new EncryptionServerSocket (port)<\/p>\n<p>return serverSocket;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses, er ogs\u00e5 skridt 3 og 4 foretaget, idet createSocket og createServerSocket er blevet redefineret. Form\u00e5let med en RMIFactory er, at den skal forsyne RMI-systemet med sockets, n\u00e5r programmet afvikles. Derfor er det n\u00f8dvendigt at redefinere createSocket-metoden. Ogs\u00e5 createServerSocket skal redefineres, s\u00e5 ogs\u00e5 serverdelen af en RMI-kommunikation kan benytte den korrekte socket.<\/p>\n<p>&nbsp;<\/p>\n<p>Nu hvor det er blevet gennemg\u00e5et, hvordan man opretter en RMISocketFactory, der kan producere en enkelt type af sockets, kan man g\u00e5 videre med at se p\u00e5, hvordan man laver en RMISocketFactory, der kan producere flere forskellige sockets. Det vil blive gennemg\u00e5et i det n\u00e6ste afsnit.<\/p>\n<p>&nbsp;<\/p>\n<h2>En RMISocketFactory, der producerer flere forskellige typer sockets<\/h2>\n<p>&nbsp;<\/p>\n<p>At oprette en RMISocketFactory, der producerer flere forskellige typer sockets, minder meget om at oprette en RMISocketFactory, der blot producerer \u00e9n type sockets. Man beh\u00f8ver kun en smule mere information.<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis en RMISocketFactory kan producere mere end \u00e9n type sockets, skal<\/p>\n<p>&nbsp;<\/p>\n<p>den have en m\u00e5de at angive, hvilken type der skal produceres. Udover de metoder, der blev brugt i den foreg\u00e5ende RMISocketFactory, nemlig<\/p>\n<p>&nbsp;<\/p>\n<p>public Socket createSocket (String host, int port) og<\/p>\n<p>public ServerSocket createServerSocket (int port)<\/p>\n<p>&nbsp;<\/p>\n<p>definerer RMISocketFactory ogs\u00e5 to metoder, der g\u00f8r det muligt at angive, hvilken type socket, der skal produceres. Disse har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public Socket createSocket (String host, int port, SocketType type)<br \/>\npublic ServerSocket createServerSocket (int port, SocketType type)<\/p>\n<p>&nbsp;<\/p>\n<p>Som det ses, er forskellen p\u00e5 disse to og de, der blev anvendt i det forrige eksempel, at de to nye metoder, hver har en parameter, der angiver hvilken type socket, der skal oprettes. Klassen SocketType har tre variable:<\/p>\n<p>&nbsp;<\/p>\n<p>private String protocol<br \/>\nprivate byte[] refData<br \/>\nprivate Object serverData<\/p>\n<p>&nbsp;<\/p>\n<p>Disse tre elementer s\u00e6ttes af klassens constructor. Elementet protocol angiver den protokol, der anvendes i kommunikationen. De to \u00f8vrige elementer kan bruges til at opbevare data, der kan v\u00e6re n\u00f8dvendige for protokollen.<\/p>\n<p>&nbsp;<\/p>\n<p>Med denne viden i bagh\u00e5nden kan man bruge fremgangsm\u00e5den med de 4 trin, der blev skitseret i forrige afsnit.<\/p>\n<p>&nbsp;<\/p>\n<p>F\u00f8rst v\u00e6lges, hvilke sockets der skal kunne produceres. For eksemplets skyld v\u00e6lges her EncryptionSocket og java.net.Socket. Den RMISocketFactory, der skal oprette en eller flere af disse to sockets, kaldes DualRMISocketFactory.<\/p>\n<p>&nbsp;<\/p>\n<p>import java.io.*;<\/p>\n<p>import java.net.*;<\/p>\n<p>import java.rmi.server.*;<\/p>\n<p>&nbsp;<\/p>\n<p>public class DualRMISocketFactory extends RMISocketFactory<\/p>\n<p>{<\/p>\n<p>private RMISocketFactory defaultFactory = RMISocketFactory.getDefaultSocketFactory();<\/p>\n<p>&nbsp;<\/p>\n<p>public Socket createSocket(String host, int port) throws IOException<\/p>\n<p>{<\/p>\n<p>return defaultFactory.createSocket (host, port);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public ServerSocket createServerSocket (int port) throws IOException<\/p>\n<p>{<\/p>\n<p>return defaultFactory.createServerSocket (port);<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public Socket createSocket (String host, int port, SocketType type) throws IOException<\/p>\n<p>&nbsp;<\/p>\n<p>{<\/p>\n<p>Socket socket;<\/p>\n<p>String protocol = type.getProtocol();<\/p>\n<p>if (protocol.equalsIgnoreCase(&#8220;encryption&#8221;))<\/p>\n<p>socket = new EncryptionSocket (host, port);<\/p>\n<p>else<\/p>\n<p>if (protocol.equalsIgnoreCase(&#8220;java&#8221;))<\/p>\n<p>socket = new Socket (host, port);<\/p>\n<p>else<\/p>\n<p>throw new ProtocolException (&#8220;Protocol not supported : &#8221; + protocol);<\/p>\n<p>&nbsp;<\/p>\n<p>return socket;<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>public ServerSocket createServerSocket (int port, SocketType type) throws IOException<\/p>\n<p>{<\/p>\n<p>ServerSocket socket;<\/p>\n<p>String protocol = type.getProtocol();<\/p>\n<p>if (protocol.equalsIgnoreCase(&#8220;encryption&#8221;))<\/p>\n<p>socket = new EncryptionServerSocket (port);<\/p>\n<p>else<\/p>\n<p>if (protocol.equalsIgnoreCase(&#8220;java&#8221;))<\/p>\n<p>socket = new ServerSocket (port);<\/p>\n<p>else<\/p>\n<p>throw new ProtocolException (&#8220;Protocol not supported : &#8221; + protocol);<\/p>\n<p>&nbsp;<\/p>\n<p>return socket;<\/p>\n<p>}<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Sammenlignet med EncryptionRMISocketFactory er der en del forskelle. Begge implementerer metoderne public Socket createSocket(String host, int port) og public ServerSocket createServerSocket (int port). DualRMISocketFactory sender imidlertid blot disse metodekald videre til den almindelige RMISocketFactory, da der ikke sendes nogen SocketType med, og da det derfor ikke er muligt at bestemme, hvilken type socket, der skal oprettes. Derudover definerer DualRMISocketFactory metoderne public Socket createSocket (String host, int port, SocketType type) og public ServerSocket createServerSocket (int port, SocketType type). Disse indeholder en parameter, der g\u00f8r det muligt for klassen at se, hvilken type socket, der skal oprettes. Dette g\u00f8res p\u00e5 baggrund af SocketType\u2019s protocol-element. Hvis v\u00e6rdien af dette er \u201dencryption\u201d oprettes en EncryptionSocket, mens en streng med v\u00e6rdien \u201djava\u201d vil oprette en java.net.Socket. Er v\u00e6rdien noget andet end dette, opst\u00e5r en exception.<\/p>\n<p>&nbsp;<\/p>\n<p><strong><em><br \/>\n<\/em><\/strong><\/p>\n<h2>Brug af en brugerdefineret socket i en applikation<\/h2>\n<p>&nbsp;<\/p>\n<p>Det nytter ikke meget at kunne definere smarte RMISocketFactory-klasser, hvis man ikke kan bruge dem i sine applikationer. Derfor vil det i dette afsnit blive forklaret, hvordan man s\u00e6tter RMI-systemet til at bruge brugerdefinerede sockets.<\/p>\n<p>&nbsp;<\/p>\n<p>At implementere brugerdefinerede sockets i en applikation er en proces, der kan deles op i to trin:<\/p>\n<p>&nbsp;<\/p>\n<ol>\n<li>S\u00e6t RMI-factory i b\u00e5de klienten og serveren til den brugerdefinerede factory.<\/li>\n<li>Implement\u00e9r en constructor i serverklassen, der kalder UnicastRemoteObject\u00a0med SocketType-parametren.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>Trin 2 er kun n\u00f8dvendig, hvis man implementerer en factory, der kan producere flere forskellige typer sockets.<\/p>\n<p>&nbsp;<\/p>\n<p>For at s\u00e6tte RMI-factory i klient og server, skal der bruges en statisk metode, som er en del af RMISocketFactory-klassen. Denne metode har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>public synchronized static void setSocketFactory(RMISocketFactory fac)<\/p>\n<p>&nbsp;<\/p>\n<p>Den kode, der s\u00e6tter RMI-factory og som skal implementeres p\u00e5 s\u00e5vel klient- som serverside, kan se s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>try<\/p>\n<p>{<\/p>\n<p>RMISocketFactory.setSocketFactory(new DualRMISocketFactory());<\/p>\n<p>}<\/p>\n<p>catch (IOException e)<\/p>\n<p>{<\/p>\n<p>\/\/ h\u00e5ndter fejl<\/p>\n<p>}<\/p>\n<p>&nbsp;<\/p>\n<p>Hvis den RMISocketFactory, man anvender, kan producere flere forskellige sockettyper, er det n\u00f8dvendigt at angive, hvilken der bruges. Dette skal kun g\u00f8res p\u00e5 serveren, og hvis denne nedarver fra UnicastRemoteObject kan det g\u00f8res rimeligt nemt. UnicastRemoteObject har nemlig en protected constructor, hvor man blandt andet kan angive, hvilken SocketType, der anvendes. Denne constructor har f\u00f8lgende signatur:<\/p>\n<p>&nbsp;<\/p>\n<p>protected UnicastRemoteObject(int port, SocketType socketType)<\/p>\n<p>&nbsp;<\/p>\n<p>I constructoren i server-klassen kan kaldet af denne constructor, der alts\u00e5 findes i superklassen, se s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>super (0, new SocketType (\u201dencryption\u201d, null, null));<\/p>\n<p>&nbsp;<\/p>\n<p><strong><br \/>\n<\/strong><\/p>\n<h1>Sammenfatning<\/h1>\n<p>&nbsp;<\/p>\n<p>I dette kapitel er det blevet gennemg\u00e5et, hvordan man kan skrive et distribueret system ved hj\u00e6lp af Remote Method Invocation (RMI). Det er blevet forklaret, hvordan de forskellige klasser skal fordeles. Derudover er det blevet vist, hvordan man kan anvende mere avancerede emner indenfor RMI. Det drejer sig blandt andet om anvendelsen af en nameserver og programmereing af egne sockets.<\/p>\n<p>&nbsp;<\/p>\n<h1>FAQ<\/h1>\n<h3>Kan man bruge RMI fra applets?<\/h3>\n<p>&nbsp;<\/p>\n<p>Ja. RMI er den letteste m\u00e5de at foretage netv\u00e6rkskommunikation fra en applet p\u00e5. Man skal blot huske, at Javas sikkerheds\u00adpolitik kun tillader, at man foretager RMI-kald til den compu\u00adter, appleten blev indl\u00e6st fra. Man finder adressen p\u00e5 den computer ved at foretage kaldet getCodeBase().getHost(). Det vil sige, at en typisk s\u00f8gning i Naming-servicen kommer til at se s\u00e5ledes ud:<\/p>\n<p>&nbsp;<\/p>\n<p>RemoteInterface obj = (RemoteInterface) Naming.lookup (&#8220;\/\/&#8221; + getCodeBase().getHost() + &#8220;\/Remote&#8221;);<\/p>\n<p>&nbsp;<\/p>\n<h3>Prorammet siger, at den ikke kan finde en computer med ser\u00adverens navn, men navnet er rigtigt. Hvad er der galt?<\/h3>\n<p>&nbsp;<\/p>\n<p>Hvis man fors\u00f8ger at f\u00e5 kontakt med en computer p\u00e5 et andet netv\u00e6rk &#8211; for eksempel via Internet &#8211; kan det v\u00e6re n\u00f8dvendigt at angive computerens IP-nummer i stedet for dens navn. Det samme er tilf\u00e6ldet, hvis den bruger, der er logget ind p\u00e5 servermaskinen, ikke er logget ind p\u00e5 netv\u00e6rket.<\/p>\n<p>&nbsp;<\/p>\n<h3>Kan man k\u00f8re to registreringsservices p\u00e5 samme computer?<\/h3>\n<p>&nbsp;<\/p>\n<p>Ja. Hvis man har brug for at stille forskellige objekter til r\u00e5dighed for forskellige servere, kan man med fordel v\u00e6lge at k\u00f8re to registreringsservices i stedet for blot en enkelt. Det g\u00f8res ved at s\u00e6tte dem til at bruge hver sin port. N\u00e5r registreringsservicen startes, angives portnummeret som parameter. De programmer, der skal bruge registreringsservicen, skal angive portnummeret efter navnet p\u00e5 serveren. For eksempel vil \/\/void-main:1234\/Math angive, at registreringsservices p\u00e5 port 1234 p\u00e5 computeren void-main skal bruges. Angives intet portnummer bruges port 1099.<\/p>\n<h3>Kan RMI bruges fra JDK 1.0.2?<\/h3>\n<p>&nbsp;<\/p>\n<p>RMI kan godt bruges fra JDK 1.0.2, men den er ikke en integreret del. Det vil sige, at man er n\u00f8dt til at downloade yderligere klassefiler fra Javasofts hjemmeside. Man skal v\u00e6re opm\u00e6rksom p\u00e5, at visse funktionskald har andre navne, end de, der er gennemg\u00e5et her i bogen.<\/p>\n<p><strong><em><br \/>\n<\/em><\/strong><\/p>\n<h3>Hvorfor f\u00e5r jeg fejlen \u201djava.lang.ClassMismatchError\u201d, n\u00e5r jeg pr\u00f8ver at k\u00f8re mit program?<\/h3>\n<p>&nbsp;<\/p>\n<p>Sandsynligvis er en eller flere af klassefilerne blevet \u00e6ndret. For at \u00e6ndringen kan tr\u00e6de i kraft er det n\u00f8dvendigt at genstarte alle programmer, der stiller RMI-objekter til r\u00e5dighed for klienter. Ogs\u00e5 selve registreringsservices skal genstartes.<\/p>\n<p>&nbsp;<\/p>\n<h3>Er det muligt at se, om et objekt stadig bliver brugt af klienter?<\/h3>\n<p>&nbsp;<\/p>\n<p>Ja. Ved at lade objektet implementere interfacet java.rmi.server.Unreferenced f\u00e5r objektet signaturen til en metoden void unreferenced(), der automatisk bliver kaldt, n\u00e5r der ikke l\u00e6ngere er flere referencer til objektet. Hvis objektet er bundet i registreringsservices, vil denne funktion aldrig blive udf\u00f8rt, fordi registreringsservicen i s\u00e5 fald altid vil have en reference til den.<\/p>\n<h3>Er det fra serveren muligt at se, hvilken computer klientprogrammet k\u00f8rer p\u00e5?<\/h3>\n<p>&nbsp;<\/p>\n<p>Ja. Det kan lade sig g\u00f8re ved hj\u00e6lp af funktionen java.rmi.server.RemoteServer.getClientHost(). Funktionen er defineret som<\/p>\n<p>&nbsp;<\/p>\n<p>public static <a href=\"java.lang.String.html\">String<\/a> getClientHost() throws <a href=\"java.rmi.server.ServerNotActiveException.html\">ServerNotActiveException<\/a><\/p>\n<p>&nbsp;<\/p>\n<p>ServerNotActiveException opst\u00e5r, hvis funktionen bliver kaldt i en funktion, der ikke er en del af RMI.<\/p>\n<p>&nbsp;<\/p>\n<div style=\"border: 1px solid red; color: red\">\n<b>Du l\u00e6ser en gammel bog<\/b><br \/>\nDen bog, du l\u00e6ser her, er fra 1998, og mange ting kan have \u00e6ndret sig siden da.<br \/>\nVi h\u00e5ber, at du stadig kan finde relevant information i den.<br \/>\nHvis du vil l\u00e6se aktuelle oplysninger om de avancerede dele af Java, anbefaler vi<br \/>\nbogen <a href=\"http:\/\/clk.tradedoubler.com\/click?p(197229)a(2029446)g(19172914)url(http:\/\/www.adlibris.com\/dk\/product.aspx?isbn=0132354799)\"  target=\"_blank\">Core Java &#8211; Advanced Features<\/a><img decoding=\"async\" src=\"http:\/\/impdk.tradedoubler.com\/imp?type(inv)g(19172914)a(2029446)\" \/>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Det er relativt nemt at f\u00e5 programmer p\u00e5 to computere forbundet i netv\u00e6rk til at kommunikere ved hj\u00e6lp af Java. Det er alts\u00e5 ikke at etablere kommunikationen, der er kompliceret B det er at bruge den fornuftigt. Samtale er ikke meget v\u00e6rd, hvis ikke begge parter snakker et sprog, den anden kan forst\u00e5. Det betyder, [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":332,"menu_order":60,"comment_status":"closed","ping_status":"open","template":"","meta":{"_monsterinsights_skip_tracking":false,"footnotes":""},"class_list":["post-399","page","type-page","status-publish","hentry"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"keywords\" content=\"java,programmering,rmi,remote method invocation,corba,kommunikation,klient,server,name server,navneserver,nameserver\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"da_DK\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Biblioteket | Samling af artikler og b\u00f8ger\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Remote Method Invocation (RMI) | Biblioteket\" \/>\n\t\t<meta property=\"og:description\" content=\"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2011-11-03T12:36:19+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2011-11-03T12:42:05+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Remote Method Invocation (RMI) | Biblioteket\" \/>\n\t\t<meta name=\"twitter:description\" content=\"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-6-remote-method-invocation-rmi\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/#listItem\",\"name\":\"B\\u00f8ger\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/#listItem\",\"position\":2,\"name\":\"B\\u00f8ger\",\"item\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/#listItem\",\"name\":\"Avanceret Java-programmering\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/#listItem\",\"position\":3,\"name\":\"Avanceret Java-programmering\",\"item\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-6-remote-method-invocation-rmi\\\/#listItem\",\"name\":\"Kapitel 6 Remote Method Invocation (RMI)\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/#listItem\",\"name\":\"B\\u00f8ger\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-6-remote-method-invocation-rmi\\\/#listItem\",\"position\":4,\"name\":\"Kapitel 6 Remote Method Invocation (RMI)\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/#listItem\",\"name\":\"Avanceret Java-programmering\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#organization\",\"name\":\"Biblioteket\",\"description\":\"Samling af artikler og b\\u00f8ger\",\"url\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-6-remote-method-invocation-rmi\\\/#webpage\",\"url\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-6-remote-method-invocation-rmi\\\/\",\"name\":\"Remote Method Invocation (RMI) | Biblioteket\",\"description\":\"S\\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.\",\"inLanguage\":\"da-DK\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/b%c3%b8ger\\\/avanceret-java-programmering\\\/kapitel-6-remote-method-invocation-rmi\\\/#breadcrumblist\"},\"datePublished\":\"2011-11-03T14:36:19+02:00\",\"dateModified\":\"2011-11-03T14:42:05+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#website\",\"url\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/\",\"name\":\"Biblioteket\",\"description\":\"Samling af artikler og b\\u00f8ger\",\"inLanguage\":\"da-DK\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.kristiansborg.dk\\\/bibliotek\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Remote Method Invocation (RMI) | Biblioteket","description":"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.","canonical_url":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/","robots":"max-image-preview:large","keywords":"java,programmering,rmi,remote method invocation,corba,kommunikation,klient,server,name server,navneserver,nameserver","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BreadcrumbList","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek#listItem","position":1,"name":"Home","item":"https:\/\/www.kristiansborg.dk\/bibliotek","nextItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/#listItem","name":"B\u00f8ger"}},{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/#listItem","position":2,"name":"B\u00f8ger","item":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/#listItem","name":"Avanceret Java-programmering"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/#listItem","position":3,"name":"Avanceret Java-programmering","item":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/#listItem","name":"Kapitel 6 Remote Method Invocation (RMI)"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/#listItem","name":"B\u00f8ger"}},{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/#listItem","position":4,"name":"Kapitel 6 Remote Method Invocation (RMI)","previousItem":{"@type":"ListItem","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/#listItem","name":"Avanceret Java-programmering"}}]},{"@type":"Organization","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#organization","name":"Biblioteket","description":"Samling af artikler og b\u00f8ger","url":"https:\/\/www.kristiansborg.dk\/bibliotek\/"},{"@type":"WebPage","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/#webpage","url":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/","name":"Remote Method Invocation (RMI) | Biblioteket","description":"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.","inLanguage":"da-DK","isPartOf":{"@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#website"},"breadcrumb":{"@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/#breadcrumblist"},"datePublished":"2011-11-03T14:36:19+02:00","dateModified":"2011-11-03T14:42:05+02:00"},{"@type":"WebSite","@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#website","url":"https:\/\/www.kristiansborg.dk\/bibliotek\/","name":"Biblioteket","description":"Samling af artikler og b\u00f8ger","inLanguage":"da-DK","publisher":{"@id":"https:\/\/www.kristiansborg.dk\/bibliotek\/#organization"}}]},"og:locale":"da_DK","og:site_name":"Biblioteket | Samling af artikler og b\u00f8ger","og:type":"article","og:title":"Remote Method Invocation (RMI) | Biblioteket","og:description":"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.","og:url":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/","article:published_time":"2011-11-03T12:36:19+00:00","article:modified_time":"2011-11-03T12:42:05+00:00","twitter:card":"summary","twitter:title":"Remote Method Invocation (RMI) | Biblioteket","twitter:description":"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer."},"aioseo_meta_data":{"post_id":"399","title":"Remote Method Invocation (RMI) | #site_title","description":"S\u00e5dan bruges Javas egen protokol RMI (Remote Method Invocation) til at kommunikere mellem forskellige programmer.","keywords":[{"label":"Java","value":"Java"},{"label":"programmering","value":"programmering"},{"label":"RMI","value":"RMI"},{"label":"Remote Method Invocation","value":"Remote Method Invocation"},{"label":"CORBA","value":"CORBA"},{"label":"kommunikation","value":"kommunikation"},{"label":"klient","value":"klient"},{"label":"server","value":"server"},{"label":"name server","value":"name server"},{"label":"navneserver","value":"navneserver"},{"label":"nameserver","value":"nameserver"}],"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2020-12-21 06:59:15","updated":"2025-06-04 02:17:13","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.kristiansborg.dk\/bibliotek\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/\" title=\"B\u00f8ger\">B\u00f8ger<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/\" title=\"Avanceret Java-programmering\">Avanceret Java-programmering<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tKapitel 6 Remote Method Invocation (RMI)\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.kristiansborg.dk\/bibliotek"},{"label":"B\u00f8ger","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/"},{"label":"Avanceret Java-programmering","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/"},{"label":"Kapitel 6 Remote Method Invocation (RMI)","link":"https:\/\/www.kristiansborg.dk\/bibliotek\/b%c3%b8ger\/avanceret-java-programmering\/kapitel-6-remote-method-invocation-rmi\/"}],"_links":{"self":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/399","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/comments?post=399"}],"version-history":[{"count":5,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/399\/revisions"}],"predecessor-version":[{"id":401,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/399\/revisions\/401"}],"up":[{"embeddable":true,"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/pages\/332"}],"wp:attachment":[{"href":"https:\/\/www.kristiansborg.dk\/bibliotek\/wp-json\/wp\/v2\/media?parent=399"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}