Netværksprogrammering

Tags:    java
Skrevet af Bruger #1425 @ 28.03.2005

Introduktion:


Denne artikel vil omhandle netværksprogrammering i Java via to protokoller, TCP og UDP. Til dem der ikke ved hvad TCP og UDP præcis er for nogle protokoller er der et kort afsnit om hvert af dem nedenunder.
Artiklens gennemgående eksempel vil være en echo-server samt en klient. En echo-server er et meget simpelt program der blot modtager data og sender det samme data tilbage til afsenderen.
Eksempel koden kan hentes på http://udvikleren.dk/articlefiles/java-networking.zip

User Datagram Protocol (UDP):


UDP er en forbindelsesløs og såkaldt upålidelig protokol, hvilket vil sige at man reelt sender pakker i blinde uden at være klar over om modtageren eksisterer eller modtager de pakker man sender.
Der er ej heller nogen garanti for at de sendte pakker vil ankomme i samme rækkefølge på modtager siden.
En pakke er en klump data på maksimalt 64kb.

UDP anvendes ofte hvor der skal udveksles mindre datamængder ret hurtigt. Et eksempel er i forbindelse med DNS (Domain Name Service er det system der kan lave værtsnavne som Udvikleren.dk om til en IP adresse.)


Transmission Control Protocol (TCP):



TCP er en forbindelses orienteret og pålidelig protokol, hvilket vil sige at der skal oprettes en forbindelse før data kan sendes mellem to processer. En oprettelse foregår ved et 3-vejs håndtryk, der groft kan beskrives således: Process A vil oprette forbindelse til process B. Process A sender en pakke til B der siger ”Jeg vil gerne oprette en forbindelse”, hvortil B svarer ”Okay, det må du gerne”, til dette svarer A ”Godt, så kan vi tale sammen”.
Herefter kan både A og B sende data til hinanden.

Data sendes som ens strøm af tegn fra afsender til modtager. Det er garanteret at alle tegn når frem, og i samme rækkefølge som de blev sendt.

TCP anvendes ofte hvor der skal overføres større datamængder, så som en browser-session eller anden filoverførsel.



Enkelt trådet echo server



For at vi kan acceptere indkomne forbindelser skal vi lave en instans af ServerSocket. Porten der skal lyttes på angives i konstruktoren.

Eksempel 1:
Fold kodeboks ind/udKode 

Dette vil dog ikke virke da konstruktoren kaster IOException’s som vi er nødt til at fange. I mange tilfælde vil det være en god idé at fange en subtype af IOException først, og derefter fange resten af de IOExceptions der måtte blive kastet. I eksempel 1.1 fanger vi BindException’s så vi kan fortælle brugeren hvorfor vores ServerSocket ikke kunne oprettes.

Eksempel 2:
Fold kodeboks ind/udKode 


Så er vi klar til at acceptere nye forbindeler! For at acceptere en forbindelse fra en klient skal vi kalde ServerSocket’s accept() metode, der returnerer en Socket for den nye forbindelse. Dette kan igen kaste IOExceptions, men gør det kun hvis ServerSocketen er lukket eller ikke lytter på en port.

Eksempel 3:
Fold kodeboks ind/udKode 


Det vil unægteligt være meget rart at vide hvem en Socket er forbundet til, og det kan vi finde ud af ved hjælp af to metoder på Socket klassen:
Socket.getPort() – Returnerer fjernværtens port som en int.
Socket.getInetAddress – Returnerer fjernværtens adresse som en InetAddress
For at få adressen som en String kan InetAdress’ getHostAddress() metode bruges.

Som nævnt i indledningen foregår TCP kommunikation ved hjælp af data strømme. Derfor skal vi bruge en Socket’s tilhørende InputStream og OutputStream for at læse og skrive. Disse to streams kan tilgås via. getInputStream() og getOutputStream() på Socket. Begge metoder kaster IOExceptions som vi selvfølgelig skal fange.

For at læse bruger vi InputStream’s read(byte[] buffer, int off, int len) metode. Vi opretter først et byte-array til at gemme det indkomne data og kalder så metoden med parameterne byte-array, 0, byte-array.length. Metoden vil enten returnere antallet af læste tegn (Dog højst len tegn) eller -1 hvis inputstreamen har nået sin slutning (f.eks. hvis vores forbindelse er blevet afbrudt). Ergo kan vi loope indtil read-metoden returnerer -1, og derefter antage at forbindelse er blevet lukket.

For at skrive bruger vi OntputStream’s write metode. Da vi blot vil lave en echo-server skal vi selvfølgelig bare skrive samme byte-array som vi lige har læst.

Lad os sætte alle tingene sammen til en komplet TCP-echo server:
Eksempel 4:
Fold kodeboks ind/udKode 


Du vil måske bemærke at denne echo-server kun understøtter én klient af gangen. Dette problem råder vi bod på senere i eksempel ????? med en multitrådet echo-server.


Simpel TCP klient:



Når vi skal oprette en forbindelse til en fjernvært skal vi bruge en Socket. Vi angiver værtsnavn samt portnummer som parametre i konstruktoren. Vi er nødt til at fange to typer exceptions, nemlig UnknownHostException der opstår hvis værtnavnets IP adresse ikke kunne findes og IOException hvis der opstod en I/O-fejl.

Eksempel 5:
Fold kodeboks ind/udKode 


Bemærk at vi arbejder med Socket, som vi jo også brugte i server delen. Derfor læser og skriver vi på præcis samme måde.

Vi har dermed alle delene til en fungerende TCP klient, som vi har i eksempel 6.
Idéen er at vi opretter en forbindelse, sender en String og læser et eventuelt svar i klumper af 16kb.

Eksempel 6:
Fold kodeboks ind/udKode 



Multitrådet TCP echo server:


Som lovet løser vi nu et problem i eksempel 4 – nemlig at vi kun understøttede én klient af gangen. Løsningen er at dele koden ud så alle blocking-events for hver socket klares i separate tråde. Tråd klassen kan findes i eksempel 7 og programmet der starter trådene findes i eksempel 8. Tilsammen er der tilføjet én linie kode – for at starte nye tråde:
Fold kodeboks ind/udKode 


Eksempel 7:
Fold kodeboks ind/udKode 


Eksempel 8:
Fold kodeboks ind/udKode 


UDP echo server:


For at vi kan modtage UDP pakker (Datagrammer) skal vi oprette en DatagramSocket og binde den til en bestemt port. Lige som i TCP delen angiver vi porten i konstruktoren. Vi skal også fange en eventuel SocketException.

Eksempel 9:
Fold kodeboks ind/udKode 

Dette eksempel vil oprette en DatagramSocket bundet til port 10000.

Vi skal også have oprettet en byte-buffer til at indeholde data som vil blive indkapslet i en DatagramPacket. Vi lader bufferen være af en størrelse så en hel UDP pakke kan være i den.

Eksempel 10:
Fold kodeboks ind/udKode 


Nu er vi klar til at modtage data, hvilket gores via DatagramSocket’s receive() metode. Som altid er det rart at vide hvem afsenderen er, hvilket vi går i følgende eksempel.

Eksempel 11:
Fold kodeboks ind/udKode 


Så mangler vi blot at sende UDP pakker – hvilket gøres vha. DatagramSocket’s send() metode. Nu har vi alle delene til en UDP Echo server.

Eksempel 12:
Fold kodeboks ind/udKode 



UDP klient:



For at kunne sende UDP pakker skal vi som det første finde en InetAddress for den process vi vil sende pakken til.

Eksempel 13:
Fold kodeboks ind/udKode 

InetAddress.getByName() kaster en UnknownHostException hvis et værtsnavns tilhørende InetAddress ikke kan bestemmes.

Vi skal igen oprette en DatagramSocket som pakker kan sendes igennem. Vi skal ikke angive en destination i denne omgang – det skal i stedet gøres i hver enkelt DatagramPacket.

Eksempel 14:
Fold kodeboks ind/udKode 


For at sende data skal dette selvfølgelig pakkes ind i en DatagramPacket, men først skal det gemmes i et byte-array:

Eksempel 15:
Fold kodeboks ind/udKode 


Så er det tid til at lave en DatagramPacket vi kan sende. Denne DatagramPacket skal indeholde 3 ting: Data, fjernvært og portnummer:

Eksempel 16:
Fold kodeboks ind/udKode 


Sidste skridt er at sende pakken og modtage et eventuelt svar. Disse metoder blev beskrevet i serverdelen. Vi har dermed en komplet UDP klient:

Eksempel 17:
Fold kodeboks ind/udKode 



Konklusion


Det var så det. Jeg håber du har fået noget ud af artiklen :)
Som nævnt i indledningen kan eksempelkoden hentes på http://udvikleren.dk/articlefiles/java-networking.zip


Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (7)

User
Bruger #2137 @ 31.05.05 22:00
meget interesant artikel. Eksemplerne er korte og konkrete.
User
Bruger #7939 @ 04.08.05 17:29
Nice!
jeg har altid godt kunne tænke mig at lave en TelNet-agtig klient i JAVA... Dette her er godt lavet!
User
Bruger #7939 @ 04.08.05 17:29
Nice!
jeg har altid godt kunne tænke mig at lave en TelNet-agtig klient i JAVA... Dette her er godt lavet!
User
Bruger #8363 @ 28.10.05 21:35
Det toh noget tid at læse. Og god læse stof. Og fin forklaring.
User
Bruger #6483 @ 24.02.06 09:27
Denne artikel har helt sikkert kickstartet min forståelse af TCP i java... god artikel!
User
Bruger #13988 @ 10.08.08 18:43
Nice... top fed artikkel
User
Bruger #15301 @ 27.09.10 14:05
God artikel :)
Du skal være logget ind for at skrive en kommentar.
t