DKT1
Laboratorieøvelse 2
Gruppe 18
Rapport
- filoverførsel
IHA, den 09-04-2003
Udarbejdet af: 01011 Jacob Germundsen
02847 Søren S. Munk
|
Rev |
Dato |
Bemærkninger |
Side |
Afsnit |
|
01 |
09-04-2003 |
Første release |
- |
- |
|
|
|
|
|
|
4.1 Hvilke lag i OSI-modellen er involveret. 3
4.7 Connectionoriented / connectionless.. 7
4.8 Pålidelig/ upålidelig forbindelse.. 7
4.9 Transmissionkanalens udnyttelsesgrad.. 8
Det program vi har lavet, kan enten sende eller modtage en fil. Filoverførslen foregår på serialporten. Programmet sørger for at sende filen over i passende blokstørrelser og kontrollerer de transmitterede pakker med et CRC tjek. Der gøres brug af nogle af OSI-modellens 7 lag - i dette tilfælde 5. De 4 af dem er designet og implementeret med klasser og kode og det femte (netværkslaget) er kun beskrevet med det der skulle til, hvis dette lag også skulle være implementeret.
Formålet med opgaven er at overføre en fil i passende blokstørrelser fra PC1 til PC2. Hvert lag i OSI modellen "koder" så at sige fildataene på deres egen måde. Ved at lade hvert lag kode og dekode efter samme metode, kan dataene genskabes uden fejl. Ved fejl i det sendte, sendes en NACK tilbage til det samme lag i senderdelen og en retransmission forekommer.
OSI modellen og vores program:
level 1: (fysisk)
Det nederste niveau i OSI-modellen er her hvor bits bliver til en spænding i et kabel. Det er umuligt at lave noget uden dette niveau.
Level 2: (datalink)
Dette niveau består af SLiP protokollen.
Level 4: (transport)
Et vigtigt niveau hvis man ønsker fejl-detektering af de modtagede pakker, det er nemlig her fejl-detekteringen sker og retransmission evt. tages i brug, så senere niveauer modtager korrekte data.
Det er her vores program udregner 16-bit CRC på de modtagede data.
Level 7: (program)
Program niveauet er det som brugeren kan se og interagere med i programmer som f.eks. word og excel.
I vores program er der et tekst-vindue (DOS-vindue) hvor al information bliver skrevet til.
|
|
SENDER-DELEN (PC1) |
SENDER-DELEN (PC2) |
||||||||||
|
Kommentarer: |
Data størrelse: |
Kommentarer: |
Datastørrelse: |
|||||||||
|
5) Applikationslaget Class Read Class Write |
Åbn fil xx.x
|
<= 1024 Bytes |
|
<= 1024 Bytes |
||||||||
|
4) Transportlaget Class TP
|
TPDU: 1024 Bytes Header: 5 byte
|
<= 1029 Bytes |
og gør DATA tilgængelige til næste lag,
|
<= 1029 Bytes |
||||||||
|
3) Netværkslaget
|
- |
<= 1029 Bytes |
- |
<= 1029 Bytes |
||||||||
|
2) Datalinklaget
Class SLiP |
"Kod" Header + DATA efter udleverede specifikationer. Startbyte: 7E = 1 Byte Stopbyte: 7E = 1 Byte
|
<= 1029 Bytes + extra data |
Fjern start- og stop byte.
|
<= 1029 Bytes |
||||||||
|
1) Fysiske lag
Class comclass |
Ud og ind af serial port |
<= 1029 Bytes + extra data
|
|
<= 1029 Bytes + extra data |
||||||||
Viser den vej fildataene kører fra PC1's applikationslag og over til PC2
Viser den vej ACK og NACK bevæger sig fra PC2 til PC1
Kommunikationen på det fysiske lag vil i denne opgave foregå via RS-232 seriel port. Tilgang til denne vil foregå via en udleveret driver.
På dette niveau ligger SLiP, HDLC, ARP m.fl. protokoller. Til denne opgave bruges en mindre modificeret SLiP protokol.
SLiP protokollen har til formål at pakke dataene ind så man kan skelne pakker fra hinanden og hvornår en datastrøm starter/stopper.
Vi skal implementere en SLiP protokol, så et array af data bliver pakket ind i start/stop bytes = '~' og eventuelle forekomster af start/stop bytes i arrayet bliver kodet således, at man kan genskabe det originale array på modtagersiden.
ARP protokollen bruges til at forbinde en netværksadresse til en fysisk adresse (MAC-adressen), den er ikke i brug i vores program, da der er tale om en p2p kommunikation, og man vil derfor altid vide hvor/hvem modtageren er.
Da vi selv skal implementere dette lag, har det givet anledning til nogle små tests for at give os et indblik i hvad der sker og hvordan.
Vi har bl.a. opdaget at sendes teksten '~~' modtages følgende tekst hos modtageren '~þ'.
Præcist hvad det er der sker, har vi ikke været i stand til at finde ud af, men det giver jo et kæmpe problem under implementeringen. Det er ikke muligt at sende "tomme" data, for sendes "~~" og lidt senere ~DATA~ modtages ~þ~DATA~ hvilket betyder at den data som modtageren modtager og gemmer er "þ", og DATA vil blive ignoreret, da der ikke er start bits foran ('~'). Dette problem fremkommer oftest når et tegn gentages, men vi har ikke kunnet genskabe fænomenet.
På netværkslaget ligger IP og Routing protokoller. Formålet med netværkslaget er at få pakker fra et sted på et netværk til et andet (ofte entydigt) sted på et netværk.
Kommunikationen i denne øvelse foregår via et serielt kabel, hvor det ikke er muligt at kommunikere med mere end en anden maskine. Da denne er forbundet direkte til den anden maskine (point-to-point), har vi ikke brug for et netværkslag.
Hvis man ønsker at bruge et netværkslag eller senere implementere et, kan man med fordel brug den eksisterende IP protokol. Da IP protokollen indeholder et modtager og et afsender felt, kan man sende pakker til en entydig adresse og denne kan svare tilbage via afsenderadressen. Protokollen indeholder yderligere checksum og pakke-identifier, så det er muligt at skabe en pålidelig dataoverførsel. IP protokollen implementerer desuden fragment offset, som bruges hvis MTU i et net er mindre end det net hvor den kommer fra. Der gør det muligt at fragmentere (opdele) en pakke under transport fra en host til en anden, og stadig få pakkerne samlet korrekt ved destinationen.
Det er på dette lag TCP, IPX og NetBIOS protokollerne ligger. I transport laget skal der sikres at overført data er "korrekt" hermed ment at transmissions fejl af data delen bilver fanget. Men også en lille ting som et serielt kabel der bliver trukket ud og sat i igen skal detekteres her.
I den stillede opgave skal vi selv designe og implementere dette lag.
For at sikre os at afsendt data er overført korrekt, vil vi i vores header have en 16bit CRC, denne splittes op i 2 bytes.
Vi vil desuden have mulighed for at se om dette er den første (init-bit) eller sidste (slut-bit) pakke, så vi ved hvornår der ikke kommer flere pakker, det kan være en fordel når der sendes filer som ikke er fastlagt på afsendingstidspunktet (fx en fil der samples til). Init-bit fortæller modtageren at der skal gøres klar til at modtage data, i vores tilfælde en fil. Hvis programmet for eksempel kunne sende mere end en fil pr "session", kan den bruges til at sige her starter jeg en ny filoverførsel.
For at sikre os at en pakke er kommet korrekt over, skal vi kunne sende en ACKnowledge eller NotACKnowledge. Dette laves med 1 bit som er 0 hvis det er en NACK og 1 hvis det er en ACK. På denne måde sikrer vi os at vi kan gensende en pakke, hvis den ikke er blevet accepteret ved modtageren.
Når vi ikke modtager en ACK inden for nogle sekunder skal pakken gensendes, det betyder der kan ligge flere pakker i bufferen hos modtageren. For at skelne pakkerne fra hinanden har vi brug for at kunne nummerere dem. I denne opgave bruges "stop and wait" så vi har kun en udestående pakke. Det betyder at det er nok at bruge en enkelt bit, så en pakke kan enten have nummer 0 eller 1.
Efter man har modtaget en pakke skiftes pakke nummeret og passer den næste pakkes nummer ikke overens med det forventede pakkenummer, kasseres pakken og næste pakke læses.
Til sidst vil vi i vores header have et felt hvor størrelsen af den medsendte data står. Det sikrer at man kan checke den modtagne datamængde op mod den medsendte size i headeren.
Man kan med fordel implementere sliding window for at få en bedre udnyttelsesgrad af båndbredden. Denne form for flow-of-control er en del sværere at implementere og er mere hukommelseskrævende end "stop and wait". Man skal huske alle pakker der er blevet sendt siden sidst modtaget ACK på sendersiden. På modtagersiden skal man holde styr på pakkerne der kommer ind, da det ikke er sikkert de kommer i kronologisk rækkefølge.
Det overordnede lag hvor filen læses fra/skrives til disken i små dele. Vi ved at der skal læses data af 1024 Kbyte per pakke der skal sendes
Connectionoriented forbindelse består i at før der sendes data mellem 2 parter, først skal oprettes en forbindelse. Herefter sendes data imellem de 2 parter. Når der ikke er mere data der skal overføres, lukkes forbindelsen igen.
I en connectionless forbindelse forventes modtageren til noget data at være klar til at modtage når afsenderen begynder at sende. Der udføres ikke noget check for at sikre sig at den er det. Altså er der meget stor sandsynlighed for at data går tabt sammenlignet med en Connectionoriented forbindelse.
I vores program er der tale om en connectionless.
En pålidelig service er en service uden fejl, dvs. at man kan stole på den data man modtager også er dem der blev afsendt.
En pålidlig forbindelse bliver brugt i forbindelse med pålidelige services, for at sikre at den modtagede data er korrekt. Heri ligger retransmission af pakker.
De fleste netværk yder en upålidelig service i form af best-try eller best-effort, altså bliver de pakker med fejl kasseret og der skal en retransmission til.
For at et program kan opnå en pålidelig service, må man dele de data man vil sende op i blokke af en bestemt størrelse. Og bliver en blok ikke modtaget skal den gensendes. Er det opfyldt kaldes det en pålidelig service.
Vores hastighed er 19200 baud = 19200 bit/sek = 2400 byte/sek.
En korrekt pakke består maksimalt af 1031 byte, inklusiv CRC, start- og stop-byte.
I pakken findes:
start- og stop-bit = 2 byte
header = 5 byte
data = 1024 byte
i alt 1031 byte => 7/1031 ~ 0,6%
Dvs. den bedste udnyttelsesgrad bliver ca. 99,4% af den samlede båndbredde brugt til reelle data, af 2400 byte/sek er det 2385 byte data / sek.
Udregningerne forudsætter at modtageren er klar til at modtage data og kan nå at aflæse de sendte data ligeså hurtigt som de bliver afsendt.
Man bør selvfølgelig overveje hvor meget reel data der i den sidste pakke der bliver sendt. Men i worst case bliver der kun sendt 1 byte, sammen med header giver det 6 bytes i alt => 1/6 ~ 17%.
Her bliver udnyttelsesgraden kun 400 byte data / sek.
Udarbejde en main applikation, der efter et valg bliver den del der styrer afsendelsen eller modtagelsen af den ønskede fil.
Main applikationen fortæller i begge tilfælde om, hvor stor filen er og om transmissionen gik godt.
Sender-delen sørger for at åbne og læse den ønskede fil der skal overføres til modtagerdelen, og læser op til 1024 bytes ad gangen.
Ved eventuelle retransmissioner orienteres brugeren om at dette sker.
Under transmissionen bliver der sat en * (stjerne) for hver pakke der er overført, på både sender og modtager. Dette kan bruges som en form for progress-line indikator.
TPDU er i denne opgave valgt til 1024bytes, og med en baud rate på 19200 overføres 1029 bytes = 8232bit på ca. 1/19200 * 8232 ~ 0,26 sek. Det vil sige at man kan forvente at startbyten '~' og slutbyten '~' på serielporten, kommer med max 0,3 sekunders mellemrum. Sker dette ikke, er der noget galt med pakken/transmissionen og funktionen kan defor times ud.
SLiP protokollen sætter en header/start bit foran de data vi vil sende og en header/stop bit efter dataene.
Desuden erstattes alle forekomster af start/stop karakteren med '7B' og '7C' i hex. Forekommer tegnet '7B' i hex erstattes det af '7B' og '7D' i hex.
Til dette formål var der udleveret nogle færdige C++ klasser som blev brugt til DKT opgave 1 de er blevet brugt igen.

Diagrammet viser opbygningen af vores klassser og som vi har tænkt os at de skal snakke sammen. Den klasse der er givet af lærerstaben, kan ses nederst i diagrammet.
Vi laver en klasse serielStyring som bruger den udleverede klasse "ComClass".
serielStyring klassen skal give et nemt interface til ComClass klassen, i denne er der en default constructor som åbner com port 1 med default værdier.
Vi laver nu en SLiP klasse som udvider serielStyring klassen, og dermed giver SLiP klassen adgang til den serielle port. Når et SLiP objekt oprettes og der leveres noget data til SLiP objektet kodes det med start- og stop-byte og der laves byte stuffing på det leverede data.
Man kan nu sende SLiP objektets data og ligeledes modtage SLiP kodet data.
SLiP klassen har en funktion getData() som giver det ukodede data, altså er SLiP klassen transparent.
Vi laver nu en TP klasse som udvider (bruger) SLiP klassen, og dermed får adgang til at sende data via SLiP, men ikke direkte via ComClass (da SLiP laver private arv af serielStyring).
Man kan nu oprette et TP objekt, sætte diverse flag i headeren og kalde send funktionen for TP klassen som bruger SLiP klassens send funktion (som jo koder dataene med start- og stop-byte).
Altså kan man helt transparent kalde send() (og receive()) funktionen, og få sent en mængde data med transport protokollens header via seriel porten, pakket vha. SLiP protokollens kodning.
Transport Protocol lagets implementering.
Kodningen for en datastreng, med forklarende tekst nedenfor.
|
|
|
H3 |
H4 |
H5 |
D1 |
D2 |
D3 |
D4 |
D5 |
osv |
osv |
osv |
osv |
osv |
. |
. |
. |
H1: Init: 1 bit på plads 2^0 (1)
Slut: 1 bit på plads 2^1 (2)
Ack: 1 bit på plads 2^2 (4)
Nr: 1 bit på plads 2^3 (8)
0: 1 bit på plads 2^4 (16)
0: 1 bit på plads 2^5 (32)
0: 1 bit på plads 2^6 (64)
0: 1 bit på plads 2^7 (128)
H2: CRC_High (8 mest betydende bit af 16 bit integer)
H3: CRC_Low (8 mindst betydende bit af 16 bit integer)
H4: 000 + Size_High 2^12 til 2^8 (5 næstmest betydende bit af 16 bit)
H5: Size_Low 2^0 til 2^7 (8 mindst betydende bit af 16 bit)
Her ses ovenstående datastreng kodet på SliP laget, "H" contra H betyder at data på pladsen kan være ændrede iflg. oplæg (f.eks at ASCII nummeret for 7E (~) bliver til 7B7C( }| ))
|
~ |
H1 |
"H2" |
"H3" |
H4 |
"H5" |
"D1" |
"D2" |
"D3" |
"D4" |
"D5" |
osv |
osv |
. |
. |
~ |

Flowchart for SLiP's receive funktion, samt en privat funktion som bruges til at allokere mere hukommelse, hvis den mængde data der modtages vokser over det vi har allokeret.

Flowchar for vores main function, heri læses/skrives data fra/til disken (filen) og sendes vha. TP.
Start programmet filetransfer.exe og følgende velkomstskærm dukker op:

Ved tryk på tasten s starter senderdelen og følgende linie med indtast navnet på den fil der skal sendes fremkommer nu. Efter kolonnet skrives filens navn. Programmet forventer at finde den fil der skal overføres, i den samme folder hvor filetransfer.exe ligger.
SENDERDELEN:

Når navnet er skrevet kommer filens størrelse på skærmen og der skal trykkes på ENTER tasten, for at starte overførslen.
Hvis den ønskede fil ikke findes kommer følgende skærm:

og man kan nu vælge at skrive et andet navn, eller kopiere filen ind i folderen og så skrive navnet igen, så virker det.

Her ses en fil med navnet 2 på 12Kbyte der er blevet overført uden problemer.

Her ses den samme fil med transmissions problemer og som det ses bliver en af blokkene retransmitteret 3 gange, for derefter at køre videre med de efterfølgende pakker.

Det hele ender godt med en korrekt overført fil.

Her er et eksempel på at det ikke lykkedes. Efter 5 gange er max retransmissioner nået og programmet afsluttes uden at filen er blevet overført.
MODTAGERDELEN:
På den pc der skal modtage filen, skrives det navn filen skal gemmes under. Hvis filen ikke findes i modtager pc'ens folder med filetransfer.exe programmet, starter modtagelsen omgående efter indtastningen af navnet efterfulgt af ENTER.

Hvis filen allerede findes i folderen, så kommer der en meddelelse om det. På det følgende billede ses et forsøg på at gemme en fil med navnet 3 og da den findes i forvejen, får brugeren det at vide:

Her ses en korrekt modtaget 12 Kbyte fil der blev gemt i navnet fil.exe:

Vi har lavet et program som kan sende og modtage en fil via en serielport, detektere CRC fejl i de modtagede data samt fejlfinde ved manglende start/stop-bytes. Vi er meget tilfredse med vores måde at implementere TP og SLiP klassen på, det gør det meget nemt at bruge og meget simpelt styring i vores main funktion.
Den besynderlige opførsel vi fandt i starten, har vi ikke set mere til, så det har måske været det program vi brugte (HyperTerminal.exe © Microsoft™).
Århus, den 09-04-2003
__________________________ _________________________
Jacob Germundsen Søren S. Munk
0-modem kabel (de 3 fremhævede forbindelser er de mest nødvendige):
9-pin connector 9-pin connector
pin 5 <--- (ground-to-ground) ----> pin 5
pin 3 <-- (transmit-to-receive) ---> pin 2
pin 7 <------ (RTS-to-CTS) -------> pin 8
pin 6 <------ (DSR-to-DTR) -------> pin 4
pin 2 <-- (receive-to-transmit) ---> pin 3
pin 8 <------ (CTS-to-RTS) -------> pin 7
pin 4 <------ (DTR-to-DSR) -------> pin 6