Netværksprogrammering 2 - TCP client sockets

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

Introduktion


Hej igen.
Så skal vi igen til at kode noget netværk, og denne gang bliver det nok lidt mere spændende. Vi skal nemlig i gang med at lave netværks forbindelser. Vi vil kigge på, hvad sockets er, og hvordan man bruger dem. Vi bruger den IP adresse klasse, vi udviklede i første artikel om netværk, så jeg håber, at du har gemt projektet.
Målet med vores kode bliver, at forbinde til en webserver og hente forsiden. Det vil illustrere både forsendelse og læsning af data. Til slut vil vi lave en klasse, som indkapsler al funktionaliteten, som vi gennemgår.

Sockets og file descriptors



En file descriptor er et IO objekt, dvs. et objekt som man kan skrive til og/eller læse fra. En socket er også en file descriptor men lidt speciel, idet dét, der skrives/læses, ofte er netværkstrafik. Når man skal forbinde til en computer, laver man først en socket, som man derefter forbinder til serveren. Derefter kan man læse fra og skrive til sin socket og til sidst lukke forbindelsen. Ca. ligesom man kan åbne en fil, skrive til den og læse fra den og til sidst lukke den.
Der findes mange forskellige socket typer, men når vi snakker Internet trafik, koncentrerer vi os om to typer: TCP og UDP.
Denne artikel koncentrerer sig udelukkende om TCP.

TCP protokollen



TCP (Transmission Control Protocol) er en pålidelig protokol, hvilket betyder, at når du skriver 10 beskeder, så kan du være sikker på, at de bliver modtaget i samme rækkefølge, som du skrev dem, og at de alle nåede sin destination, medmindre du fik en fejlbesked. Dette står i kontrast til UDP (User Datagram Protocol), som vi skal se nærmere på i en senere artikel. Det foregår ved, at TCP pakkerne indeholder fortløbende numre, så når operativ systemet på den modtagende computer modtager TCP pakker, så tjekker den om alle pakker, som har et nummer, som er mindre end den nye pakkes, er nået frem. Hvis dette ikke er tilfældet, så sendes en besked tilbage, hvor der bedes om at få tilsendt de manglende pakker. Dette sker uden at vi, som programmerer på et højere niveau, ved det. Det var lige lidt baggrundsviden, så vi ved, hvad vi har med at gøre.

En simpel klient



I denne artikel ser vi udelukkende på, hvordan man laver en netværks klient. En klient er et program, som tager initiativet til at påbegynde kommunikation. Det er den, som "ringer op" til serveren, og jeg har valgt at vise et eksempel, som læser en web servers forside. Vi fortsætter med projektet fra første artikel, hvor der er en fil kaldet test.cpp. Det er den, vi ændrer på i dette eksempel.
De funktioner, vi skal bruge under Linux, er følgende:
Fold kodeboks ind/udKode 

socket opretter en socket tilhørende en protokol familie (domain som skal være PF_INET for Internet IPv4), en type (som skal være SOCK_STREAM for TCP), og en protokol (som kan sættes til 0 eller IPPROTO_TCP).
connect forbinder en socket til en server. Her skal gives en IP adresse og et port nummer med som parameter.
htons konverterer en short datatype fra det format, som bruges på din computer til det format, som bruges på Internettet, og ntohs konverterer tilbage. Vi bruger dem til at konvertere port nummeret til Internet format.
select lader os vente et stykke tid på at noget sker. Hvis vi læser fra en socket, som ikke har modtaget noget, så blokerer vi, indtil der er noget. select lader os vente kortvarigt og fortæller os, om der er noget at læse.
send skriver et antal bytes fra en buffer til en socket. flags sætter vi bare til 0.
recv læser et antal bytes fra en socket og placerer dem i en buffer, og igen sætter vi flags til 0. Den returnerer antallet af bytes, som blev læst (eller -1 hvis der gik noget galt), og da den blokerer, til der er noget, returnerer den aldrig 0, MEDMINDRE forbindelsen blev lukket i den anden ende.
close lukker en forbindelse og frigiver alle resourcer, der er forbundet med en socket.

På Windows har vi de samme funktioner, bortset fra én, som hedder noget andet og to andre, som tager andre parametre:
Fold kodeboks ind/udKode 


Det var en hel del, men nu skal vi endelig til at kode. Koden til vores eksempel bliver ret lang, men jeg kommenterer, hvad jeg gør undervejs.
Fold kodeboks ind/udKode 


Og igen compiler det uden problemer. Vi prøver at køre det:
Fold kodeboks ind/udKode 

Den første kørsel går galt, fordi jeg prøver at forbinde til min routers port 80, hvor der ikke kører en service. Fair nok. Google derimod har en masse at sige.
Det var slet ikke så slemt.
Nu brugte jeg slet ikke select kaldet. Det er fordi, jeg ønskede den blokerende funktionalitet. Hvis programmet var multitrådet, ville jeg nok lytte i et sekund, derefter tjekke om jeg skulle lytte videre og så lytte et sekund igen, osv. Det lægger vi ind i vores klasse, når vi designer den.

En TCPSocket klasse



Så skal vi i gang. Klassen skal, ligesom i tidligere artikler, kaste exceptions for at indikere fejl og undtagelsestilstande, og til det bruger vi følgende udviddelser til exception klasserne fra tidligere:



ConnectionLostException kastes, hvis computeren, vi kommunikerer med, lukker forbindelsen.
TimerException kastes for at vise, at en timer er udløbet. I TCPSocket definerer jeg en mulighed for at standse en ellers blokerende funktion efter et antal millisekunder. Når denne tid er gået, kastes en TimerException. Jeg har ikke lagt den under NetException, da jeg måske vil bruge den andre steder også.
Alle andre fejl kaster NetExceptions.
Som alle andre exceptions, er implementeringen meget ligetil:
Fold kodeboks ind/udKode 

Fold kodeboks ind/udKode 

og
Fold kodeboks ind/udKode 

Fold kodeboks ind/udKode 


Jeg har defineret følgende interface til TCPSocket klassen:
Fold kodeboks ind/udKode 


Implementeringen er ret ligetil:
Fold kodeboks ind/udKode 

Ja, det var vist heller ikke så slemt selvom, der var meget kode.

En revideret web læser


Nu hvor vi har en TCPSocket klasse skal vi som sædvanligt kode vores eksempel om, så det bruger klassen. Det har jeg gjort her:
Fold kodeboks ind/udKode 

connectAndRead funktionen er blevet lidt pænere og lettere at læse. Til Linux skal Makefilen også ændres lidt til at compile de nye filer:
Fold kodeboks ind/udKode 

Det skulle være det!!!

Konklusion


Det var slutningen. Håber du har lært noget. Vi har fået udviklet en klasse, som gør det noget nemmere at kode netværk. Det er et mindre og simplere interface, som er nemmere at huske, men vi har indtil videre været nødt til at bruge en webserver til at teste vores kode. Det ændrer sig i næste artikel, hvor vi skal se på server programmering. Det er faktisk også ret let.


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

User
Bruger #714 @ 29.02.04 21:34
Endnu engang guffeluf, dejligt med alle de kommentarer du smider undervejs.
User
Bruger #11057 @ 28.12.06 13:37
En rigtig fin artikel :D

test www.google.com virker fint i wondows kommandoprompt, men hvis jeg prøver med en ip-adresse fx 127.0.0.1, så sker der ikke andet end at programmet låser indtil der kommer en runtime-error.

Er der en fejl i koden omkring TCPSocket-objektet?
Du skal være logget ind for at skrive en kommentar.
t