Netværksprogrammering 4 - UDP sockets

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

Introduktion


Hej igen allesammen.
Vi har indtil videre dækket Internet adresser og adresse opslag samt TCP kommunikation, som er den mest brugte Internet kommunikations form. I denne artikel skal vi se på en anden protokol, som er knap så brugt men i visse tilfælde meget anvendelig.

UDP protokollen


UDP (User Datagram Protocol) er en forbindelsesløs og utroværdig protokol.
At den er forbindelsesløs betyder, at når du har oprettet din socket (med socket kaldet ca. ligesom TCP sockets), så kan du bare begynde at sende data til højre og venstre uden at kalde connect. Du specificerer en modtager adresse i hver forsendelse.
At den er utroværdig betyder, at du kun får besked om, hvorvidt dataene blev afsendt. Du ved ikke om de blev modtaget i den anden ende, eller om de blev modtaget i den samme rækkefølge, som du sendte dem. Hvis disse informationer er vigtige for din applikation, så er UDP ikke den rigtige protokol for dig. Der er dog også fordele ved, at UDP ikke er troværdig. Hver gang et antal pakker er blevet afsendt, venter det afsendende operativsystem på, at det modtagende operativsystem har sendt besked om modtagelsen. Hvis dette ikke sker, bliver pakkerne sendt igen. Denne venten og genforsendelse tager tid, og det er ikke altid, at man har det. Og hvis det er ligegyldigt om en enkelt eller to datapakker bliver mistet undervejs, så er det ekstra spild af tid.
Forestil dig et realtime multiplayer spil (Quake, Counter Strike, osv.). Det er serveren, som ved, hvor alle enheder (spillere, AI'er, osv.) er, og sender mange gange i sekundet en opdatering ud til alle spillere. Hver opdatering kunne indeholde informationer om, at figur F1 er på 3D koordinat x1,y1,,z1 og kigger i retning vx1,vy1,vz1 og figur F2 er på 3D koordinat x2,y2,,z2 og kigger i retning vx2,vy2,vz2 osv. Om et tiendedel sekund er dette en oldgammel nyhed, for da har alle flyttet sig en lille smule. Dvs. at hvis en pakke mistes, er det ligegyldigt for vi får snart en nyere, og hvis vi får pakke 10 og derefter pakke 9, så smider vi bare pakke 9 væk for vi har fået nyere koordinater.
En fordel ved den manglende forbindelse (bortset fra at man ikke skal koncentrere sig om at forbinde, holde forbindelsen og lukke forbindelsen) er, at eftersom vores socket ikke er dedikeret til én klient/server, så kan den bruges til alle. Derudover kan vi adressere en pakke til et helt netværk. Dette kaldes broadcasting.
Forestil dig igen et netværksspil. Serveren starter op og sætter en UDP socket til at lytte på port 6666 og en TCP socket til at lytte på port 6543. Derefter startes en klient og broadcaster en pakke til port 6666 til hele netværket, hvori der spørges, om der er startet en server. Serveren modtager denne pakke og sender en besked tilbage med informationer om, at TCP port 6543 er åben for yderligere kommunikation samt måske antallet af forbundne spillere, og hvilken tilstand spillet er i. Klienten kan nu forbinde til serveren.
De fleste realtime spil (??? alle ???) benytter faktisk både TCP og UDP sockets og udnytter dermed hver protokols styrker og svagheder.
TCP protokollen bruges til at sende de vigtige opdateringer (nye spillere, "jeg ønsker at flytte min spiller fremad", indlæs level 7, og lignende), medens UDP bruges til de mindre vigtige informationer (placeringer og retninger).

En UDP echo applikation



Vi vil lave endnu en echo applikation, som enten sættes til at lytte efter echo requests eller som sender echo requests.
Hvis applikationen sættes til at lytte, venter den på en UDP pakke. Hvis denne pakke indeholder teksten "CLOSEDOWN", så lukkes applikationen. Ellers bliver pakken bare sendt tilbage. Ligesom i sidste artikel.
Vi kommer til at kigge på følgende funktionskald:
Fold kodeboks ind/udKode 

sendto sender en besked på en socket. Den tager også en adresse som parameter, og det er denne adress, dataene vil blive sendt til.
recvfrom modtager en besked på en socket. Funktionen tager også en pointer til en adresse som parameter, og denne adresse vil efter kaldet indeholde adresse fortælle os, hvem der sendte dataene.
Når man har oprettet en UDP socket, kan man ikke sende broadcasts fra starten. Det er en option, som skal sættes, og det gør vi med setsockopt. getsockopt kan bl.a. fortælle os, om broadcasts er mulige på vores socket.
Både getsockopt og setsockopt kan sætte og læse et hav af options på både UDP og TCP sockets, men det er de færreste, som man bruger. I de fleste tilfælde er alt, sat fornuftigt op, men vi vil gerne broadcaste, så denne option vil vi sætte.
Lad os gå i koder mode:
Fold kodeboks ind/udKode 

Det var også mere kode end vi gider at se på normalt, så det skal selvfølgelig
kapsles ind. Men det er jo slet ikke så kompliceret igen.
Følgende billede viser et screenshot, hvor jeg kører serveren og klienten:



Først sender klienten beskeden "Hello, World" til localhost. Det bliver modtaget fint. Derefter sendes beskeden "Direkte besked" direkte til IP adressen 192.168.1.3, og derefter til 192.168.1.0 nettets broadcast adresse. Det bliver faktisk modtaget af alle computere på hele mit netværk (to desktops, en laptop og en router), men det er kun min ene desktop som lytter. Til sidst sender jeg luk-ned kommandoen til broadcast adressen.
Det virker jo bare.

En UDP socket klasse


Så er det på tide at skjule kompleksiteten bag et simpelt interface. Jeg har følgende klasse definition:
Fold kodeboks ind/udKode 

Og implementeringen:
Fold kodeboks ind/udKode 

UDPSocket filen skal selvfølgelig også tilføjes til vores makefil:
Fold kodeboks ind/udKode 

Et revideret UDP echo program


Det reviderede echo program, som bruger vores klasse, ser således ud:
Fold kodeboks ind/udKode 

Der røg en hel del linjer kode og kompleksitet, så det var jo udemærket.

Konklusion


Det var det!!
Nu er vi alle (næsten) eksperter i netværks programmering. Vi kan både lave multitrådede servere og programmere med UDP og TCP og vælge, hvornår den ene protokol er bedre til at klare opgaven end den anden. Og vi har et meget lille interface, som er let at bruge. Og vores kode kan compile lige godt på Windows og Linux. Kan det næsten blive bedre ?
Ja...det kan det helt sikkert. Netværksprogrammering er et enormt emne og bare fordi vi har fået nogle simple eksempler til at virke, skal vi ikke tro, at det stopper dér. Beklager.
Skulle der være noget, jeg mangler at beskrive (mon ikke der er?), og som gør, at I ikke kan komme videre, så skriv jeres ønsker ned. Enten som en kommentar til artiklen eller på udviklerposten. Så vil jeg måske skrive noget, når jeg får tid.
Indtil da.....happy hacking


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

User
Bruger #10613 @ 17.07.09 00:29
det crasher hele tiden... :(
Du skal være logget ind for at skrive en kommentar.
t