Netværksprogrammering 1 - Hostnames og IP adresser

Tags:    c++
Skrevet af Bruger #2695 @ 29.02.2004

Introduktion



Hej alle
Der har været en del spørgsmål i C++ foraet om netværksprogrammering, så jeg har valgt at skrive en række artikler om emnet. Netværksprogrammering er ret komplekst, og der er mange funktioner og strukturer at beskrive, så jeg har valgt at skrive mindst fire artikler:
* Netværksprogrammering 1 - Hostnames og IP adresser
* Netværksprogrammering 2 - TCP client sockets
* Netværksprogrammering 3 - TCP server sockets
* Netværksprogrammering 4 - UDP sockets

Gennem artikelserien vil vi udvikle et sæt af genbrugelige klasser, som kan hjælpe os i vores programmering.
Denne artikel vil beskrive hostnames og IP adresser, samt hvordan man konverterer mellem de forskellige notationer. Gennem artiklen vil vi udvikle en klasse, som indkapsler en internet adresse, og som kan skaffe informationer om adressen fra DNS. Nu er det heldigvis sådan at der er en standard for netværksprogrammering, som kaldes Berkeley Sockets, men desværre har Microsoft valgt ikke at tilbyde et 100% kompatibelt interface til deres socket library. Derfor er vi nødt til at udvikle koden, som jeg har beskrevet i artiklen om multiplatform udvikling, så jeg vil foreslå, at du læser den først. Derudover vil vi bruge exceptions til at indikere fejl, og der vil jeg bygge videre på det exception klasse hieraki, som jeg begyndte i artiklen om multithreading, så den artikel vil jeg også foreslå, at du læser.
Eftersom IPv6 ikke er så brugt endnu, har jeg valgt at koncentrere mig om IPv4 adresser. Måske kommer der en artikel om IPv6 senere. Hvem ved ?
Jeg regner med, at min kode virker med alle compilere, men jeg tester kun med Dev-C++ (altså GCC) under Windows, og GNU's tools under Linux.

IPv4 adresser



Som bekendt består en IP adresse af fire tal i intervallet 0-255. Det er tal som ligger inden for grænserne i en unsigned char og en IP adresse er derfor også repræsenteret af en integer på fire bytes. En sådan værdi er dog meget uhandy at læse for et menneske, så vi opfandt en notations form, som er lidt lettere at gennemskue, kaldet dotted notation (192.168.147.72 f.eks.). Denne notation kan vi godt nok ikke bruge til meget i softwaren, for når vi skal lave netværksforbindelser skal vi bruge den mere kompakte version med de fire bytes. Derfor har de gode Berkeley folk defineret et sæt funktioner til at konvertere mellem notationerne. Under Linux er der:
Fold kodeboks ind/udKode 

inet_ntoa (ntoa står for 'network to ascii') konverterer fra vores fire byte lange adresse til dottet notation og inet_aton (aton står for 'ascii to network') konverterer den anden vej.

Under Windows har vi:
Fold kodeboks ind/udKode 

inet_ntoa virker på samme måde som under Linux. inet_addr findes også til Linux men det anbefales, at man ikke bruger den, for hvis man giver adressen 255.255.255.255, som er gyldig, får man adressen -1 tilbage som indikerer en fejl. inet_aton findes ikke til Windows.
Man bruger funktionerne sådan her:
Fold kodeboks ind/udKode 

Det compiler uden problemer under Linux, men under Windows skal vi lænke med
ws2_32 biblioteket. Det gør du i Dev-C++ gennem "Project->Project
Options->Parameters->Add Library or Object". Så finder du dit Dev-C++
bibliotek, hvorunder der er et 'lib' bibliotek. Her vælger du 'libws2_32.a' og
trykker OK. Så skulle alt virke. Vi prøver at køre det under Linux:
Fold kodeboks ind/udKode 

Vi får fejl når vi giver www.udvikleren.dk som adresse. Det er fordi det ikke er en IP adresse. Vi kigger nærmere på DNS opslag senere.
Og under Windows:
Fold kodeboks ind/udKode 

Der giver også 255.255.255.255 problemer, så det er vi nødt til at tjekke for,
når vi senere udvikler vores internet adresse klasse.

DNS opslag



IP adresser kan være svære for mennesker at huske, og hvis en server går ned, og en anden skal tage over, skal alle der brugte den første server, have dette at vide. Derfor opfandt man internet navne servicen DNS (Domain Name Service), hvor man kan tilknytte ét eller flere navne til en server. Det betyder så, at hvis udvikleren.dk gik ned, så kunne Kasper bare flytte en backup over på en anden server og rette i DNS, så udvikleren.dk peger på den nye server, og ingen ville opdage det.
Nu er det bare sådan, at al Internet trafik kun bruger IP adresser, så når du forbinder til www.udvikleren.dk, så bliver navnet først slået op hos en DNS server, som giver din browser IP adressen. Derefter kan du forbinde til serveren. Lidt ligesom du ikke kan ringe til Hans Peter. Du kan slå hans navn og adresse op i en telefon bog og så ringe til det telefon nummer, der står angivet.
Da DNS er en meget brugt protokol, har alle operativ systemer også implementeret den for os og givet os et interface til at lave opslag.
For en gangs skyld er Microsoft kompatibel med Berkeley Socket interfacet så både under Linux og Windows bruger vi:
Fold kodeboks ind/udKode 

gethostbyname slår et host navn op (f.eks. www.udvikleren.dk) og returnerer information om denne. gethostbyaddr slår en IP adresse op og returnerer information om denne. Man får en hostent struktur tilbage, som indeholder det officielle hostnavn (h_name), en liste af alternative navne (h_aliases), som denne server også har. h_addrtype er typen af adresse, og den er altid AF_INET, når vi snakker Internet opslag. Vi kunne også have lavet IPX eller AppleTalk opslag, og så havde vi fået andre adressetyper, men disse protokoller vil vi ikke koncentrere os om. h_length fortæller os, hvor mange bytes hver adresse i den efterfølgende adresseliste har. h_addr_list er en liste af netværks adresser, som denne maskine kan nåes på. En server kan godt have tilknyttet flere IP adresser, men vi bruger bare den første i listen, når vi senere vil forbinde til en server.
Vi udbygger programmet fra tidligere til at lave et opslag på adressen, som gives som parameter, og først brokke sig, hvis der ikke kan laves et opslag:
Fold kodeboks ind/udKode 


Kort forklaret så tjekker vi først, om argumentet er en IP adresse, og det kan konverterings funktionen jo gøre for os. Derefter laver vi et opslag med enten gethostbyname eller gethostbyadr baseret på dette tjek.
Når vi har fået en pointer til vores hostent struktur skriver vi først det officielle navn efterfulgt af aliaser og IP adresser. Hvis vi fik NULL, undersøger vi, hvad der gik galt og skriver en passende fejlbesked. Som det ses, tjekker vi for fejl forskelligt mellem Windows og Linux. Lad os prøve at teste det engang:
Fold kodeboks ind/udKode 

Som det kan ses, er det ikke altid, at vi får alle informationer med. F.eks. får vi ikke at vide, at www.udvikleren.dk og cgi2.webhotel.net faktisk er den samme host. Ét af disse navne er et alias (sandsynligvis udvikleren.dk).
Vi kan også se, at der ikke findes en DNS record for 192.168.1.1, selvom jeg kan pinge den. Sådan er livet. Ikke alle servere er registreret nogen steder, så det skal vi tage højde for i vores klasse.

Internet adresse klasse



Vi har nu været lidt omkring i socket API'et, som handler om IP adresser, så vi kan nu definere en klasse, som indkapsler denne funktionalitet.
Da jeg udvikler klasserne så objekt orienteret som muligt, bruger jeg også exceptions til at indikere fejl, og som sagt bygger jeg videre på de klasser, jeg lavede i artiklen om multithreading. Det nye hieraki er:



UnknownHostException bliver kastet, hvis et hostname ikke findes i DNS, og NetException bruges til alle andre fejltilstande. Man kunne have lavet flere specialiseringer, men jeg har aldrig haft brug for andre når det gælder adresse opslag.
Koden til Exception og ThreadException blev listet i artiklen om multithreading. De nye er implementeret således:
Fold kodeboks ind/udKode 

Fold kodeboks ind/udKode 

og
Fold kodeboks ind/udKode 

Fold kodeboks ind/udKode 


For at gøre initialiseringen af WinSock arkitektur uafhængig vil jeg lave en define, som under Linux ikke gør noget, men som under Windows laver et kald til WSAStartup. Det gør jeg i følgende header og source fil:
Fold kodeboks ind/udKode 

og
Fold kodeboks ind/udKode 

Vi prøver at lave koden arkitektur uafhængig, men det er ikke alle platforme, hvor en int fylder fire bytes. Derfor omdefinerer man ofte de simple datatyper. Det gør jeg i følgende fil:
Fold kodeboks ind/udKode 

Det var forarbejdet. Nu kommer det spændende....IPAddress. Jeg har lavet følgende interface:
Fold kodeboks ind/udKode 


Og implementeringen bliver:
Fold kodeboks ind/udKode 


Det var det. Slet ikke så besværligt.

Revideret internet opslags program



Nu hvor vi har lavet vores IPAddress klasse, skal vi selvfølgelig også se et eksempel, hvor den bliver brugt. Vi har designet og implementeret klassen, så IP adresser, som ikke står i DNS, stadig kan bruges, men så host navne, som ikke er i DNS, kaster exceptions. Vores program tager et uendeligt antal parametre og laver et opslag for hver, hvorefter det skriver informationer ud om hver adresse:
Fold kodeboks ind/udKode 

Det var en hel del nemmere end før, og koden er også meget pænere.
Koden skulle meget gerne compile lige godt under både Linux og Windows, men da projektet begynder at blive stort (både koden fra multithreading artiklen og denne), og det bliver større i de efterfølgende artikler, så vil vi bruge en makefil til at compile under Linux:
Fold kodeboks ind/udKode 

Så skal man bare skrive 'make' for at compile og lænke koden:
Fold kodeboks ind/udKode 

Lad os prøve det:
Fold kodeboks ind/udKode 

Det kører jo bare.

Konklusion



Det var alt for denne gang. Vi har kun set på adresser indtil videre, men har allerede en pænt stor mængde kode. Det er fordi netværksprogrammering er komplekst, men så er det jo godt, at vi kan abstrahere lidt væk og kun koncentrere os om Internet adresser. Så bliver det hele lidt mere overskueligt.
I næste artikel går vi rigtig i gang med netværks forbindelser, og her får vi også brug for vores IPAddress klasse. Glæd jer.

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 (13)

User
Bruger #5352 @ 29.02.04 17:01
Rigtig fed artikel!.
User
Bruger #404 @ 29.02.04 17:46
det var satme den bedste artikel jeg har læst længe !!!
User
Bruger #2695 @ 29.02.04 18:59
rødme rødme Tak skal I ha :-)
2. artikel ligger klar til læsning. Jeg håber, at I også kan lide dén.
3. artikel om server programmering er lige røget til godkendelse
User
Bruger #714 @ 29.02.04 21:32
Guf guf guf :D Kan jeg sige andet?
User
Bruger #4768 @ 17.03.04 12:27
DEN ER SUPER
User
Bruger #4404 @ 15.06.04 18:38
Endelig en rigtig god artikkel... men jeg får nogle fejl... jeg tror det har noget at gøre med når man skal bruge filen 'libws2_32.a'

[Linker error] undefined reference to `WSAStartup@8'

[Linker error] undefined reference to `inet_addr@4'

[Linker error] undefined reference to `inet_ntoa@4'

[Linker error] undefined reference to `WSACleanup@0'

C:\\Documents and Settings\\Kasper Nielsen\\Skrivebord\\Windows & Linux\\Ip program\\Makefile.win
[Build Error] [Projekt1.exe] Error 1

Nogle der ved hvad der er galt??
User
Bruger #2695 @ 18.06.04 17:38
Ja Kasper. Problemet er, at du ikke lænker med ws2_32.ii
Det gør du Dev-C++ ved gennem:
Project->Project options->Parameters->Add Library or object
Så finder du dit lib bibliotek under Dev-C++ (c:\\dev-cpp\\lib) og derunder vælger du libws2_32.a

Så skulle det virke.
User
Bruger #4404 @ 25.06.04 11:29
Men det har jeg gjordt jeg får stadig fejlen :'(
User
Bruger #5369 @ 29.09.04 16:52
Forrygende artikler. Jeg læser til SW-ingeniør og bruger meget dine artikler til inspiration.
User
Bruger #7603 @ 10.11.05 17:34
Fuck en lækker artikel!
User
Bruger #9394 @ 11.08.06 13:41
Skide godt - meget inspirerende. :) 5 herfra!
User
Bruger #13937 @ 16.09.09 18:34
Hej, og tak for nogle fine artikler :b

Jeg sidder og leger med det, men får følgende fejl når jeg kør min makefile:

martin@martin-ub:~/workspace/cpp/Network_play$ make
g++ -ggdb -Wall -c -o Exception.o Exception.cpp
g++ -ggdb -Wall -c -o IPAddress.o IPAddress.cpp
g++ -ggdb -Wall -c -o Net.o Net.cpp
g++ -ggdb -Wall -c -o NetException.o
g++: no input files
make: *** [ NetException.o] Error 1

Er det fordi at funktionen i NetException.cpp er tom?

mvh. martin :)
User
Bruger #15047 @ 07.04.10 10:59
Jeg kan ikke compile din klasse, men alt det andet virker fint. Jeg har alle filerne, men den spørger efter filen Exception.h, den har jeg ikke. Og den er ikke i projektet. Men så lavede jeg den selv, men så får jeg bare syntax error, som : expected { before class osv. Jeg har Dev-C++ Version 4.9.9.2
Du skal være logget ind for at skrive en kommentar.
t