Udp protokol. Koja je razlika između TCP-a i UDP-a, jednostavno rečeno. Dublje u kod. Zatvaranje veze zbog isteka vremena

Paket mrežnih protokola za Internet. Sa UDP-om, računarske aplikacije mogu slati poruke (u ovom slučaju nazvane datagrami) drugim domaćinima preko IP mreže bez potrebe za prethodnom komunikacijom za uspostavljanje posebnih kanala prijenosa ili putanja podataka. Protokol je razvio David P. Reed 1980. i formalno definiran u RFC 768.

UDP koristi jednostavan model prijenosa, bez implicitnih rukovanja kako bi se osigurala pouzdanost, redoslijed ili integritet podataka. Dakle, UDP pruža nepouzdan servis, a datagrami mogu stići van reda, biti duplicirani ili nestati bez traga. UDP implicira da provjera i ispravljanje grešaka ili nisu potrebni ili se moraju izvršiti unutar aplikacije. Aplikacije koje su osjetljive na vrijeme često koriste UDP jer je bolje ispustiti pakete umjesto čekati na odgođene pakete, što možda nije moguće na sistemima u realnom vremenu. Ako je potrebno ispraviti greške na sloju mrežnog interfejsa, aplikacija može koristiti TCP ili SCTP dizajniran za ovu svrhu.

Priroda UDP-a kao protokola bez stanja je također korisna za servere koji odgovaraju na male zahtjeve velikog broja klijenata, kao što su DNS i aplikacije za streaming medija kao što su IPTV, Voice over IP, protokoli za IP tuneliranje i mnoge online igre.

Enciklopedijski YouTube

    1 / 5

    ✪ Portovi i preusmjeravanje\otvaranje portova. Uputstva i objašnjenja na dohvat ruke!

  • Titlovi

Servisni portovi

UDP ne daje nikakve garancije isporuke poruke višem protokolu i ne pohranjuje stanje poslanih poruka. Iz tog razloga, UDP se ponekad naziva nepouzdanim protokolom datagrama.

Čekovni zbroj

Polje kontrolne sume se koristi za provjeru zaglavlja i podataka na greške. Ako iznos nije generiran od strane predajnika, tada se polje popunjava nulama. Polje nije obavezno za IPv4.

Kalkulacija kontrolne sume

Metoda za izračunavanje kontrolne sume je definisana u RFC 1071.

Prije izračunavanja kontrolne sume, ako je dužina UDP poruke u bajtovima neparna, tada se UDP poruka na kraju puni nul bajtom (pseudo-zaglavlje i nul-bajt za punjenje se ne šalju s porukom, oni se koriste samo pri izračunavanju kontrolne sume). Pretpostavlja se da je polje kontrolne sume u UDP zaglavlju nula tokom izračunavanja kontrolne sume.

Za izračunavanje kontrolne sume, pseudo-zaglavlje i UDP poruka se dijele na dvobajtne riječi. Tada se zbroj svih riječi izračunava u aritmetici obrnutog koda (to jest, kod u kojem se negativan broj dobije iz pozitivnog broja invertiranjem svih znamenki broja i postoje dvije nule: 0x0000 (označeno + 0) i 0xffff (označeno -0)). Rezultat se upisuje u odgovarajuće polje u UDP zaglavlju.

Vrijednost kontrolne sume jednaka 0x0000 (+0 u obrnutom kodu) je rezervirana i znači da kontrolna suma nije izračunata za slanje. Ako je kontrolna suma izračunata i ispostavilo se da je jednaka 0x0000, tada se u polje kontrolne sume upisuje vrijednost 0xffff (-0 u obrnutom kodu).

Kada je poruka primljena, primalac ponovo izračunava kontrolnu sumu (uzimajući u obzir polje kontrolne sume), a ako je rezultat −0 (tj. 0xffff), smatra se da je kontrolna suma konvergirala. Ako se zbroj ne konvergira (podaci su oštećeni tokom prijenosa, ili je kontrolni zbroj pogrešno izračunat na strani koja šalje), odluku o daljnjim radnjama donosi strana koja prima. Po pravilu, većina modernih uređaja koji rade sa UDP/IP paketima imaju podešavanja koja im omogućavaju da ili ignorišu takve pakete ili ih preskoče radi dalje obrade, bez obzira na neispravnost kontrolne sume.

Primjer izračuna kontrolne sume

Na primjer, izračunajmo kontrolni zbir nekoliko 16-bitnih riječi: 0x398a, 0xf802, 0x14b2, 0xc281.

Da biste to učinili, prvo možete sabirati brojeve u parovima, tretirajući ih kao 16-bitne neoznačene brojeve, nakon čega slijedi redukcija na komplementarni kod dvojke dodavanjem jedan rezultatu, ako je tokom sabiranja došlo do prijenosa na najvišu (17.) znamenku (to jest, de facto, ovom operacijom pretvaramo negativan broj iz njegovog komplementa u njegov recipročni kod). Ili, što je ekvivalentno, možemo smatrati da se prijenos dodaje na nižu cifru broja.

0x398a + 0xf802 = 0x1318c → 0x318d (prijenos u visoki red) 0x318d + 0x14b2 = 0x0463f → 0x463f (pozitivan broj) 0x463f + 0xc281 = 0x1 → 0x1

Na kraju, svi bitovi rezultirajućeg broja se invertiraju

0x08c1 = 0000 1000 1100 0001 → 1111 0111 0011 1110 = 0xf73e ili, inače - 0xffff − 0x08c1 = 0xf73e . Ovo je željena kontrolna suma.

Prilikom izračunavanja kontrolne sume, ponovo se koristi pseudo-zaglavlje koje simulira pravo IPv6 zaglavlje:

Bits 0 - 7 8 - 15 16 - 23 24 - 31
0 Izvorna adresa
32
64
96
128 Adresa primaoca
160
192
224
256 UDP dužina
288 Nule Sljedeći naslov
320 Izvorni port Odredišna luka
352 Dužina Čekovni zbroj
384+
Podaci

Izvorna adresa je ista kao u IPv6 zaglavlju. Adresa primaoca - krajnji primalac; ako IPv6 paket ne sadrži zaglavlje rutiranja, onda će to biti odredišna adresa iz IPv6 zaglavlja, u suprotnom, na početnom čvoru, to će biti adresa posljednjeg elementa zaglavlja usmjeravanja, a na prijemnom čvoru, odredišnu adresu iz IPv6- zaglavlja. Vrijednost sljedećeg zaglavlja jednaka je vrijednosti protokola - 17 za UDP. UDP dužina - dužina UDP zaglavlja i podataka.

Pouzdanost i rješenja za probleme preopterećenja

Zbog nedostatka pouzdanosti, UDP aplikacije moraju biti pripremljene za gubitak, grešku i dupliranje. Neki od njih (na primjer, TFTP) mogu opciono dodati elementarne mehanizme pouzdanosti na razini aplikacije.

Ali češće nego ne, takve mehanizme ne koriste UDP aplikacije, pa ih čak i ometaju. Streaming mediji, igre za više igrača u realnom vremenu i VoIP primjeri su aplikacija koje često koriste UDP protokol. U ovim konkretnim aplikacijama gubitak paketa obično nije veliki problem. Ako aplikacija zahtijeva visok nivo pouzdanosti, tada možete koristiti drugačiji protokol (TCP) ili koristiti metode kodiranja otporne na greške (Erasure code). ru en).

Ozbiljniji potencijalni problem je taj što, za razliku od TCP-a, aplikacije zasnovane na UDP-u nemaju nužno dobru kontrolu zagušenja i mehanizme za izbjegavanje. UDP aplikacije koje su osjetljive na zagušenje koje troše značajan dio dostupnog propusnog opsega mogu ugroziti stabilnost Interneta.

Mrežni mehanizmi su dizajnirani da minimiziraju mogućih efekata od preopterećenja pod nekontrolisanim opterećenjima velike brzine. Mrežni elementi kao što su ruteri koji koriste redove paketa i tehnike ispuštanja često su jedini dostupni alati za usporavanje prekomjernog UDP prometa. DCCP (Datagram Congestion Control Protocol) razvijen je kao djelomično rješenje za ovo potencijalni problem dodavanjem mehanizama krajnjem hostu za praćenje zagušenja za UDP tokove velike brzine kao što su streaming medija.

Prijave

Brojne ključne Internet aplikacije koriste UDP, uključujući DNS (gdje zahtjevi moraju biti brzi i sastoje se od samo jednog zahtjeva praćenog jednim paketom odgovora), Simple Network Management Protocol (SNMP), Routing Information Protocol (RIP), Dynamic Host Configuration (DHCP) .

Glasovni i video saobraćaj se obično prenosi pomoću UDP-a. Protokoli za video i audio streaming uživo dizajnirani su za rukovanje nasumičnim gubicima paketa tako da je kvalitet samo neznatno degradiran umjesto da doživi velika kašnjenja kada se izgubljeni paketi ponovo prenesu. Budući da i TCP i UDP rade na istoj mreži, mnoge kompanije su primijetile da nedavno povećanje UDP prometa iz ovih aplikacija u realnom vremenu ometa performanse TCP aplikacija kao što su baze podataka ili računovodstveni sistemi. Budući da su i poslovne aplikacije i aplikacije u realnom vremenu važne za kompanije, razvoj kvalitetnih rješenja za probleme neki vide kao glavni prioritet.

Poređenje UDP-a i TCP-a

TCP je protokol orijentiran na vezu, što znači da je potrebno "rukovanje" za uspostavljanje veze između dva hosta. Kada se veza uspostavi, korisnici mogu slati podatke u oba smjera.

  • Pouzdanost- TCP upravlja potvrdom poruke, ponovnim prijenosom i timeoutom. Brojni su pokušaji da se poruka prenese. Ako se usput izgubi, server će ponovo zatražiti izgubljeni dio. Kod TCP-a nema podataka koji nedostaju ili (u slučaju višestrukih vremenskih ograničenja) prekinutih veza.
  • Urednost- ako se dvije poruke šalju uzastopno, prva poruka će prva stići do aplikacije primaoca. Ako komadi podataka stignu pogrešnim redoslijedom, TCP šalje podatke van reda u međuspremnik dok se svi podaci ne mogu naručiti i poslati aplikaciji.
  • Težina- TCP zahtijeva tri paketa za uspostavljanje utičnice prije slanja podataka. TCP prati pouzdanost i zagušenje.
  • Threading- podaci se čitaju kao tok bajtova, ne prenose se posebne oznake za granice poruke ili segmente.

UDP je jednostavniji protokol bez povezivanja zasnovan na porukama. Ovi tipovi protokola ne uspostavljaju namjensku vezu između dva hosta. Komunikacija se ostvaruje prenošenjem informacija u jednom smjeru od izvora do primatelja bez provjere spremnosti ili stanja primaoca. U aplikacijama Voice over IP (TCP/IP), UDP ima prednost u odnosu na TCP, gdje bi svako rukovanje spriječilo dobru glasovnu komunikaciju. U VoIP-u, od krajnjih korisnika se očekuje da pruže svaku potrebnu potvrdu prijema poruke u realnom vremenu.

  • Nepouzdan- kada se poruka pošalje, ne zna se da li će stići na odredište - može se izgubiti na putu. Ne postoje koncepti kao što su potvrda, retransmisija, vremensko ograničenje.
  • Poremećaj- ako se dvije poruke šalju istom primaocu, tada se ne može predvidjeti redoslijed kojim se postiže cilj.
  • Lakoća- nema redanja poruka, nema praćenja veze, itd. To je mali transportni sloj dizajniran u IP-u.
  • Datagrami- paketi se šalju pojedinačno i provjeravaju integritet samo ako stignu. Paketi imaju određene granice koje se poštuju kada su primljene, što znači da će operacija čitanja na prijemnoj utičnici proizvesti poruku onako kako je prvobitno poslana.
  • Nema kontrole preopterećenja- UDP sam po sebi ne izbjegava zagušenja. Moguće je da aplikacije velike propusnosti prouzrokuju kolaps zagušenja osim ako ne implementiraju kontrole na razini aplikacije.

Korisnički datagram protokol - UDP

UDP protokol je jedan od dva protokola transportnog sloja koji se koriste u stogu TCP/IP protokola. UDP omogućava aplikacijskom programu da prenosi svoje poruke preko mreže sa minimalnim troškovima povezanim s pretvaranjem protokola sloja aplikacije u IP. Međutim, sam aplikacijski program mora se pobrinuti za potvrdu da je poruka isporučena na odredište. Zaglavlje UDP datagrama (poruke) izgleda kao što je prikazano na slici 2.10.

Rice. 2.10. Struktura zaglavlja UDP poruke

Jedinica podataka UDP protokola naziva se UDP paket ili korisnički datagram. UDP paket se sastoji od zaglavlja i polja podataka koje sadrži paket sloja aplikacije. Zaglavlje je jednostavnog formata i sastoji se od četiri dvobajtna polja:

    UDP izvorni port - broj porta procesa slanja,

    UDP odredišni port - broj porta procesa primaoca,

    Dužina UDP poruke - dužina UDP paketa u bajtovima,

    UDP kontrolna suma - kontrolna suma UDP paketa

Ne moraju se popuniti sva polja UDP paketa. Ako poslani datagram ne očekuje odgovor, onda se nule mogu staviti na adresu pošiljaoca. Također možete odbiti izračunavanje kontrolne sume, ali imajte na umu da IP protokol izračunava kontrolnu sumu samo za zaglavlje IP paketa, zanemarujući polje podataka

Portovi u zaglavlju definiraju UDP protokol kao multiplekser koji omogućava prikupljanje poruka iz aplikacija i njihovo slanje sloju protokola. U ovom slučaju, aplikacija koristi određeni port. Aplikacije koje komuniciraju preko mreže mogu koristiti različite portove, što se odražava u zaglavlju paketa. Ukupno se može definirati 216 različitih portova. Prvih 256 portova je dodijeljeno takozvanim "dobro poznatim uslugama", koje uključuju, na primjer, UDP port 53, koji je dodijeljen DNS servisu.

Polje Dužina određuje ukupnu dužinu poruke. Polje Kontrolni zbroj služi za kontrolu integriteta podataka. Aplikacija koja koristi UDP protokol mora voditi računa o integritetu podataka analizom polja Kontrolna suma i Dužina. Osim toga, prilikom razmjene podataka putem UDP-a, sam aplikativni program mora voditi računa o praćenju isporuke podataka primaocu. To se obično postiže razmjenom potvrda isporuke između aplikacijskih programa.

Najpoznatije usluge zasnovane na UDP-u su BIND usluga imena domena i NFS distribuirani sistem datoteka. Vraćajući se na primjer traceroute, ovaj program također koristi UDP transport. Zapravo, to je UDP poruka koja se šalje u mrežu, ali koristi port koji nema servis, zbog čega se generiše ICMP paket koji detektuje nedostatak usluge na mašini koja prima kada paket konačno stigne do odredišna mašina.

Protokol kontrole prijenosa - TCP

Ako je praćenje kvaliteta prenosa podataka preko mreže važno za aplikaciju, onda se u ovom slučaju koristi TCP protokol. Ovaj protokol se također naziva pouzdanim protokolom orijentiranim na vezu i protokom. Prije diskusije o ovim svojstvima protokola, razmotrimo format datagrama koji se prenosi preko mreže (slika 2.11). Prema ovoj strukturi, TCP, kao i UDP, ima portove. Prvih 256 portova dodijeljeno je WKS-u, portovi 256 do 1024 su dodijeljeni Unix servisima, a ostali se mogu koristiti prema vlastitom nahođenju. Na terenu Sequence Number broj paketa je definiran u nizu paketa koji čini cijelu poruku, nakon čega slijedi polje za potvrdu Broj znanja i druge kontrolne informacije.

Rice. 2.11. Struktura TCP paketa

    Izvorni port (SOURS PORT) zauzima 2 bajta, identifikuje proces slanja;

    Odredišni port (DESTINATION PORT) zauzima 2 bajta, identifikuje proces primaoca;

    Broj sekvence (SEQUENCE NUMBER) zauzima 4 bajta, označava broj bajta, koji određuje pomak segmenta u odnosu na tok poslatih podataka;

    Potvrđeni broj (BROJ POTVRDE) zauzima 4 bajta, sadrži maksimalni broj bajta u primljenom segmentu, uvećan za jedan; to je ta vrijednost koja se koristi kao račun;

    Dužina zaglavlja (HLEN) je duga 4 bita i označava dužinu zaglavlja TCP segmenta, mjerenu u 32-bitnim riječima. Dužina zaglavlja nije fiksna i može varirati ovisno o vrijednostima postavljenim u polju Opcije;

    Rezerva (RESERVED) zauzima 6 bita, polje je rezervisano za kasniju upotrebu;

    Bitovi koda (CODE BITS) zauzimaju 6 bitova i sadrže servisne informacije o tipu datog segmenta, specificirane postavljanjem odgovarajućih bitova ovog polja na jedan:

    URG - hitna poruka;

    ACK - prijem za primljeni segment;

    PSH - zahtjev za slanje poruke bez čekanja da se bafer napuni;

    RST - zahtjev za vraćanje veze;

    SYN - poruka koja se koristi za sinhronizaciju brojača prenetih podataka prilikom uspostavljanja veze;

    FIN je znak da je strana koja prenosi stigla do posljednjeg bajta u toku prijenosa podataka.

    Prozor (WINDOW) zauzima 2 bajta, sadrži deklariranu vrijednost veličine prozora u bajtovima;

    Kontrolna suma (CHECKSUM) zauzima 2 bajta i izračunava se po segmentu;

    Hitni pokazivač (URGENT POINTER) zauzima 2 bajta, koristi se zajedno sa bitom koda URG, označava kraj podataka koji se moraju hitno primiti, uprkos prelivu bafera;

    OPCIJE - ovo polje ima promjenjivu dužinu i može biti odsutno u potpunosti, maksimalna veličina polja je 3 bajta; koristi se za rješavanje pomoćnih problema, na primjer, pri odabiru maksimalne veličine segmenta;

    PADDING, padding promjenljive dužine, je lažno polje koje se koristi za dovođenje veličine zaglavlja na cijeli broj 32-bitnih riječi.

Pouzdanost TCP-a leži u činjenici da izvor podataka ponavlja njihovo slanje osim ako u određenom vremenskom periodu od primaoca ne dobije potvrdu da je uspješno primljen. Ovaj mehanizam se zove Pozitivna svijest s retransmisijom (PAR). Kao što smo prethodno definisali, jedinica prenosa (paket podataka, poruka, itd.) u terminima TCP naziva se segment. U TCP zaglavlju postoji polje kontrolne sume. Ako su podaci oštećeni tokom prijenosa, tada modul koji odvaja TCP segmente od IP paketa može to odrediti pomoću kontrolne sume. Oštećeni paket je uništen i ništa se ne šalje izvoru. Ako podaci nisu oštećeni, oni se prosljeđuju kroz sklop poruka aplikacije, a izvoru se šalje potvrda.

Orijentacija veze je određena činjenicom da prije slanja segmenta podataka, izvorni i odredišni TCP moduli razmjenjuju kontrolne informacije. Ova razmjena se zove rukovanje(bukvalno "rukovanje"). TCP koristi trofazno rukovanje:

    Izvor uspostavlja vezu sa odredištem tako što mu šalje paket sa oznakom Synchronize Sequence Numbers (SYN). Broj u nizu identifikuje broj paketa u aplikacijskoj poruci. Ne mora biti 0 ili jedan. Ali svi ostali brojevi će ga koristiti kao bazu, što će omogućiti prikupljanje paketa ispravnim redoslijedom;

    Primalac odgovara brojem u polju za potvrdu prijema SYN koji odgovara broju koji je odredio izvor. Pored toga, polje „broj u nizu“ takođe može ukazivati ​​na broj koji je tražio izvor;

    Izvor potvrđuje da je prihvatio odredišni segment i šalje prvi podatak.

Grafički je ovaj proces prikazan na slici 2.12.

Rice. 2.12. Uspostavljanje TCP veze

Nakon uspostavljanja veze, izvor šalje podatke primaocu i čeka njegovu potvrdu da je primljen, zatim ponovo šalje podatke, itd., dok se poruka ne završi. Poruka se završava kada se FIN bit postavi u polje zastavice, što znači "nema više podataka".

Priroda striminga protokola određena je činjenicom da SYN određuje početni broj za brojanje prenesenih bajtova, a ne paketa. To znači da ako je SYN postavljen na 0 i 200 bajtova je preneseno, tada će broj postavljen u sljedećem paketu biti 201, a ne 2.

Jasno je da priroda striminga protokola i zahtjev za potvrdom prijema podataka dovode do problema brzine prijenosa podataka. Da biste riješili ovaj problem, koristite "prozor" - polje - prozor. Ideja korištenja prozora je prilično jednostavna: prenosite podatke bez čekanja na potvrdu prijema. To znači da izvor prenosi određenu količinu podataka jednaku prozoru bez čekanja na potvrdu prijema, a nakon toga zaustavlja prijenos i čeka potvrdu. Ako primi potvrdu samo za dio poslanih podataka, počet će slati novi dio sa broja koji slijedi nakon potvrđenog. Ovo je grafički prikazano na slici 2.13.

Rice. 2.13. TCP mehanizam za prijenos podataka

U ovom primjeru, prozor je postavljen na 250 bajtova širine. To znači da je trenutni segment segment sa SYN pomakom od 250 bajtova. Međutim, nakon prijenosa cijelog prozora, izvorni TCP modul je dobio potvrdu da primi samo prvih 100 bajtova. Stoga će prijenos početi od 101 bajta, a ne od 251.

Dakle, ispitali smo sva osnovna svojstva TCP protokola. Ostaje samo navesti najpoznatije aplikacije koje TCP koristi za razmjenu podataka. To su prvenstveno TELNET i FTP, kao i HTTP protokol, koji je srce World Wide Weba.

Prekinimo malo razgovor o protokolima i skrenimo pažnju na tako važnu komponentu cjelokupnog TCP/IP sistema kao što su IP adrese.

Uvod

UDP je jednostavan protokol transportnog sloja orijentiran na datagram: proces izdaje jedan po jedan UDP datagram, što rezultira jednim IP datagramom koji se prenosi. Ovo je u suprotnosti sa protokolima orijentisanim na protok kao što je TCP, gde količina podataka koju aplikacija izbaci praktično nema veze sa brojem poslanih IP datagrama.

Slika 11.1 prikazuje enkapsulaciju UDP datagrama u IP datagram.

Slika 11.1 UDP enkapsulacija.

Zvanična UDP specifikacija je data u RFC 768 [Postel 1980].

UDP je nepouzdan protokol: šalje datagrame koje aplikacija upisuje na IP sloj, ali nema garancije da će stići na svoje konačno odredište. Sa stanovišta pouzdanosti, možda ćete biti u iskušenju da izbjegnete korištenje UDP-a i uvijek koristite pouzdane protokole kao što je TCP. Nakon što je TCP pokriven u , vratit ćemo se na ovu temu i pogledati koje vrste aplikacija mogu koristiti UDP.

Aplikacije ne moraju da brinu o veličini rezultirajućeg IP datagrama. Ako je veći od MTU-a za datu mrežu (pogledajte Poglavlje 2, odjeljak), IP datagram će biti fragmentiran. Ovo se odnosi na svaku mrežu kroz koju će datagram proći na svom putu od izvora do odredišta, osim na prvu mrežu na koju je povezan host koji šalje. (Definisali smo koncept transportnog MTU-a u odeljku u Poglavlju 2.) O fragmentaciji IP-a će se detaljnije govoriti u odeljku ovog poglavlja.

UDP zaglavlje

Slika 11.2 prikazuje polja prisutna u UDP zaglavlju.

Slika 11.2 UDP zaglavlje.

Brojevi portova označavaju procese slanja i primanja. Pokazuje da TCP i UDP koriste broj odredišnog porta za demultipleksiranje podataka koji dolaze sa IP-a. Budući da IP demultipleksira dolazne IP datagrame za TCP i UDP (koristeći vrijednost protokola u IP zaglavlju), TCP gleda na brojeve TCP portova, a UDP na UDP brojeve portova. Brojevi TCP portova su nezavisni od brojeva UDP portova.

Uprkos ovoj nezavisnosti, ako rezervisanu uslugu pružaju i TCP i UDP, isti broj porta se obično bira za oba transportna sloja. Ovo se radi radi praktičnosti, a ne kao zahtjev protokola.

Polje dužine UDP-a sadrži dužinu u bajtovima UDP zaglavlja i UDP podataka. Minimalna vrijednost za ovo polje je 8 bajtova. (Ništa loše se neće dogoditi ako se pošalje UDP datagram sa nultom dužinom podataka.) Parametar dužine UDP-a je suvišan. IP datagram sadrži svoju ukupnu dužinu u bajtovima, tako da je dužina UDP datagrama ukupna dužina minus dužina IP zaglavlja (koje je navedeno u polju dužine zaglavlja na ).

UDP kontrolni zbroj

UDP kontrolni zbir pokriva UDP zaglavlje i UDP podatke. Podsjetimo da kontrolni zbroj u IP zaglavlju pokriva samo IP zaglavlje - ne pokriva podatke sadržane u IP datagramu. I UDP i TCP sadrže kontrolne sume u svojim zaglavljima, koje pokrivaju i zaglavlje i podatke. Za UDP kontrolni zbroj nije obavezan, ali za TCP je obavezan.

UDP kontrolna suma se izračunava na potpuno isti način kao (Poglavlje 3, odeljak) kontrolna suma IP zaglavlja (zbir 16-bitnih reči sa prelivom), iako postoje razlike. Prvo, UDP datagram se može sastojati od neparnog broja bajtova, dok izračunavanje kontrolne sume uključuje dodavanje 16-bitnih riječi. Ovo dodaje nula dopunskih bajtova na kraj datagrama ako je potrebno za izračunavanje kontrolne sume. (Padding bajtovi se ne prenose.)

U UDP-u i TCP-u postoje 12-bajtna pseudo-zaglavlja (u UDP datagramima i TCP segmentima) samo za potrebe izračunavanja kontrolne sume. Pseudo-zaglavlja sadrže određena polja iz IP zaglavlja. Ovo je sve učinjeno kako bi se dvostruko provjerilo da li su podaci stigli na svoje odredište (IP neće prihvatiti datagrame koji nisu adresirani na taj host, i neće moći proslijediti UDP datagrame namijenjene drugom gornjem sloju). Slika 11.3 prikazuje pseudo-zaglavlje i UDP datagram.

Slika 11.3 Polja koja se koriste za izračunavanje UDP kontrolne sume.

Na ovoj slici smo posebno prikazali datagram neparne dužine, u kom slučaju je potreban dodatni bajt za izračunavanje kontrolne sume. Imajte na umu da se dužina UDP datagrama pojavljuje dvaput prilikom izračunavanja kontrolne sume.

Ako je izračunata kontrolna suma 0, pohranjuje se kao svi jedinični bitovi (65535), a ove vrijednosti su ekvivalentne u aritmetici komplementa jedinica. Ako je prenesena kontrolna suma 0, to znači da pošiljalac nije izračunao kontrolnu sumu.

Ako pošiljalac izračuna kontrolnu sumu i primalac utvrdi da postoji greška, UDP datagram se tiho uništava i ne generiše se poruka o grešci. (Ista stvar se dešava ako IP sloj otkrije grešku u kontrolnoj sumi zaglavlja IP-a.)

UDP kontrolni zbroj izračunava pošiljalac i verifikuje ga primalac. Omogućava vam da odredite sve promjene u UDP zaglavlju ili podacima koje su se dogodile duž putanje između pošiljaoca i primatelja.

Iako je UDP kontrolni zbir opcioni parametar, uvijek se mora izračunati. Kasnih 1980-ih, neki proizvođači računara počeli su da podrazumevano onemogućavaju izračunavanje UDP kontrolne sume kako bi povećali brzinu mrežnog sistema datoteka (NFS), koji koristi UDP. Ovo može biti prihvatljivo na jednom LAN-u gdje se CRC izračunava za okvire na sloju veze podataka (Ethernet ili Token ring okviri), koji se mogu koristiti za određivanje oštećenja okvira dok datagram prolazi kroz rutere. Vjerovali ili ne, postoje ruteri koji imaju greške u svom softveru ili hardveru koji mijenjaju bitove u datagramima koje rutiraju. Ove greške se ne mogu otkriti u UDP datagramima ako je opcija kontrolne sume onemogućena. Takođe treba napomenuti da neki protokoli sloja veze (npr. SLIP) nemaju nikakav oblik izračunavanja kontrolne sume za podatke u linku.

Zahtjevi hosta RFC zahtijeva da izračunavanje UDP kontrolne sume bude omogućeno po defaultu. Oni također zahtijevaju da se primljena kontrolna suma mora provjeriti ako ju je izračunao pošiljalac (u slučaju da primljena kontrolna suma nije nula). Neke implementacije zanemaruju ovo i provjeravaju primljenu kontrolnu sumu ako je omogućena opcija za izračunavanje odlazne kontrolne sume.

Izlaz naredbe tcpdump

Prilično je teško odrediti da li je opcija izračunavanja UDP kontrolne sume omogućena na određenom sistemu. Obično aplikacija nema pristup polju kontrolne sume primljenog UDP zaglavlja. Da bi riješio ovaj problem, autor je dodao još jednu opciju programu tcpdump, nakon čega je počeo da izlazi primljene UDP kontrolne sume. Ako je primljena vrijednost 0, to znači da pošiljalac nije izračunao kontrolni zbroj.

Slika 11.4 prikazuje izlaz za i iz tri različita sistema na našoj mreži. Pokrenuli smo sock() program, šaljući jedan UDP datagram sa 9 bajtova podataka na standardni eho server.

>

1 0.0 sun.1900 > gemini.echo: udp 9 (UDP cksum=6e90)
2 0,303755 (0,3038) gemini.echo > sun.1900: udp 9 (UDP cksum=0)

3 17.392480 (17.0887) sun.1904 > aix.echo: udp 9 (UDP cksum=6e3b)
4 17.614371 (0.2219) aix.echo > sun.1904: udp 9 (UDP cksum=6e3b)

5 32.092454 (14.4781) sun.1907 > solaris.echo: udp 9 (UDP cksum=6e74)
6 32.314378 (0.2219) solaris.echo > sun.1907: udp 9 (UDP cksum=6e74)

Slika 11.4 Izlaz iz tcpdump, koji se može koristiti za određivanje da li je opcija UDP checksum omogućena na određenom hostu.

Odavde možemo vidjeti da dva od tri sistema imaju omogućenu opciju UDP checksum.

Takođe imajte na umu da odlazni datagram ima istu kontrolnu sumu kao i dolazni datagram (linije 3 i 4, 5 i 6). Na slici 11.3, primijetit ćete da su dvije IP adrese zamijenjene, kao i dva broja portova. Ostala polja u pseudo-zaglavlju i UDP zaglavlju ostala su ista pošto su podaci odjekivani. Ovo potvrđuje da je UDP kontrolni zbir (i zaista svi kontrolni sumi u porodici TCP/IP protokola) jednostavan 16-bitni zbir. Uz njegovu pomoć nemoguće je otkriti grešku koja se sastoji od promjene mjesta dvije 16-bitne vrijednosti.

Neka statistika

[Mogul 1992] pruža neke statističke informacije o pojavi grešaka kontrolnog zbira na prilično zauzetom NFS serveru koji radi već 40 dana. Slika 11.5 prikazuje statističke podatke.

Broj grešaka u kontrolnim zbrojima

Približan broj paketa

Ethernet
IP
UDP
TCP

Slika 11.5 Statistika oštećenih paketa identifikovanih korišćenjem kontrolnih suma.

Posljednja kolona prikazuje približan broj paketa, budući da drugi protokoli koriste Ethernet i IP sloj. Na primjer, IP datagrami ne koriste sve Ethernet okvire; ARP također koristi Ethernet. UDP ili TCP ne koriste sve IP datagrame, jer ICMP također koristi IP.

Imajte na umu da je otkriveno znatno više grešaka u TCP kontrolnoj sumi nego UDP grešaka u kontrolnoj sumi. Ovo je najvjerovatnije zbog činjenice da TCP obično uspostavlja veze na "duže udaljenosti" (koje prolaze kroz mnoge rutere, mostove itd.), dok je UDP saobraćaj obično lokalni.

Stoga, greške navedene u donjem redu nisu uvijek povezane sa slojem veze podataka (Ethernet, Token ring). Kada prenosite podatke, uvijek biste trebali omogućiti opciju kontrolne sume na krajnjim tačkama. Međutim, ako podaci koji se prenose imaju neku vrijednost, ne biste trebali u potpunosti vjerovati UDP ili TCP kontrolnim sumama, jer su to jednostavni kontrolni sumi i nije zajamčeno da štite podatke od svih mogućih grešaka.

Jednostavan primjer

Koristićemo sock program da generišemo neke UDP datagrame, koje ćemo pogledati koristeći tcpdump:

>bsdi % sock -v -u -i -n4 svr4 odbaciti
povezan na 140.252.13.35.1108 na 140.252.13.34.9

bsdi % sock -v -u -i -n4 -w0 svr4 odbaciti
povezan na 140.252.13.35.1110 na 140.252.13.34.9

U prvom slučaju pokretanja programa postavljen je način za otklanjanje grešaka ( -v), dok možete vidjeti brojeve dinamički dodijeljenih portova, umjesto TCP-a po defaultu je specificiran UDP ( -u), a podešen je izvorni način, opcija ( -i), to znači da ćemo slati podatke, umjesto da čitamo sa standardnog ulaza ili pišemo na standardni izlaz. Opcija -n4 mu govori da izbaci 4 datagrama (umjesto zadanog 1024) na odredišni host svr4. Usluga odbacivanja je opisana u odeljku u poglavlju 1. Koristimo podrazumevanu izlaznu veličinu od 1024 bajta po zapisu.

Drugi put kada smo pokrenuli program, specificirajući -w0, ovo će proizvesti datagrame nulte dužine. Slika 11.6 prikazuje izlaz naredbe tcpdump za dva primjera.

>

1 0.0 bsdi.1108 > svr4.odbaci: udp 1024
2 0,002424 (0,0024) bsdi.1108 > svr4.odbaci: udp 1024
3 0,006210 (0,0038) bsdi.1108 > svr4.odbaci: udp 1024
4 0,010276 (0,0041) bsdi.1108 > svr4.odbaci: udp 1024

5 41.720114 (41.7098) bsdi.1110 > svr4.odbaci: udp 0
6 41.721072 (0.0010) bsdi.1110 > svr4.odbaci: udp 0
7 41.722094 (0.0010) bsdi.1110 > svr4.odbaci: udp 0
8 41.723070 (0.0010) bsdi.1110 > svr4.odbaci: udp 0

Slika 11.6 Izlaz naredbe tcpdump kada se UDP datagrami šalju u jednom smjeru.

Izlaz prikazuje četiri datagrama od 1024 bajta nakon kojih slijede četiri datagrama nulte dužine. Svaki datagram prati prethodni sa intervalom od nekoliko milisekundi. (Bilo je potrebno 41 sekundu da unesete drugu komandu.)

Prije slanja prvog datagrama nije bilo veze između pošiljaoca i primatelja. (U našoj raspravi o TCP-u pokazujemo da se veza mora uspostaviti prije slanja prvog bajta podataka.) Važno je napomenuti da primalac ne izdaje potvrdu kada primi podatke. Pošiljalac, u ovom primjeru, nema pojma da li su podaci primljeni na udaljenom kraju.

Na kraju, imajte na umu da se broj izvornog UDP porta mijenja svaki put kada pokrenete program. Port je prvo bio 1108, a zatim 1110. U poglavlju 1, pokazali smo da se dinamički dodijeljeni brojevi portova koje koriste klijenti obično kreću od 1024 do 5000.

IP fragmentacija

Slika 11.9 prikazuje ICMP format nedostižne greške za ovaj slučaj. Razlikuje se od prikazanog formata jer bitovi 16-31 u drugoj 32-bitnoj riječi mogu sadržavati MTU sljedećeg skoka umjesto da budu postavljeni na 0.

Slika 11.9 ICMP nedostižna greška kada je potrebna fragmentacija, ali je postavljen bit "ne fragmentiraj".

Ako ruter ne podržava ovaj novi format ICMP greške, MTU sljedećeg skoka je postavljen na 0.

Novi zahtjevi za ruterom RFC [Almquist 1993] specificira da ruter mora generirati ovaj novi obrazac kada izda ICMP nedosegljivu poruku.

Problem o kojem ćemo raspravljati nastao je kada smo primili ICMP grešku dok smo pokušavali odrediti MTU SLIP dial-up veze između netb rutera i host sun. Znamo MTU ove veze od sun do netb jer, prvo, to je specificirano prilikom konfigurisanja SLIP-a na host sun, a drugo, vidjeli smo MTU kada smo pokrenuli naredbu netstat u odeljku poglavlja 3. Sada želimo da odredimo MTU u drugom pravcu. (Ovo objašnjava kako odrediti MTU koristeći SNMP.) Za veze od točke do točke, MTU ne mora biti isti u oba smjera.

Za određivanje je korištena sljedeća tehnika. Pokrenuli smo ping od host solarisa do hosta bsdi, povećavajući veličinu paketa podataka sve dok se na pakete nije primijenila fragmentacija. Proces je prikazan na slici 11.10.

Slika 11.10 Sistemi koji su korišteni za određivanje MTU SLIP veze između netb-a i sunca.

Program tcpdump je pokrenut na sun hostu, što nam je omogućilo da vidimo kako se fragmentacija izvodi u SLIP kanalu. Fragmentacija se nije pojavila i sve je bilo u redu dok se veličina podataka u ping paketu nije povećala sa 500 bajtova na 600. Dolazni eho zahtjevi su bili vidljivi (kao da nije bilo fragmentacije), ali eho odgovori su nestali.

Da bi se bolje razumjelo šta se dešava, tcpdump je pokrenut i na bsdi, nakon čega je postalo jasno šta se šalje, a šta prima. Slika 11.11 prikazuje izlaz.

>

1 0.0 solaris >
2 0,000000 (0,0000) bsdi >
3 0,000000 (0,0000) sunce >
treba fragirati, mtu = 0 (DF)

4 0,738400 (0,7384) solaris > bsdi: icmp: zahtjev za eho (DF)
5 0,748800 (0,0104) bsdi > solaris: icmp: eho odgovor (DF)
6 0,748800 (0,0000) sunce > bsdi: icmp: solaris nedostižan -
treba fragirati, mtu = 0 (DF)

Slika 11.11 Izlaz programa tcpdump iz pinga u bsdi iz solarisa sa IP datagramom veličine 600 bajtova.

Prvo, izraz (DF) u svakoj liniji znači da je bit "ne fragmentiraj" u IP zaglavlju postavljen na jedan. To znači da Solaris 2.2 normalno postavlja ovaj bit na jedan kao dio mehanizma za određivanje transportnog MTU-a.

Linija 1 pokazuje da ping prolazi kroz netb do sun bez fragmentacije i sa postavljenim DF bitom, tako da možemo zaključiti da kritična MTU veličina za SLIP host netb još nije dostignuta.

Također, primijetite iz reda broj 2 da se DF zastavica kopira na svaki eho odgovor. Upravo je to uzrokovalo problem. Eho odgovor je iste veličine kao i eho zahtjev (nešto više od 600 bajtova), ali je MTU odlaznog SLIP interfejsa host sun 552. Eho odgovor mora biti fragmentiran, ali je postavljena DF zastavica. Ovo uzrokuje da Sun generiše ICMP nedostižnu grešku i pošalje je na bsdi (gdje je uništena).

Zbog toga nismo vidjeli odjeke iz solarisa. Odgovori nisu prolazili kroz sunce. Slika 11.12 prikazuje putanju paketa.

Slika 11.12 Razmjena paketa za ovaj primjer.

Konačno, imajte na umu da izraz mtu=0 u redovima 3 i 6 na slici 11.11 ukazuje da sun ne vraća MTU za odlazni interfejs u ICMP poruci nedostupno, kao što je prikazano na slici 11.9. (U poglavlju 25, ovaj problem ćemo riješiti korištenjem SNMP-a i uvjeriti se da je SLIP MTU netb interfejsa 1500.)

Određivanje transportnog MTU-a koristeći Traceroute

Budući da većina sistema ne podržava funkciju određivanja transportnog MTU-a, modificirat ćemo program traceroute() tako da može odrediti transportni MTU. Poslat ćemo paket sa postavljenim bitom ne fragmentiraj. Veličina prvog poslanog paketa bit će jednaka MTU odlaznog sučelja. Kada se vrati ICMP greška "ne mogu fragmentirati", smanjit ćemo paket veličina. Ako ruter koji je poslao ICMP grešku podržava novu verziju koja uključuje MTU odlaznog interfejsa u ICMP poruci, koristimo rezultujuću vrijednost; inače ćemo probati sljedeći manji MTU. Kako RFC 1191 [Mogul i Deering 1990] navodi da postoji ograničen broj MTU vrijednosti, naš program ima tabelu mogućih vrijednosti i jednostavno će se preći na sljedeću manju vrijednost.

Pokušajmo sa sličnim algoritamom od hosta sun do host slip, znajući da SLIP kanal ima MTU od 296:

>

sunce % traceroute.pmtu slip

odlazni MTU = 1500
1 bsdi (140.252.13.35) 15 ms 6 ms 6 ms
2 bsdi (140.252.13.35) 6 ms
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 1492
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 1006
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 576
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 552
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 544
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 512
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 508
potrebna je fragmentacija i DF postavljen, pokušava se novi MTU = 296
2 slip (140.252.13.65) 377 ms 377 ms 377 ms

U ovom primjeru, bsdi ruter nije vratio MTU odlaznog interfejsa u ICMP poruci, tako da ćemo pasti na sljedeću nižu MTU vrijednost. Prvi red izlaza za TTL od 2 prijavljuje ime hosta bsdi, ali to je zato što ga je ruter vratio u ICMP grešci. Posljednja linija izlaza za TTL od 2 je upravo ono što smo očekivali.

Lako je modifikovati ICMP kod na bsdi da bi se dobio MTU odlaznog interfejsa. A ako to uradimo i vratimo se na naš program, dobićemo sledeći izlaz:

>

sunce % traceroute.pmtu slip
traceroute to slip (140.252.13.65), 30 skokova max
odlazni MTU = 1500
1 bsdi (140.252.13.35) 53 ms 6 ms 6 ms
2 bsdi (140.252.13.35) 6 ms
potrebna je fragmentacija i DF set, sljedeći skok MTU = 296
2 slip (140.252.13.65) 377 ms 378 ms 377 ms

Ovdje nema smisla isprobavati osam različitih MTU vrijednosti; ruter je prijavio željenu vrijednost.

World Internet

Modificirana verzija traceroutea pokrenuta je nekoliko puta na raznim hostovima širom svijeta. Došao je do 15 zemalja (uključujući Antarktik), koristeći različite transatlantske i transpacifičke kanale. Međutim, prije nego što to učinimo, povećali smo MTU SLIP dial-up veze između naše podmreže i netb rutera (slika 11.12) na 1500, isto kao u Ethernetu.

Od 18 puta kada je program pokrenut, u samo dva slučaja je transportni MTU bio manji od 1500. Jedna transatlantska veza imala je MTU od 572 (čudna vrijednost koja čak nije ni navedena u RFC 1191), a ruter nije vratio ICMP greška na novom formatu. Druga veza između dva rutera u Japanu nije obrađivala okvire od 1500 bajtova, a ruter nije vratio ICMP grešku u novom formatu. Nakon što je MTU smanjen na 1006, sve je radilo.

Zaključak koji možemo izvući iz ovih eksperimenata je da većina (ali ne sve) mreže širokog područja trenutno može rukovati paketima većim od 512 bajtova. Korišćenje funkcije pretrage transportnog MTU-a omogućava aplikacijama da rade znatno efikasnije koristeći veće MTU-ove.

Određivanje transportnog MTU-a kada se koristi UDP

Pogledajmo interakciju između aplikacije koja koristi UDP i mehanizma za otkrivanje transportnog MTU-a. Moramo vidjeti šta se dešava kada aplikacija pošalje datagram koji je prevelik za neki posredni kanal.

Budući da je jedini sistem koji podržava mehanizam za otkrivanje transportnog MTU-a Solaris 2.x, koristimo ga kao izvorni host za slanje 650-bajtnog datagrama na slajd. Budući da se host slip nalazi iza SLIP veze sa MTU od 296, svaki UDP datagram veći od 268 bajtova (296 - 20 - 8) sa postavljenim bitom "ne fragmentiraj" trebao bi uzrokovati ICMP grešku "ne može fragmentirati" iz bsdi-a ruter. Slika 11.13 prikazuje topologiju i MTU kanala.

Slika 11.13 Sistemi koji se koriste za određivanje transportnog MTU-a koristeći UDP.

Sljedeća naredba generiše deset 650-bajtnih UDP datagrama u intervalima od 5 sekundi:

>solaris % čarapa -u -i -n10 -w650 -p5 odbaciti klizanje

Slika 11.14 prikazuje izlaz naredbe tcpdump. Kada je ovaj primjer pokrenut, bsdi ruter je konfiguriran da ne vraća MTU sljedećeg skoka kao dio ICMP greške "ne može fragmentirati".

Prvi datagram poslan sa DF bitom (red 1) generiše očekivanu grešku od bsdi rutera (red 2). Zanimljivo je da sljedeći datagram, također poslan sa DF bitom (red 3), generiše istu ICMP grešku (red 4). Očekivali smo da će ovaj datagram biti poslan sa isključenim DF bitom.

U redu 5, IP je konačno shvatio da se datagrami na ovu destinaciju ne bi trebali slati sa postavljenim DF bitom, a zatim je počeo fragmentirati datagrame na izvornom hostu. Ovo ponašanje se razlikuje od onoga što je prikazano u ranijim primjerima, gdje je IP slao datagrame koje je primio od UDP-a, dok je ruterima sa manjim MTU-ovima (u ovom slučaju bsdi) bilo dozvoljeno da se fragmentiraju.

>

1 0.0 solaris.38196 > slip.discard: udp 650 (DF)
2 0,004218 (0,0042) bsdi > solaris: icmp:

3 4.980528 (4.9763) solaris.38196 > slip.discard: udp 650 (DF)
4 4.984503 (0.0040) bsdi > solaris: icmp:
slip nedostižan - potrebno je fragirati, mtu = 0 (DF)

5 9.870407 (4.8859) solaris.38196 > slip.discard: udp 650 (frag 47942:552@0+)
6 9.960056 (0.0896) solaris > slip: (frag 47942:106@552)

7 14.940338 (4.9803) solaris.38196 > slip.discard: udp 650 (DF)
8 14.944466 (0.0041) bsdi > solaris: icmp:
slip nedostižan - potrebno je fragirati, mtu = 0 (DF)

9 19.890015 (4.9455) solaris.38196 > slip.discard: udp 650 (frag 47944:552@0+)
10 19.950463 (0.0604) solaris > slip: (frag 47944:106@552)

11 24.870401 (4.9199) solaris.38196 > slip.discard: udp 650 (frag 47945:552@0+)
12 24.960038 (0.0896) solaris > slip: (frag 47945:106@552)

13 29.880182 (4.9201) solaris.38196 > slip.discard: udp 650 (frag 47946:552@0+)
14 29.940498 (0.0603) solaris > slip: (frag 47946:106@552)

15 34.860607 (4.9201) solaris.38196 > slip.discard: udp 650 (frag 47947:552@0+)
16 34.950051 (0.0894) solaris > slip: (frag 47947:106@552)

17 39.870216 (4.9202) solaris.38196 > slip.discard: udp 650 (frag 47948:552@0+)
18 39.930443 (0.0602) solaris > slip: (frag 47948:106@552)

19 44.940485 (5.0100) solaris.38196 > slip.discard: udp 650 (DF)
20 44.944432 (0.0039) bsdi > solaris: icmp:
slip nedostižan - potrebno je fragirati, mtu = 0 (DF)

Slika 11.14 Određivanje transportnog MTU-a pomoću UDP-a.

Budući da ICMP poruka "ne može fragmentirati" ne sadrži sljedeći MTU skoka, to znači da je IP odlučio da su svi zadovoljni sa MTU od 576. Prvi fragment (red 5) sadrži 544 bajta UDP podataka, 8 bajtova UDP zaglavlja i 20 bajtova IP zaglavlja, ukupna veličina IP datagrama je 572 bajta. Drugi fragment (red 6) sadrži preostalih 106 bajtova UDP podataka i 20-bajtno IP zaglavlje.

Nažalost, sljedeći datagram, red 7, ima DF bit postavljen, pa ga bsdi odbacuje i vraća ICMP grešku. Ono što se ovdje dogodilo je da je istekao IP tajmer, koji je rekao IP-u da provjeri da li se transportni MTU povećao resetovanjem DF bita. Videćemo da se ovo ponovo dešava u redovima 19 i 20. Upoređujući vremena u redovima 7 i 19 gde je DF bit postavljen na jedan, vidimo da se transportni MTU proverava da se povećava svakih 30 sekundi.

Tajmer od 30 sekundi je prekratak. RFC 1191 preporučuje postavljanje tajmera na 10 minuta. Vrijednost tajmera može se promijeniti korištenjem parametra ip_ire_pathmtu_interval (Dodatak E, odjeljak). U Solarisu 2.2 ne postoji način da se onemogući detekcija transportnog MTU-a za jednu UDP aplikaciju ili za sve UDP aplikacije. Može se omogućiti ili onemogućiti samo za cijeli sistem u cjelini promjenom parametra ip_path_mtu_discovery. Kao što možemo vidjeti iz ovog primjera, omogućavanje funkcije detekcije transportnog MTU-a kada UDP aplikacije šalju datagrame za koje je vjerovatno da će biti fragmentirani, rezultirat će odbacivanjem datagrama.

Maksimalna veličina datagrama koju prihvata IP sloj na solarisu (576 bajtova) je netačna. Na slici 11.13 smo vidjeli da je stvarni MTU 296 bajtova. To znači da se fragmenti koje generiše solaris ponovo fragmentiraju na bsdi. Slika 11.15 prikazuje tcpdump izlaz primljen na odredišnom (slip) hostu za prvi datagram koji je stigao (redovi 5 i 6 na slici 11.14).

>

1 0.0 solaris.38196 > slip.discard: udp 650 (frag 47942:272@0+)
2 0,304513 (0,3045) solaris > slip: (frag 47942:272@272+)
3 0,334651 (0,0301) solaris > slip: (frag 47942:8@544+)
4 0,466642 (0,1320) solaris > slip: (frag 47942:106@552)

Slika 11.15 Prvi datagram koji stiže na slip host iz solarisa.

U ovom primjeru, solaris host ne bi trebao fragmentirati odlazne datagrame, već bi trebao isključiti DF bit i dozvoliti ruteru s manjim MTU-om da izvrši fragmentaciju.

Sada ćemo pokrenuti isti primjer, ali promijenimo ponašanje bsdi rutera tako da vraća MTU sljedećeg skoka u ICMP poruci "ne može fragmentirati". Slika 11.16 prikazuje prvih šest linija tcpdump izlaza.

>

1 0.0 solaris.37974 > slip.discard: udp 650 (DF)
2 0,004199 (0,0042) bsdi > solaris: icmp:

3 4.950193 (4.9460) solaris.37974 > slip.discard: udp 650 (DF)
4 4.954325 (0.0041) bsdi > solaris: icmp:
slip nedostižan - potrebno je fragirati, mtu = 296 (DF)

5 9.779855 (4.8255) solaris.37974 > slip.discard: udp 650 (frag 35278:272@0+)
6 9.930018 (0.1502) solaris > slip: (frag 35278:272@272+)
7 9.990170 (0.0602) solaris > slip: (frag 35278:114@544)

Slika 11.16 Određivanje transportnog MTU-a pomoću UDP-a.

Još jednom vidimo da su prva dva datagrama poslana sa postavljenim DF bitom, a oba su primila ICMP greške. Trenutno, ICMP greška ukazuje na sljedeći MTU naprijed, a to je 296.

U redovima 5, 6 i 7 vidimo da izvorni host vrši fragmentaciju, kao na slici 11.14. Ako je MTU sljedećeg skoka poznat, generišu se samo tri fragmenta, u poređenju sa četiri fragmenta koje generiše bsdi ruter na slici 11.15.

Interakcija između UDP-a i ARP-a

Koristeći UDP, možemo pogledati vrlo zanimljivu interakciju između UDP-a i tipične ARP implementacije.

Koristimo sock program da generišemo jedan UDP datagram sa 8192 bajta podataka. Očekujemo da će u ovom slučaju biti generisano šest Ethernet fragmenata (vidi Poglavlje 11). Također, prije pokretanja programa, uvjerit ćemo se da je ARP keš prazan, tako da prije slanja prvog fragmenta, ARP zahtjev i odgovor moraju biti razmijenjeni.

>

bsdi % arp -a provjerite da li je ARP keš prazna
bsdi % sock -u -i -n1 -w8192 svr4 odbaciti

Očekujemo da će prvi datagram izazvati slanje ARP zahtjeva. Sljedećih pet fragmenata koji su generirani putem IP-a postavljaju dva pitanja na koja možemo odgovoriti samo korištenjem tcpdump-a: hoće li preostali fragmenti biti spremni za slanje prije nego što se primi ARP odgovor, ako je tako, šta će ARP učiniti sa ovih nekoliko paketa? na određeno odredište dok se čeka ARP odgovor? Slika 11.17 prikazuje izlaz programa tcpdump.

>

1 0.0 arp ko-ima svr4 reci bsdi
2 0,001234 (0,0012) arp tko-ima svr4 reci bsdi
3 0,001941 (0,0007) arp tko-ima svr4 reci bsdi
4 0,002775 (0,0008) arp tko-ima svr4 reci bsdi
5 0,003495 (0,0007) arp tko-ima svr4 reci bsdi
6 0,004319 (0,0008) arp tko-ima svr4 reci bsdi
7 0.008772 (0.0045) arp odgovor svr4 is-at 0:0:c0:c2:9b:26
8 0,009911 (0,0011) arp odgovor svr4 is-at 0:0:c0:c2:9b:26
9 0,011127 (0,0012) bsdi > svr4: (frag 10863:800@7400)
10 0.011255 (0.0001) arp odgovor svr4 is-at 0:0:c0:c2:9b:26
11 0,012562 (0,0013) arp odgovor svr4 is-at 0:0:c0:c2:9b:26
12 0,013458 (0,0009) arp odgovor svr4 is-at 0:0:c0:c2:9b:26
13 0.014526 (0.0011) arp odgovor svr4 is-at 0:0:c0:c2:9b:26
14 0,015583 (0,0011) arp odgovor svr4 is-at 0:0:c0:c2:9b:26

Slika 11.17 Razmjena paketa prilikom slanja UDP datagrama od 8192 bajta preko Etherneta.

Ovaj zaključak je prilično neočekivan. Prvo, prije nego što se primi prvi ARP odgovor, generira se šest ARP zahtjeva. Kao što možete pretpostaviti, IP brzo generiše šest fragmenata, a za svaki se šalje ARP zahtjev.

Zatim, kada se primi prvi ARP odgovor (red 7), šalje se samo posljednji fragment (red 9)! To znači da je prvih pet fragmenata odbačeno. U stvarnosti, ovo je primjer normalnog ARP rada. Većina implementacija zadržava samo posljednji paket koji se šalje odredišnom hostu dok čeka ARP odgovor.

Zahtjevi hosta RFC zahtijevaju implementacije za sprječavanje ARP poplave (uzastopno slanje ARP zahtjeva za istu IP adresu sa velikom frekvencijom). Preporučena maksimalna frekvencija je jednom u sekundi. Ovdje vidimo šest ARP zahtjeva unutar 4,3 milisekundi. Zahtjevi hosta RFC zahtijevaju da ARP pohrani najmanje jedan paket i to mora biti najnoviji paket. To je upravo ono što smo vidjeli ovdje.

Sljedeća neobjašnjiva anomalija je da je svr4 vratio sedam ARP odgovora, a ne šest.

Posljednja stvar koju treba primijetiti je da je tcpdump radio još 5 minuta nakon što se vratio zadnji ARP odgovor, čekajući da vidi svr4 da pošalje natrag ICMP grešku "vrijeme je prekoračeno tokom ponovnog sastavljanja". ICMP poruka se nikada nije pojavila. (Prikazali smo format ove poruke u . Polje koda postavljeno na 1 označava da je proteklo vrijeme dok se datagram ponovo sastavlja.)

IP sloj mora pokrenuti tajmer kada se pojavi prvi fragment datagrama. Ovdje "prvi" znači prvi fragment koji stiže za dati datagram, a ne samo prvi fragment (sa pomakom fragmenta od 0). Tipična vrijednost vremenskog ograničenja kreće se od 30 do 60 sekundi. Ako svi fragmenti za ovaj datagram nisu stigli prije isteka tajmera, svi fragmenti se odbacuju. Ako se to ne učini, fragmenti koji nikada ne stignu (kao što smo vidjeli u ovom primjeru) mogu uzrokovati prelijevanje bafera za primanje.

Dva su razloga zašto nismo vidjeli ICMP poruku. Prvo, većina Berkeley implementacija nikada ne generiše ovu grešku! Ove implementacije postavljaju tajmer i odbacuju sve fragmente kada tajmer istekne, ali se ne generiše ICMP greška. Drugo, prvi fragment - fragment sa pomakom jednakim 0, koji sadrži UDP zaglavlje, nije primljen. (Ovo je bio prvi od pet paketa koje je ARP ispustio.) Implementacija nije potrebna za generiranje ICMP greške ako prvi fragment nije primljen. Razlog je taj što ICMP ponor grešaka ne može reći koji je korisnički proces poslao datagram koji je odbačen jer zaglavlje transportnog sloja nije bilo dostupno. A viši nivo (bilo TCP aplikacija ili UDP aplikacija) će isteći i ponoviti prijenos.

U ovom odeljku koristili smo IP fragmentaciju da pogledamo kako funkcioniše komunikacija između UDP-a i ARP-a. Ova interakcija se može vidjeti ako pošiljalac pošalje više UDP datagrama u brzom slijedu. Koristili smo fragmentaciju jer se paketi brzo generiraju pomoću IP-a, što je mnogo brže nego da korisnički proces generiše više paketa.

Maksimalna veličina UDP datagrama

Teoretski, maksimalna veličina IP datagrama može biti 65535 bajtova, što je ograničeno 16-bitnim poljem pune dužine u IP zaglavlju (vidi). Sa dužinom IP zaglavlja od 20 bajtova i dužinom UDP zaglavlja od 8 bajtova, UDP datagram ostavlja maksimalno 65507 bajtova za korisničke podatke. Većina implementacija, međutim, koristi značajno manje datagrame.

Obično dolaze u obzir dva ograničenja. Prvo, aplikacijski program može biti ograničen svojim programskim sučeljem. Sockets API (poglavlje 1, odjeljak) pruža funkciju koju aplikacija može pozvati za postavljanje veličine ulaznih i izlaznih bafera. Za UDP utičnicu, ova veličina je direktno povezana sa maksimalnom veličinom UDP datagrama koji UDP može čitati i pisati. Trenutno, većina sistema obezbeđuje podrazumevanu maksimalnu veličinu UDP datagrama koji se može čitati ili pisati, a to je 8192 bajta. (Ova vrijednost je postavljena na 8192 jer je to ono što NFS čita i piše po defaultu.)

Sljedeće ograničenje je određeno implementacijom TCP/IP kernela. Mogu postojati karakteristike implementacije (ili greške) koje ograničavaju veličinu UDP datagrama na manje od 65535 bajtova.

Autor je eksperimentisao sa različitim veličinama UDP datagrama koristeći sock program. Koristeći loopback interfejs pod SunOS 4.1.3, maksimalna veličina UDP datagrama bila je 32767 bajtova. Nije bilo moguće koristiti veću vrijednost. Prilikom prijenosa preko Etherneta sa BSD/386 na SunOS 4.1.3, maksimalna veličina IP datagrama koju je Sun mogao prihvatiti bila je 32786 (sa 32758 bajtova korisničkih podataka). Koristeći loopback interfejs u Solarisu 2.2, maksimalna veličina IP datagrama koji se mogao poslati i primiti bila je 65535 bajtova. Prilikom prijenosa sa Solarisa 2.2 na AIX 3.2.2, bilo je moguće prenijeti IP datagram maksimalne veličine od 65535 bajtova.

Skraćivanje datagrama

Samo zato što IP može slati i primati datagrame određene veličine ne znači da je aplikacija koja prima datagrame spremna za čitanje datagrama te veličine. UDP API omogućava aplikacijama da specificiraju maksimalan broj bajtova koji će se obraditi u jednom trenutku. Šta se dešava ako je primljeni datagram veći od datagrama koji je aplikacija spremna da prihvati?

Nažalost, odgovor zavisi od programskog interfejsa i implementacije.

Tradicionalne verzije Berkeley socket API-ja skraćuju datagrame, odbacujući sve podatke koji se ne uklapaju. Hoće li aplikacija biti obaviještena ovisi o verziji. (4.3 BSD Reno i novije verzije mogu obavijestiti aplikaciju da je datagram skraćen.) API utičnice pod SVR4 (uključujući Solaris 2.x) ne skraćuju datagrame. Svi podaci koji se ne uklapaju čitaju se uzastopno. Aplikacija nije obaviještena o više ciklusa čitanja i bit će poslat jedan UDP datagram. TLI API-ji ne odbacuju podatke. Umjesto toga, vraća se zastavica koja pokazuje da ima više podataka nego što se može pročitati odjednom, tako da aplikacija počinje uzastopno čitati preostali datagram.

Kada budemo raspravljali o TCP-u, vidjet ćemo da ovaj protokol obezbjeđuje sekvencijalne tokove bajtova aplikaciji bez ikakvih ograničenja. TCP aplikaciji isporučuje podatke bilo koje veličine koje aplikacija zahtijeva - i nikakvi podaci se nikada ne gube na tom interfejsu.

Greška suzbijanja ICMP izvora

Koristeći UDP, možete generirati grešku gašenja ICMP izvora. Ovu grešku može generirati sistem (ruter ili host) kada prima datagrame brže nego što se ti datagrami mogu obraditi. Obratite pažnju na izraz "može biti". Sistem ne zahtijeva supresiju slanja izvora čak i ako su baferi puni i datagrami su odbačeni.

Slika 11.18 prikazuje ICMP format greške potiskivanja izvora. U idealnoj smo poziciji da generišemo sličnu grešku na našoj test mreži. Možemo slati datagrame sa bsdi na ruter sun preko Etherneta, a ovi datagrami moraju biti rutirani kroz SLIP kanal. Pošto je SLIP kanal oko hiljadu puta sporiji od Etherneta, lako možemo preliti bafer. Sljedeća naredba šalje 100 1024 bajt datagrama od hosta bsdi preko rutera sun u solaris. Datagrame šaljemo standardnom servisu odbacivanja, gdje će biti zanemareni:

>bsdi % sock -u -i -w1024 -n100 solaris odbaciti

Slika 11.18 Greška suzbijanja ICMP izvora.

Slika 11.19 prikazuje tcpdump izlaz koji odgovara ovoj naredbi.

>

1 0.0 bsdi.1403 > solaris.discard: udp 1024
26 linija nije prikazano
27 0.10 (0.00) bsdi.1403 > solaris.discard: udp 1024
28 0.11 (0.01) sunce > bsdi: icmp: gašenje izvora

29 0.11 (0.00) bsdi.1403 > solaris.discard: udp 1024
30 0.11 (0.00) sunce > bsdi: icmp: gašenje izvora
142 linije nisu prikazane
173 0,71 (0,06) bsdi.1403 > solaris.discard: udp 1024
174 0.71 (0.00) sunce > bsdi: icmp: gašenje izvora

Slika 11.19 ICMP potiskivanje izvora od sunca rutera.

Uklonili smo mnoge redove iz ovog izlaza. Prvih 26 datagrama primljeno je bez grešaka: prikazali smo samo izlaz za prvi. Počevši od 27. datagrama, svaki put kada se pošalje datagram dobijamo grešku potiskivanja izvora. Ukupno je bilo 26+(74x2)=174 linije.

Iz našeg izračunavanja širine opsega serijske linije u Poglavlju 2, možemo vidjeti da bi bilo potrebno više od jedne sekunde za prijenos 1024-bajtnog datagrama brzinom od 9600 bps. (U našem primjeru, ovo će trajati duže jer će datagram od 20+8+1024 bajta biti fragmentiran jer je MTU SLIP veze od sunca do netb 552 bajta.) Međutim, iz vremena prikazanih na slici 11.19, vidimo da je ruter sun primio svih 100 datagrama za manje od jedne sekunde prije nego što je prvi poslat na SLIP kanal. Jasno je da smo koristili mnoge njegove bafere.

Iako RFC 1009 zahtijeva da ruter generiše greške potiskivanja izvora kada se njegovi baferi popune, novi zahtjevi za ruterom RFC [Almquist 1993] to mijenja i kaže da ruter ne smije generirati greške potiskivanja izvora.

Sljedeća stvar koju treba primijetiti u primjeru je da sock program nikada nije primio obavještenja da je izvor potisnut, ili ako jeste, ignorirao ih je. Ovo sugerira da BSD implementacije obično zanemaruju primljene poruke potiskivanja izvora kada se koristi UDP protokol. (U slučaju TCP-a, kada se primi obavijest, prijenos podataka na vezi za koju se generiše potiskivanje izvora je usporen. O tome ćemo raspravljati u poglavlju 21.) Problem je u tome što proces koji je generirao podatke koji je zauzvrat uzrokovao potiskivanje izvora možda je već završeno kada je primljena poruka o potiskivanju izvora. Zaista, ako koristimo Unix vremenski program za procjenu koliko dugo je sock program bio pokrenut, otkrićemo da je radio samo oko 0,5 sekundi. Međutim, vidjeli smo na slici 11.19 da su neke poruke potiskivanja izvora primljene 0,71 sekundu nakon što je prvi datagram poslan, odnosno nakon što je proces prestao da radi. Šta će se dogoditi ako naš program proizvede 100 datagrama i izađe. Neće biti poslano svih 100 datagrama - neki od njih će biti u izlaznom redu.

Ovaj primjer dokazuje da je UDP nepouzdan protokol i pokazuje važnost kontrole toka. Iako je sock program uspješno poslao 100 datagrama na mrežu, samo 26 je stiglo na odredište. Preostala 74 je najvjerovatnije odbačena od strane srednjeg rutera. Ako aplikacija ne podržava neki oblik obavještenja, pošiljatelj ne zna da li je primalac prihvatio podatke.

UDP server

Postoji nekoliko karakteristika korišćenja UDP-a koje utiču na dizajn i implementaciju servera. Dizajniranje i implementacija klijenata obično je lakše od implementacije servera. Zbog toga ćemo ovdje raspravljati o razvoju servera, a ne o razvoju klijenata. Serveri obično stupaju u interakciju sa operativnim sistemom, a većina servera zahteva da postoji neki način za rukovanje zahtevima od više klijenata u isto vreme.

Obično, kada se klijent pokrene, on odmah uspostavlja vezu sa jednim serverom. Serveri se, s druge strane, pokreću i onda spavaju dok čekaju da stigne zahtjev od klijenta. U slučaju UDP-a, server se "budi" kada datagram stigne od klijenta, ovaj datagram može sadržavati zahtjev u nekom obliku.

Nećemo razmatrati programske aspekte klijenata i servera (on sve detaljnije opisuje), ali ćemo razmotriti karakteristike UDP protokola koje utiču na dizajn i implementaciju servera koji koristi UDP. (O detaljima TCP servera ćemo razgovarati u odeljku u poglavlju 18.) Razgovaraćemo o nekim karakteristikama u zavisnosti od UDP implementacija koje će se koristiti, a takođe ćemo pogledati karakteristike koje su zajedničke većini implementacija.

IP adresa klijenta i broj porta

Od klijenta stiže UDP datagram. IP zaglavlje sadrži izvornu i odredišnu IP adresu, a UDP zaglavlje sadrži izvorne i odredišne ​​brojeve UDP portova. Kada aplikacija primi UDP datagram, operativni sistem joj mora reći ko je poslao poruku – izvornu IP adresu i broj porta.

Ova karakteristika omogućava jednom UDP serveru da rukuje više klijenata. Svaki odgovor se šalje klijentu koji je poslao zahtjev.

Odredišna IP adresa

Neke aplikacije moraju znati kome je datagram namijenjen, odnosno odredišnoj IP adresi. Na primjer, Host Requirements RFC specificira da TFTP server mora zanemariti primljene datagrame koji se šalju na adresu emitiranja. (Opisaćemo adresiranje emitovanja u , a TFTP u .)

To znači da operativni sistem mora proslediti odredišnu IP adresu iz primljenog UDP datagrama aplikaciji. Nažalost, ne pružaju sve implementacije ovu funkciju.

API utičnica pruža ovu mogućnost koristeći opciju IP_RECVDSTADDR. Od sistema obrađenih u tekstu, samo BSD/386, 4.4BSD i AIX 3.2.2 podržavaju ovu opciju. SVR4, SunOS 4.x i Solaris 2.x nisu podržani.

UDP ulazni red

U poglavlju 1, rekli smo da većina UDP servera može poslužiti sve zahtjeve klijentima koristeći jedan UDP port (unaprijed poznati portovi servera).

Obično je veličina ulaznog reda povezanog sa svakim UDP portom koji koristi aplikacija ograničena. To znači da se zahtjevi koji stignu u isto vrijeme od različitih klijenata automatski stavljaju u red čekanja u UDP redu. Primljeni UDP datagrami se šalju aplikaciji (kada ona zatraži sljedeći datagram) redoslijedom kojim su primljeni.

Međutim, postoji mogućnost, ako je red pun, da će UDP modul u kernelu odbaciti dolazne datagrame. To možemo uočiti sa sljedećim eksperimentom. Pokrećemo naš sock program na bsdi hostu i tako pokrećemo UDP server:

>

bsdi % čarapa -s -u -v -E -R256 -r256 -P30 6666
od 140.252.13.33, do 140.252.13.63: 1111111111 od sunca do adrese emitovanja
sa 140.252.13.34, na 140.252.13.35: 4444444444444 sa svr4 na ličnu adresu

Koristili smo sljedeće zastavice: -s, pokreće program kao server, -u za UDP, -v, ispisuje IP adresu klijenta, a -E ispisuje odredišnu IP adresu (u ovom slučaju sistem to dozvoljava). Pored toga, postavili smo bafer za prijem UDP-a za ovaj port na 256 bajtova ( -R), zajedno sa veličinom koju može pročitati svaka aplikacija ( -r). Oznaka -P30 vam govori da sačekate 30 sekundi nakon brisanja UDP porta prije čitanja prvog datagrama. Ovo nam daje vremena da pokrenemo klijente na dva druga hosta, pošaljemo neke datagrame i vidimo kako funkcionira red primanja.

Nakon što se server pokrene i prođe pauza od 30 sekundi, pokrećemo jednog klijenta na host suncu i šaljemo tri datagrama:

>

sunce % sock -u -v 140.252.13.63 6666 na Ethernet adresu emitiranja
povezan na 140.252.13.33.1252 na 140.252.13.63.6666
1111111111 11 bajtova podataka (sa novim redom)
222222222 10 bajtova podataka (sa novim redom)
33333333333 12 bajtova podataka (sa novim redom)

Odredišna adresa je adresa emitiranja (140.252.13.63). Zatim smo pokrenuli drugog klijenta na svr4 hostu i poslali još tri datagrama:

>

svr4% čarapa -u -v bsdi 6666
povezan na 0.0.0.0.1042 na 140.252.13.35.6666
4444444444444 14 bajtova podataka (sa novim redom)
555555555555555 16 bajtova podataka (sa novim redom)
66666666 9 bajtova podataka (sa novim redom)

Prva stvar koju treba primijetiti u interaktivnom izlazu prikazanom ranije na bsdi-u je da su samo dva datagrama primljena od strane aplikacije: prvi od sun, koji je bio sve jedan, i prvi od svr4, koji je bio sve četiri. Preostala četiri datagrama su odbačena.

Izlaz komande tcpdump na slici 11.20 pokazuje da je svih šest datagrama isporučeno na odredišni host. Datagrami su stigli od dva klijenta obrnutim redoslijedom: prvo od sunca, zatim od svr4 i tako dalje. Također možemo primijetiti da je svih šest isporučeno za oko 12 sekundi, tokom perioda od 30 sekundi dok je server spavao.

>

1 0.0 sun.1252 > 140.252.13.63.6666: udp 11
2 2.499184 (2.4992) svr4.1042 > bsdi.6666: udp 14
3 4.959166 (2.4600) sun.1252 > 140.252.13.63.6666: udp 10
4 7.607149 (2.6480) svr4.1042 > bsdi.6666: udp 16
5 10.079059 (2.4719) sun.1252 > 140.252.13.63.6666: udp 12
6 12.415943 (2.3369) svr4.1042 > bsdi.6666: udp 9

Slika 11.20 tcpdump izlaz za UDP datagrame koje šalju dva klijenta.

Takođe treba napomenuti da sa opcijom -E server može saznati odredišnu IP adresu svakog datagrama. Server može izabrati šta da radi sa prvim primljenim datagramom koji je poslat na adresu emitovanja.

U ovom primjeru potrebno je obratiti pažnju na još neke karakteristike. Prvo, aplikacija nije prijavila kada je ulazni red pun. Dodatni UDP datagrami su jednostavno odbačeni. Takođe vidimo u tcpdump izlazu da ništa nije poslano nazad klijentu da ga obavesti da su datagrami odbačeni. Ništa poput ICMP poruke o suzbijanju izvora nije poslano, apsolutno ništa. Na kraju, želimo istaći da UDP ulazni red radi na FIFO (prvi ušao, prvi izašao), dok, kao što smo vidjeli u dijelu ovog poglavlja, ARP ulazni red radi na LIFO (posljednji u, prvi izlaz).

Ograničenje lokalne IP adrese

Većina UDP servera koristi zamjenske znakove za svoje IP adrese prilikom kreiranja UDP krajnjih tačaka. To znači da će dolazni UDP datagram namijenjen portu servera biti prihvaćen od strane bilo kojeg lokalnog sučelja. Na primjer, možemo pokrenuti UDP server na portu 7777:

>sun % čarapa -u -s 7777

Zatim ćemo koristiti naredbu netstat da vidimo status ove krajnje točke:

>

sunce % netstat -a -n -f inet
Aktivne internetske veze (uključujući servere)
Proto Recv-Q Pošalji-Q Lokalna adresa Strana adresa (država)
udp 0 0 *.7777 *.*

U ovom izlazu smo uklonili mnoge redove i ostavili samo one koji su nam zanimljivi. Oznaka -a izvještava o svim krajnjim tačkama mreže. Oznaka -n ispisuje IP adrese u decimalnom zapisu umjesto korištenja DNS-a i pretvaranja adresa u imena i ispisuje brojeve portova umjesto imena usluga. Opcija -f inet prijavljuje samo TCP i UDP krajnje tačke.

Lokalna adresa je ispisana kao *.7777, pri čemu zvjezdica znači da se bilo koja adresa može zamijeniti kao lokalna IP adresa.

Kada server kreira svoju krajnju tačku, može navesti jednu od lokalnih IP adresa domaćina, uključujući jednu od njegovih broadcast adresa, kao lokalnu IP adresu krajnje tačke. U ovom slučaju, dolazni UDP datagram će se prenijeti do krajnje točke samo ako odredišna IP adresa odgovara navedenoj lokalnoj adresi. Koristeći sock program, možete odrediti IP adresu prije broja porta, a ova IP adresa postaje lokalna IP adresa za krajnju tačku. Na primjer,

>sun % sock -u -s 140.252.1.29 7777

iz datagrama koji pristižu na SLIP interfejs, bira datagrame sa adresom 140.252.1.29. Izlaz naredbe netstat će izgledati ovako:

>


udp 0 0 140.252.1.29.7777 *.*

Ako pokušamo da pošaljemo datagram ovom serveru sa hosta bsdi, čija je adresa 140.252.13.35, preko Etherneta, vratiće se ICMP greška o nedostupnosti porta. Server nikada neće vidjeti ovaj datagram. Slika 11.21 prikazuje ovo detaljnije.

>

1 0.0 bsdi.1723 > sun.7777: udp 13
2 0,000822 (0,0008) sun > bsdi: icmp: sun udp port 7777 nedostižan

Slika 11.21 Greška obrade UDP datagrama uzrokovana nepodudaranjem lokalne adrese servera.

Moguće je pokrenuti druge servere na istom portu, svaki sa svojom lokalnom IP adresom. Međutim, aplikacija mora dozvoliti sistemu da ponovo koristi isti broj porta.

Opcija utičnice mora biti specificirana u SO_REUSEADDR API-ju. To radi naš sock program koristeći opciju -A.

Na Sun hostu možemo pokrenuti pet različitih servera na istom UDP portu (8888):

>

sunce % sock -u -s 140.252.1.29 8888 za SLIP kanal
sunce % čarapa -u -s -A 140.252.13.33 8888 za Ethernet
sunce % sock -u -s -A 127.0.0.1 8888 za loopback interfejs
sunce % čarapa -u -s -A 140.252.13.63 8888 za Ethernet zahtjeve za emitiranje
sunce % čarapa -u -s -A 8888 za sve ostalo (metaznakovi u IP adresi)

Očekivalo se da će prvi server biti pokrenut sa -A zastavicom, koja govori sistemu da se isti broj porta može ponovo koristiti. Izlaz naredbe netstat prikazuje sljedećih pet servera:

>

Proto Recv-Q Pošalji-Q Lokalna adresa Strana adresa (država)
udp 0 0 *.8888 *.*
udp 0 0 140.252.13.63.8888 *.*
udp 0 0 127.0.0.1 8888 *.*
udp 0 0 140.252.13.33 8888 *.*
udp 0 0 140.252.1.29 8888 *.*

U ovom scenariju, samo datagrami namijenjeni adresi 140.252.1.255 će pogoditi server sa zamjenskim znakovima koji se koriste kao lokalna IP adresa, jer ostala četiri servera pokrivaju sve moguće adrese.

Kada se koristi zamjena IP adrese, koristi se sistem prioriteta. Krajnja tačka sa navedenom IP adresom koja odgovara odredišnoj IP adresi uvijek će biti odabrana prije zamjenske adrese. Krajnja točka zamjenskog znaka se koristi samo kada nije pronađeno podudaranje za navedenu adresu.

Ograničavanje eksternih IP adresa

U svim rezultatima naredbe netstat koji smo ranije pokazali, udaljene IP adrese i brojevi udaljenih portova prikazani su kao *.*. To znači da će krajnja tačka prihvatiti dolazne UDP datagrame sa bilo koje IP adrese i bilo kojeg broja porta. Većina implementacija dozvoljava UDP krajnjim tačkama da ograniče udaljene adrese.

Drugim riječima, krajnja tačka može prihvatiti samo UDP datagrame sa specificirane IP adrese i broja porta. Naš sock program koristi opciju -f da odredi udaljenu IP adresu i broj porta:

>sun % čarapa -u -s -f 140.252.13.35.4444 5555

U ovom slučaju, udaljena IP adresa je postavljena na 140.252.13.35 (naš host bsdi), a broj udaljenog porta na 4444. Unaprijed poznati port servera je 5555. Ako pokrenemo netstat, vidjet ćemo da je lokalna IP adresa je također postavljen, čak i ako ga nismo direktno naveli:

>

Proto Recv-Q Pošalji-Q Lokalna adresa Strana adresa (država)
udp 0 0 140.252.13.33.5555 140.252.13.35.4444

Ovo nuspojava Određivanje udaljene IP adrese i broja udaljenog porta na Berkeley sistemima: ako lokalna IP adresa nije odabrana prilikom postavljanja udaljene adrese, lokalna adresa se bira automatski. Lokalna IP adresa je postavljena na IP adresu sučelja koja je odabrana korištenjem IP rutiranja za postizanje navedene udaljene IP adrese. Zaista, u ovom primjeru, IP adresa Sun za Ethernet koji je povezan na udaljenu adresu je 140.252.13.33.

Slika 11.22 prikazuje tri tipa adresa i portova koje UDP server može postaviti za sebe.

Slika 11.22 Određivanje lokalne i udaljene IP adrese i broja porta za UDP server.

U svim slučajevima, lport je unaprijed poznati port servera, a localIP mora biti IP adresa lokalnog sučelja. Redoslijed u kojem su tri reda raspoređene na slici 11.22 pokazuje redoslijed kojim UDP modul pokušava odrediti koja je lokalna krajnja točka primila dolazni datagram. Najrestriktivnija asocijacija adresa-port (prvi red) se bira prva, a ona manje restriktivna (poslednji red, gde su i IP adresa i broj porta specificirani kao metaznakovi) se bira poslednji.

Višestruki prijem po portu

Iako nije navedeno u RFC-u, većina implementacija dozvoljava da se samo jedna aplikacija istovremeno poveže s jednom lokalnom adresom i brojem UDP porta. Kada UDP datagram stigne na odredišni host na njegovu IP adresu i broj porta, jedna kopija se isporučuje na jednu krajnju tačku. IP adresa krajnje tačke može biti predstavljena kao zamjenski znakovi, kao što je ranije prikazano.

Na primjer, u SunOS-u 4.1.3 smo pokrenuli jedan server na portu 9999 s lokalnom IP adresom u obliku zamjenskih znakova:

>sun % čarapa -u -s 9999

Ako zatim pokušamo pokrenuti drugi server s istom lokalnom adresom zamjenskog znaka i istim portom, neće raditi, čak i ako navedemo opciju -A:

>

sunce % čarapa -u -s 9999 ne bi trebalo da se desi ovako
ne mogu vezati lokalnu adresu: Adresa se već koristi

sunce % čarapa -u -s -A 9999 zato smo naveli -A zastavicu
ne mogu vezati lokalnu adresu: Adresa se već koristi

Za sisteme koji podržavaju multicast (pogledajte), stvari su drugačije. Više krajnjih točaka može koristiti istu lokalnu adresu i broj UDP porta, ali aplikacija mora reći API-ju da je to dozvoljeno (-A zastavica koristi opciju SO_REUSEADDR socketa).

4.4BSD, koji podržava višestruko emitiranje, zahtijeva od aplikacije da postavi drugu opciju utičnice (SO_REUSEPORT) kako bi se omogućilo da više krajnjih tačaka dijele isti port. Štaviše, svaka krajnja točka mora specificirati ovu opciju, uključujući prvu krajnju točku koja koristi ovaj port.

Kada UDP datagram stigne na svoju odredišnu IP adresu, koja je broadcast ili multicast adresa, i postoji više krajnjih tačaka povezanih sa tom IP adresom i brojem porta, kopija dolaznog datagrama se šalje svakoj krajnjoj tački. (Lokalna IP adresa krajnje tačke može se navesti kao zamjenski znakovi i odgovarat će bilo kojoj IP adresi odredišta.) Međutim, ako IP datagram koji stiže ima odredišnu IP adresu koja je privatna adresa, samo jedna kopija datagrama se isporučuje jednoj krajnjoj točki. Koja krajnja tačka prima datagram lične adrese zavisi od implementacije.

Kratki zaključci

UDP je jednostavan protokol. Zvanična specifikacija RFC 768 [Postel 1980] ima samo tri stranice. Usluge koje pruža korisničkim procesima iznad i iza IP-a su brojevi portova i opcioni kontrolni sumi. Koristili smo UDP da pregledamo kalkulacije kontrolne sume i vidimo kako funkcioniše fragmentacija.

Zatim smo pogledali ICMP nedostupnu grešku, koja je dio nove funkcije određivanja transportnog MTU-a (pogledajte Poglavlje 2, odjeljak). Pogledali smo određivanje transportnog MTU-a koristeći Traceroute i UDP. Takođe se raspravlja o procesu interakcije između UDP-a i ARP-a.

Provjerili smo da ICMP grešku potiskivanja izvora može poslati sistem koji prima IP datagrame brže nego što može obraditi. Moguće je lako generirati ove ICMP greške koristeći UDP.

Vježbe

  1. U dijelu ovog poglavlja izazvali smo fragmentaciju u Ethernetu pisanjem UDP datagrama s veličinom korisničkih podataka od 1473 bajta. Koja je najmanja veličina korisničkih podataka koja može uzrokovati fragmentaciju na Ethernetu ako se koristi IEEE 802 enkapsulacija (poglavlje 2, odjeljak)?
  2. Pročitajte RFC 791 [Postel 1981a] i recite mi zašto svi fragmenti osim posljednjeg moraju biti višestruki od 8 bajtova.
  3. Zamislite Ethernet i UDP datagram sa 8192 bajta korisničkih podataka. Koliko će fragmenata biti preneseno i kolika će biti dužina pomaka za svaki fragment?
  4. Nastavljajući s prethodnim primjerom, zamislite da se ovi datagrami zatim prenose na SLIP kanal sa MTU od 552. Morate imati na umu da količina podataka u svakom fragmentu (sve osim IP zaglavlja) mora biti višestruka od 8 bajtova. Koliko fragmenata se prenosi i koliki je pomak i dužina svakog fragmenta?
  5. Aplikacija koja koristi UDP šalje datagram koji je fragmentiran na 4 dijela. Zamislite da su fragmenti 1 i 2 stigli na svoje odredište, dok su fragmenti 3 i 4 izgubljeni. Aplikacija istekne, a zatim, nakon 10 sekundi, ponovo šalje UDP datagram. Ovaj datagram je fragmentiran na potpuno isti način kao i prvi prijenos (isti pomak i ista dužina). Sada zamislite da su fragmenti 1 i 2 izgubljeni, ali fragmenti 3 i 4 stignu na svoje odredište. Tajmer ponovnog sastavljanja na prijemnom hostu je postavljen na 60 sekundi, tako da kada su fragmenti 3 i 4 stigli na svoje konačno odredište, fragmenti 1 i 2 iz prvog prijenosa još nisu bili odbačeni. Može li primalac sastaviti IP datagram od ova četiri fragmenta?
  6. Kako možete znati da fragmenti na slici 11.15 zapravo odgovaraju redovima 5 i 6 na slici 11.14?
  7. ), tvrdo i labavo rutiranje izvora (poglavlje 8, odjeljak). Šta mislite kako se postupa sa ovim opcijama tokom fragmentacije? Uporedite svoj odgovor sa RFC 791.
  8. Rekli smo da se dolazni IP datagrami demultipleksiraju na osnovu broja odredišnog UDP porta. Da li je tačno?

Internet se odavno promijenio. Jedan od glavnih Internet protokola, UDP se koristi od strane aplikacija ne samo za isporuku datagrama i emitiranja, već i za pružanje peer-to-peer konekcija između mrežnih čvorova. Zbog svog jednostavnog dizajna, ovaj protokol ima mnogo ranije neplaniranih upotreba, međutim nedostaci protokola, kao što je nedostatak zagarantovane isporuke, nisu nestali. Ovaj članak opisuje implementaciju Protokola zagarantovane isporuke preko UDP-a.

Uvod

Originalna arhitektura Interneta je predviđala jedinstveni adresni prostor u kojem svaki čvor ima globalnu i jedinstvenu IP adresu i može direktno komunicirati sa drugim čvorovima. Sada Internet, u stvari, ima drugačiju arhitekturu – jedno područje ​​globalnih IP adresa i mnoga područja sa privatnim adresama skrivenim iza NAT uređaja. U ovoj arhitekturi, samo uređaji u globalnom adresnom prostoru mogu lako komunicirati sa bilo kim na mreži jer imaju jedinstvenu, globalno rutabilnu IP adresu. Čvor koji se nalazi na privatnoj mreži može se povezati na druge čvorove na istoj mreži, kao i na druge, dobro poznate čvorove u globalnom adresnom prostoru. Ova interakcija se uglavnom postiže zahvaljujući mehanizmu prevođenja mrežnih adresa. NAT uređaji, kao što su Wi-Fi ruteri, kreiraju posebne unose u tablici prijevoda za odlazne veze i modificiraju IP adrese i brojeve portova u paketima. Ovo omogućava uspostavljanje odlaznih veza iz privatne mreže na hostove u globalnom adresnom prostoru. Ali u isto vrijeme, NAT uređaji obično blokiraju sav dolazni promet osim ako nisu postavljena posebna pravila za dolazne veze.

Ova internet arhitektura je dovoljno ispravna za interakciju klijent-server, kada se klijenti mogu nalaziti na privatnim mrežama, a serveri imaju globalnu adresu. Ali to stvara poteškoće za direktno povezivanje dva čvora između razne privatne mreže. Direktno povezivanje dva čvora važno je za peer-to-peer aplikacije kao što su prenos glasa (Skype), udaljeni pristup računaru (TeamViewer) ili onlajn igre.

Jedan od mnogih efikasne metode uspostavljanje peer-to-peer veza između uređaja koji se nalaze na različitim privatnim mrežama naziva se "probijanje rupa". Ova tehnika se najčešće koristi sa aplikacijama baziranim na UDP protokolu.

Ali ako vaša aplikacija zahtijeva zagarantovanu isporuku podataka, na primjer, prenosite datoteke između računala, tada će korištenje UDP-a predstavljati mnoge poteškoće zbog činjenice da UDP nije zajamčeni protokol isporuke i ne osigurava isporuku paketa po redu, za razliku od TCP protokol.

U ovom slučaju, da bi se osigurala zagarantovana isporuka paketa, potrebno je implementirati protokol na nivou aplikacije koji pruža potrebnu funkcionalnost i radi na vrhu UDP-a.

Želio bih odmah napomenuti da postoji tehnika probijanja TCP rupa za uspostavljanje TCP veza između čvorova na različitim privatnim mrežama, ali zbog nedostatka podrške za to od strane mnogih NAT uređaja, obično se ne smatra glavnom metodom povezivanje takvih čvorova.

Zahtjevi protokola

  1. Pouzdana isporuka paketa implementirana kroz mehanizam pozitivne povratne informacije (tzv. pozitivna potvrda)
  2. Potreba za efikasnim prijenosom velikih podataka, tj. protokol mora izbjegavati nepotrebne ponovne prijenose paketa
  3. Trebalo bi biti moguće zaobići mehanizam potvrde isporuke (mogućnost funkcioniranja kao „čisti“ UDP protokol)
  4. Mogućnost implementacije komandnog režima, uz potvrdu svake poruke
  5. Osnovna jedinica prenosa podataka po protokolu mora biti poruka
Ovi zahtjevi su uglavnom isti kao zahtjevi protokola pouzdanih podataka opisanih u RFC 908 i RFC 1151, a razvoj ovog protokola sam bazirao na ovim standardima.

Da bismo razumjeli ove zahtjeve, pogledajmo vremenske dijagrame prijenosa podataka između dva mrežna čvora koristeći TCP i UDP protokole. Izgubimo po jedan paket u oba slučaja.

Prijenos neinteraktivnih podataka preko TCP-a:


Kao što možete vidjeti iz dijagrama, u slučaju gubitka paketa, TCP će otkriti izgubljeni paket i prijaviti ga pošiljaocu tražeći broj izgubljenog segmenta.

Prijenos podataka putem UDP protokola:



UDP ne poduzima nikakve korake da otkrije gubitke. Kontrola grešaka u prijenosu u UDP protokolu je u potpunosti odgovornost aplikacije.

Otkrivanje greške u TCP protokolu postiže se uspostavljanjem veze sa krajnjim čvorom, pohranjivanjem stanja ove veze, naznakom broja poslanih bajtova u zaglavlju svakog paketa i obavještavanjem o prijemu pomoću broja potvrde.

Dodatno, za poboljšanje performansi (tj. slanje više od jednog segmenta bez primanja potvrde), TCP protokol koristi takozvani prozor za prijenos - broj bajtova podataka koje pošiljatelj segmenta očekuje da primi.

Više detalja o TCP protokolu možete pronaći u rfc 793, sa UDP u rfc 768, gdje su oni, zapravo, definirani.

Iz navedenog je jasno da u cilju kreiranja pouzdanog protokola za isporuku poruka preko UDP-a (u daljem tekstu nazivamo Pouzdan UDP), potrebno je implementirati mehanizme prijenosa podataka slične TCP-u. naime:

  • sačuvati stanje veze
  • koristite numeraciju segmenata
  • koristite posebne pakete potvrde
  • koristite pojednostavljeni mehanizam prozora za povećanje protoka protokola
Dodatno, potrebno:
  • signalizirati početak poruke za dodjelu resursa za vezu
  • signalizira kraj poruke, kako bi primljenu poruku proslijedila uzvodnoj aplikaciji i oslobodila resurse protokola
  • dozvolite protokolu specifičnom za vezu da onemogući mehanizam potvrde isporuke da funkcionira kao "čisti" UDP

Pouzdano UDP zaglavlje

Podsjetimo da je UDP datagram inkapsuliran unutar IP datagrama. Pouzdan UDP paket je shodno tome "umotan" u UDP datagram.

Pouzdana enkapsulacija UDP zaglavlja:



Struktura pouzdanog UDP zaglavlja je prilično jednostavna:

  • Zastavice – zastavice za kontrolu paketa
  • MessageType – vrsta poruke koju koriste upstream aplikacije za pretplatu na određene poruke
  • TransmissionId - broj prenosa, zajedno sa adresom i portom primaoca, jedinstveno identifikuje vezu
  • PacketNumber – broj paketa
  • Opcije – dodatne opcije protokola. U slučaju prvog paketa, koristi se za označavanje veličine poruke
Zastave su sljedeće:
  • FirstPacket - prvi paket poruke
  • NoAsk - poruka ne zahtijeva da se omogući mehanizam potvrde
  • LastPacket - posljednji paket poruke
  • RequestForPacket - paket potvrde ili zahtjev za izgubljenim paketom

Opšti principi protokola

Pošto je pouzdan UDP fokusiran na garantovanje prenosa poruke između dva čvora, mora biti u stanju da uspostavi vezu sa drugom stranom. Da bi uspostavila vezu, strana koja šalje šalje paket sa zastavicom FirstPacket, odgovor na koji će značiti da je veza uspostavljena. Svi paketi odgovora, ili, drugim riječima, paketi potvrde, uvijek postavljaju vrijednost polja PacketNumber na jednu veću od najveće vrijednosti PacketNumber uspješno pristiglih paketa. Polje Opcije za prvi poslani paket bilježi veličinu poruke.

Sličan mehanizam se koristi za završetak veze. LastPacket zastavica je postavljena u posljednjem paketu poruke. Paket odgovora označava broj posljednjeg paketa + 1, što za stranu primateljicu znači uspješnu isporuku poruke.

Dijagram uspostavljanja i prekida veze:



Kada se veza uspostavi, počinje prijenos podataka. Podaci se prenose u blokovima paketa. Svaki blok, osim posljednjeg, sadrži fiksni broj paketa. Jednaka je veličini prozora za prijem/prenos. Posljednji blok podataka može imati manje paketa. Nakon slanja svakog bloka, strana koja šalje čeka potvrdu isporuke, ili zahtjev za ponovnu isporuku izgubljenih paketa, ostavljajući prozor za prijem/prenos otvoren za primanje odgovora. Nakon prijema potvrde isporuke bloka, prozor prijema/prijenosa se pomiče i šalje se sljedeći blok podataka.

Strana koja prima pakete prima pakete. Svaki paket se provjerava da se vidi da li spada u okvir za prijenos. Paketi i duplikati koji ne padaju u prozor se eliminišu. Jer Pošto je veličina prozora fiksna i ista za primaoca i pošiljaoca, onda se u slučaju isporuke bloka paketa bez gubitka, prozor pomera za prijem paketa sledećeg bloka podataka i šalje se potvrda isporuke . Ako se prozor ne popuni u periodu koji je zadao radni tajmer, pokrenut će se provjera da se vidi koji paketi nisu isporučeni i poslat će se zahtjevi za ponovnu isporuku.

Dijagram retransmisije:


Vremenska ograničenja protokola i tajmeri

Postoji nekoliko razloga zašto se veza ne može uspostaviti. Na primjer, ako je primalac van mreže. U tom slučaju, prilikom pokušaja uspostavljanja veze, veza će biti zatvorena zbog isteka vremena. Implementacija Pouzdan UDP koristi dva tajmera za postavljanje vremenskih ograničenja. Prvi, radni tajmer, koristi se za čekanje odgovora udaljenog hosta. Ako se aktivira na strani koja šalje, posljednji poslani paket se ponovo šalje. Ako se tajmer aktivira kod primaoca, tada se vrši provjera izgubljenih paketa i šalju se zahtjevi za ponovnu isporuku.

Drugi tajmer je neophodan za zatvaranje veze ako nema komunikacije između čvorova. Za stranu koja šalje, počinje odmah nakon što radni tajmer istekne i čeka odgovor od udaljenog čvora. Ako nema odgovora u navedenom periodu, veza se prekida i resursi se oslobađaju. Za stranu primaoca, tajmer za zatvaranje veze počinje nakon što se radni tajmer aktivira dvaput. Ovo je neophodno radi osiguranja od gubitka paketa potvrde. Kada se tajmer aktivira, veza se također prekida i resursi se oslobađaju.

Pouzdan UDP dijagram stanja prijenosa

Principi rada protokola su implementirani u konačnom automatu, čije je svako stanje odgovorno za specifičnu logiku obrade paketa.
Pouzdan UDP dijagram stanja:

Zatvoreno– zapravo nije stanje, to je početna i završna tačka mašine. Za državu Zatvoreno prima se kontrolni blok prijenosa, koji implementirajući asinhroni UDP server, preusmjerava pakete na odgovarajuće veze i započinje obradu stanja.

FirstPacketSending– početno stanje u kojem se nalazi odlazna veza prilikom slanja poruke.

U ovom stanju se šalje prvi paket za obične poruke. Za poruke bez potvrde o slanju ovo je jedino stanje - u njemu se šalje cijela poruka.

SendingCycle– osnovno stanje za prijenos paketa poruka.

Prelazak na njega iz države FirstPacketSending izvršeno nakon slanja prvog paketa poruke. U tom stanju dolaze sva priznanja i zahtjevi za retransmisije. Izlazak iz njega moguć je u dva slučaja - u slučaju uspješne isporuke poruke ili zbog isteka vremena.

FirstPacketReceived– početno stanje za primaoca poruke.

Provjerava ispravnost početka prijenosa, kreira potrebne strukture i šalje potvrdu o prijemu prvog paketa.

Za poruku koja se sastoji od jednog paketa i poslana bez upotrebe potvrde isporuke, ovo je jedino stanje. Nakon obrade takve poruke, veza se prekida.

Sastavljanje– osnovno stanje za prijem paketa poruka.

Zapisuje pakete u privremenu memoriju, provjerava gubitak paketa, šalje potvrde isporuke bloka paketa i cijele poruke, te šalje zahtjeve za ponovnu isporuku izgubljenih paketa. Ako je cijela poruka uspješno primljena, veza prelazi u Završeno, u suprotnom dolazi do izlaska s timeoutom.

Završeno– zatvaranje veze ako je cijela poruka uspješno primljena.

Ovo stanje je neophodno za sklapanje poruke i za slučaj kada je potvrda o dostavi poruke izgubljena na putu do pošiljaoca. Iz ovog stanja se izlazi nakon isteka vremena, ali se veza smatra uspješno zatvorenom.

Dublje u kod. Upravljačka jedinica mjenjača

Jedan od ključnih elemenata pouzdanog UDP-a je kontrolni blok prijenosa. Svrha ovog bloka je pohranjivanje trenutnih veza i pomoćnih elemenata, distribucija dolaznih paketa odgovarajućim vezama, obezbjeđivanje interfejsa za slanje paketa na konekciju i implementacija API protokola. Kontrolni blok prijenosa prima pakete od UDP sloja i preusmjerava ih na državni stroj za obradu. Za primanje paketa, implementira asinhroni UDP server.

Neki članovi klase ReliableUdpConnectionControlBlock:

interna klasa ReliableUdpConnectionControlBlock: IDisposable ( // niz bajtova za navedeni ključ. Koristi se za sastavljanje dolaznih poruka public ConcurrentDictionary , byte> IncomingStreams ( get; privatni set;) // niz bajtova za navedeni ključ. Koristi se za slanje odlaznih poruka. javni ConcurrentDictionary , byte> OutcomingStreams ( get; privatni skup; ) // zapis veze za navedeni ključ. privatni ConcurrentDictionary samo za čitanje , ReliableUdpConnectionRecord> m_listOfHandlers; // lista pretplatnika na poruke. privatna lista samo za čitanje m_subscribers; // lokalna utičnica privatna Socket m_socketIn; // port za dolazne poruke private int m_port; // lokalna IP adresa privatna IPAddress m_ipAddress; // lokalna krajnja točka public IPEndPoint LocalEndpoint ( get; privatni skup; ) // zbirka preinicijaliziranih // stanja državnog stroja javnih StateCollection State ( get; privatni skup; ) // generator slučajnih brojeva. Koristi se za kreiranje TransmissionId privatnog samo za čitanje RNGCryptoServiceProvider m_randomCrypto; //... )


Implementacija asinhronog UDP servera:

private void Receive() ( EndPoint connectedClient = new IPEndPoint(IPAddress.Any, 0); // kreirajte novi bafer za svaki socket.BeginReceiveFrom bajt bafer = novi bajt; // proslijedite bafer kao parametar asinkronoj metodi this. m_socketIn. BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, refconnectedClient, EndReceive, buffer); ) private void EndReceive(IAsyncResult ar) (EndPoint connectedClient = new IPEndPoint(IPAddress.Any); u .m_socketIn .EndReceiveFrom(ar, ref connectedClient); //paket primljen, spreman za primanje sljedećeg Receive(); // jer najjednostavniji način riješite problem sa baferom - dobijete vezu do njega // od IAsyncResult.AsyncState byte bytes = ((byte) ar.AsyncState).Slice(0, bytesRead); // dobijemo zaglavlje paketa ReliableUdpHeader header; if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header)) ( // stigao je neispravan paket - odbacite ga vratite; ) // konstruirajte ključ za određivanje zapisa veze za paket Tuple ključ = novi tuple (connectedClient, header.TransmissionId); // dobijete postojeći zapis veze ili kreirajte novi ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key, new ReliableUdpConnectionRecord(key, this, header.ReliableUdpMessageType)); // pokrećemo paket za obradu u državni stroj record.State.ReceivePacket(record, header, bytes); )


Za svaki prijenos poruke kreira se struktura koja sadrži informacije o vezi. Ova struktura se zove zapis veze.

Neki članovi klase ReliableUdpConnectionRecord:

interna klasa ReliableUdpConnectionRecord: IDisposable ( // niz bajtova s ​​porukom javni bajt IncomingStream ( get; set; ) // referenca na stanje konačnog državnog stroja public ReliableUdpState State ( get; set; ) // par koji jedinstveno identificira zapis veze // u kontrolnom bloku koji prenosi javni tuple Ključ (get; privatni skup;) // donja granica prozora za primanje public int WindowLowerBound; // veličina prozora za prijenos public readonly int WindowSize; // broj paketa za slanje public int SndNext; // broj paketa za slanje public int NumberOfPackets; // broj prijenosa (ovo je drugi dio tuple) // svaka poruka ima svoj javni samo za čitanje Int32 TransmissionId; // udaljena IP krajnja točka – stvarni primalac poruke samo za čitanje IPEndPoint RemoteClient; // veličina paketa, kako bi se izbjegla fragmentacija na IP razini // ne bi trebala prelaziti MTU – (IP.Header + UDP.Header + RelaibleUDP.Header) public readonly int BufferSize; // blok kontrole prijenosa javni samo za čitanje ReliableUdpConnectionControlBlock Tcb; // enkapsulira rezultate asinkrone operacije za BeginSendMessage/EndSendMessage public readonly AsyncResultSendMessage AsyncResult; // ne šalji pakete potvrde public bool IsNoAnswerNeeded; // posljednji ispravno primljeni paket (uvijek postavljen na najveći broj) public int RcvCurrent; // niz s brojevima izgubljenih paketa public int LostPackets ( get; privatni set; ) // da li je stigao zadnji paket. Koristi se kao bool. public int IsLastPacketReceived = 0; //... )

Dublje u kod. države

Države implementiraju državni stroj pouzdanog UDP protokola, u kojem se odvija glavna obrada paketa. Apstraktna klasa ReliableUdpState pruža interfejs za stanje:

Čitavu logiku protokola implementiraju gore predstavljene klase, zajedno sa pomoćnom klasom koja obezbjeđuje statičke metode, kao što je, na primjer, konstruiranje ReliableUdp zaglavlja iz zapisa veze.

DisposeByTimeout metoda

Metoda DisposeByTimeout je odgovorna za oslobađanje resursa veze kada istekne vremensko ograničenje i za signaliziranje uspješnog/neuspjelog isporuke poruke.

ReliableUdpState.DisposeByTimeout:

zaštićena virtualna void DisposeByTimeout(object record) (ReliableUdpConnectionRecord connectionRecord = (ReliableUdpConnectionRecord) zapis; if (record.AsyncResult != null) (connectionRecord.AsyncResult.SetAsCompleted(false); ) connectionRecord.Dispose();


To je samo poništeno u državi Završeno.

Completed.DisposeByTimeout:

zaštićeno nadjačavanje void DisposeByTimeout(object record) (ReliableUdpConnectionRecord connectionRecord = (ReliableUdpConnectionRecord) zapis; // prijaviti uspješan prijem poruke SetAsCompleted(connectionRecord); )

ProcessPackets metoda

Metoda ProcessPackets je odgovorna za dodatnu obradu paketa ili paketa. Poziva se direktno ili preko tajmera čekanja paketa.

U stanju Sastavljanje metoda je nadjačana i odgovorna je za provjeru izgubljenih paketa i prelazak u stanje Završeno, u slučaju prijema posljednjeg paketa i uspješnog prolaska provjere

Assembling.ProcessPackets:

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord) ( if (connectionRecord.IsDone != 0) return; if (!ReliableUdpStateTools.CheckForNoPacketLoss(connectionRecord, connectionRecord.IsLastPacketReceived) za pakete za izgubljeno je! ( int seqNum u connectionRecord.LostPackets) ( if (seqNum != 0) ( ReliableUdpStateTools.SendAskForLostPacket(connectionRecord, seqNum); ) ) // postavlja tajmer drugi put da ponovo pokuša prijenos if (!connectionRecord.TimerSecondT.connectionRecord.TimerSecondT. WaitForPacketsTimer .Change(connectionRecord.ShortTimerPeriod, -1); connectionRecord.TimerSecondTry = true; return; ) // ako nakon dva pokušaja aktiviranja WaitForPacketTimer // nije bilo moguće primiti pakete, pokrenite tajmer za završetak veze StartCloseWaitTimer(connection); ) else if (connectionRecord. IsLastPacketReceived != 0) // uspješna provjera ( // poslati potvrdu o prijemu bloka podataka ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); connectionRecord.State = connectionRecord.Tcb.States.Completed; connectionRecord.State.ProcessPackets(connectionRecord); // umjesto trenutne implementacije resursa // pokrećemo tajmer u slučaju da // posljednji ack ne stigne do pošiljaoca i on ga ponovo zatraži. // kada se tajmer aktivira - implementiramo resurse // u Completed stanje, metoda tajmera je nadjačana StartCloseWaitTimer(connectionRecord); ) // to je slučaj kada je ack na bloku paketa izgubljen u suprotnom ( if (!connectionRecord.TimerSecondTry) ( ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); connectionRecord.WaitForPacketsTimer.Change(connectionPericord.Short.1);connectionRecord.Short.1 TimerSecondTry = true; return; ) // pokreće tajmer za završetak veze StartCloseWaitTimer(connectionRecord); ) )


U stanju SendingCycle ovaj metod se poziva samo na tajmeru i odgovoran je za ponovno slanje posljednje poruke, kao i za pokretanje tajmera za zatvaranje veze.

SendingCycle.ProcessPackets:

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord) ( if (connectionRecord.IsDone != 0) return; // ponovo pošalji zadnji paket // (ako je veza obnovljena, prijemni čvor će ponovo poslati zahtjeve koji nisu stigli do njega) ReliableUdpStateTools.SendPackeTools.SendPacket. (connectionRecord, ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord, connectionRecord.SndNext - 1)); // omogućite CloseWait tajmer - da sačekate da se veza vrati ili da se završi StartCloseWaitTimer(connectionRecord); )


U stanju Završeno Metoda zaustavlja radni tajmer i šalje poruku pretplatnicima.

Completed.ProcessPackets:

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord) ( if (connectionRecord.WaitForPacketsTimer != null) connectionRecord.WaitForPacketsTimer.Dispose(); // prikupiti poruku i poslati je pretplatnicima ReliableUdpStateMessageTools.Fromoconnect)Creconnect

ReceivePacket metoda

U stanju FirstPacketReceived Glavni zadatak metode je da utvrdi da li je prvi paket poruke zaista stigao na interfejs, kao i da sastavi poruku koja se sastoji od jednog paketa.

FirstPacketReceived.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader zaglavlje, byte payload) ( if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket)) // odbaci zastavicu paketa i // zadnja Packet kombinacija kaže da je kombinacija dva paketa - FirstPacket imamo jedinu poruku ako (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) & header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket)) ( ReliableUdpStateTools.CreateMessagetFrom,Reload.MessageFrom ader.Dužina, nosivost. Dužina) ); if ( !header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk)) ( // pošaljite paket potvrde ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); ) SetAsCompleted(connectionRecord); ) SetAsCompleted(connectionRecord); ) SetAsCompleted(connectionRecord); if (header.PacketNumber != 0) return;ReliableUdpStateTools.InitIncomingBytesStorage(connectionRecord, header);ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload); // brojimo broj paketa koji bi trebali stići connectionRecord.NumberOfPackets = (int)Math.Ceiling((double) ((double) connectionRecord.IncomingStream.Length/(double) connectionRecord.BufferSize)); // bilježimo broj posljednjeg primljenog paketa (0) connectionRecord.RcvCurrent = header.PacketNumber; // nakon toga smo pomaknuli prozor za primanje za 1 connectionRecord.WindowLowerBound++; // prebacivanje stanja connectionRecord.State = connectionRecord.Tcb.States.Assembling; // ako mehanizam potvrde nije potreban // pokrenuti tajmer koji će otpustiti sve strukture if (header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk)) ( connectionRecord.CloseWaitTimer = new Timer(DisposeByTimeout, connectionRecord, connectionRecord.ShortTimerPeriod, - ); ) else ( ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1); ) )


U stanju SendingCycle ovaj metod je poništen kako bi se prihvatile potvrde isporuke i zahtjevi za ponovni prijenos.

SendingCycle.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte payload) (if (connectionRecord.IsDone != 0) return; if (!header.Flags.HasFlag(ReliableUdpHeaderPacket) return the window // finalni izračun graniceForPacket.Request); // granica prozora + 1 se uzima za primanje potvrde isporuke int windowHighestBound = Math.Min((connectionRecord.WindowLowerBound + connectionRecord.WindowSize), (connectionRecord.NumberOfPackets)); // provjerava da li je pogodio prozor if (header.PacketNumber< connectionRecord.WindowLowerBound || header.PacketNumber >windowHighestBound) return; connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); if (connectionRecord.CloseWaitTimer != null) connectionRecord.CloseWaitTimer.Change(-1, -1); // provjeravamo posljednji paket: if (header.PacketNumber == connectionRecord.NumberOfPackets) ( // prijenos završen Interlocked.Increment(ref connectionRecord.IsDone); SetAsCompleted(connectionRecord); return; ) // ovo je odgovor na prvi paket sa potvrdom if ((header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) && header.PacketNumber == 1)) ( // bez pomaka prozora SendPacket(connectionRecord); ) // stigla je potvrda o prijemu bloka podataka inače ako (header.PacketNumber == windowHighestBound) ( // pomiče prozor za prijem/prenos connectionRecord.WindowLowerBound += connectionRecord.WindowSize; // resetiraj niz kontrole prijenosa connectionRecord.WindowControlArray.Nullify(); // pošalji blok paketa SendPacket( connectionRecord); ) // ovo je zahtjev za ponavljanje prijenosa - pošaljite potrebni paket else ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord, header.PacketNumber)); )


U stanju Sastavljanje u metodi ReceivePacket odvija se glavni posao sastavljanja poruke iz dolaznih paketa.

Assembling.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte payload) (if (connectionRecord.IsDone != 0) return; // obrada paketa s onemogućenim mehanizmom potvrde isporuke if (header.Flags.HasFlag(HeliableFlags) (HeliableFlags) / / resetirajte tajmer connectionRecord.CloseWaitTimer.Change(connectionRecord.LongTimerPeriod, -1); // upišite podatke ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload); // ako smo primili paket sa posljednjom zastavicom - uradite to, završi if (header.Flags.HasFlag (ReliableUdpHeaderFlags.LastPacket)) (connectionRecord.State = connectionRecord.Tcb.States.Completed; connectionRecord.State.ProcessPackets(connectionRecord); ) return; ) // izračunavanje posljednje granice prozora B int windowH = Math.Min((connectionRecord.WindowLowerBound + connectionRecord.WindowSize - 1), (connectionRecord.NumberOfPackets - 1)); // odbaci pakete koji ne padaju u prozor if (header.PacketNumber< connectionRecord.WindowLowerBound || header.PacketNumber >(windowHighestBound)) return; // odbacimo duplikate if (connectionRecord.WindowControlArray.Contains(header.PacketNumber)) return; // zapis podataka ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload); //povećavamo brojač paketa connectionRecord.PacketCounter++; // upisuje trenutni broj paketa u niz kontrole prozora connectionRecord.WindowControlArray = header.PacketNumber; // postavlja najveći dolazni paket if (header.PacketNumber > connectionRecord.RcvCurrent) connectionRecord.RcvCurrent = header.PacketNumber; // ponovno pokretanje tajmera connectionRecord.TimerSecondTry = false; connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); if (connectionRecord.CloseWaitTimer != null) connectionRecord.CloseWaitTimer.Change(-1, -1); // ako je stigao zadnji paket if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket)) ( Interlocked.Increment(ref connectionRecord.IsLastPacketReceived); ) // ako smo primili sve pakete prozora, onda resetirajte brojač // i pošalji potvrdni paket else if (connectionRecord.PacketCounter == connectionRecord.WindowSize) ( // resetiraj brojač. connectionRecord.PacketCounter = 0; // pomaknuo prozor prijenosa connectionRecord.WindowLowerBound += connectionRecord.WindowSize; // resetiraj prijenos kontrolno polje connectionRecord.WindowControlArray.Nullify() ; ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); ) // ako je posljednji paket već dostupan if (Thread.VolatileRead(ref connectionRecord.IsLastPacketReceived) (ProcesPacketReceived Received) ); ) )


U stanju Završeno Jedini zadatak metode je slanje ponovljene potvrde o uspješnoj isporuci poruke.

Completed.ReceivePacket:

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte payload) ( // ponovno slanje posljednjeg paketa zbog // činjenice da posljednji ack nije stigao do pošiljatelja if (header.Flags.HasFlag(ReliableUdpHeaderFlacke) ) ( ReliableUdpStateTools .SendAcknowledgePacket(connectionRecord); ) )

SendPacket metoda

U stanju FirstPacketSending Ova metoda šalje prvi paket podataka, ili, ako poruka ne zahtijeva potvrdu isporuke, cijelu poruku.

FirstPacketSending.SendPacket:

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord) (connectionRecord.PacketCounter = 0; connectionRecord.SndNext = 0; connectionRecord.WindowLowerBound = 0; // ako potvrda nije potrebna, pošaljite sve pakete // i oslobodite resurse ako (connectionRecord.IsedA) (connectionRecord.IsedA) / / Ovdje se slanje događa kako jest ( ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools. CreateUdpPayload(connectionRecord, ReliableUdpStateTools. CreateReliableUdpHeader(connectionRecord))); connectionRecordion;++StateTools.connectionRecord.++StateTools.< connectionRecord.NumberOfPackets); SetAsCompleted(connectionRecord); return; } // создаем заголовок пакета и отправляем его ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord); ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header)); // увеличиваем счетчик connectionRecord.SndNext++; // сдвигаем окно connectionRecord.WindowLowerBound++; connectionRecord.State = connectionRecord.Tcb.States.SendingCycle; // Запускаем таймер connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1); }


U stanju SendingCycle u ovoj metodi se šalje blok paketa.

SendingCycle.SendPacket:

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord) ( // poslati blok paketa za (connectionRecord.PacketCounter = 0; connectionRecord.PacketCounter< connectionRecord.WindowSize && connectionRecord.SndNext < connectionRecord.NumberOfPackets; connectionRecord.PacketCounter++) { ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord); ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header)); connectionRecord.SndNext++; } // на случай большого окна передачи, перезапускаем таймер после отправки connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); if (connectionRecord.CloseWaitTimer != null) { connectionRecord.CloseWaitTimer.Change(-1, -1); } }

Dublje u kod. Stvaranje i uspostavljanje veza

Sada kada smo upoznati s osnovnim stanjima i metodama koje se koriste za obradu stanja, možemo malo detaljnije pogledati nekoliko primjera rada protokola.

Dijagram prijenosa podataka u normalnim uvjetima:



Pogledajmo pobliže stvaranje zapis veze za povezivanje i slanje prvog paketa. Prijenos uvijek inicira aplikacija koja poziva API metodu za slanje poruke. Zatim se koristi metoda StartTransmission kontrolnog bloka prijenosa, koja započinje prijenos podataka za novu poruku.

Kreiranje odlazne veze:

private void StartTransmission(ReliableUdpMessage pouzdanUdpMessage, EndPoint endPoint, AsyncResultSendMessage asyncResult) ( if (m_isListenerStarted == 0) ( if (this.LocalEndpoint == null) (bacite novi ArgumentNullException prije nego što pokrenete parametar za slušanje ("), ili startni parametar za slušanje(") slanje poruke"); ) // započnemo obradu dolaznih paketa StartListener(LocalEndpoint); ) // kreiramo ključ za rječnik baziran na EndPoint i ReliableUdpHeader.TransmissionId bajt transferId = novi bajt; // kreiraj nasumični broj transferId m_randomCrypto.GetBytes( TransferId) ; ključ = novi tuple (endPoint, BitConverter.ToInt32(transmissionId, 0)); // kreirajte novi zapis za vezu i provjerite // da li takav broj već postoji u našim rječnicima ako (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, pouzdanUdpMessage, asyncResult))) ( // ako postoji , zatim ponovite generiranje slučajnog broja m_randomCrypto.GetBytes(transmissionId); ključ = novi tuple (endPoint, BitConverter.ToInt32(transmissionId, 0)); if (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, pouzdanUdpMessage, asyncResult))) // ako ponovo ne uspije, izbaci izuzetak throw new ArgumentException("Uparite TransmissionId & EndPoint već postoji u rječniku"); ) // započelo stanje za obradu m_listOfHandlers.State.SendPacket(m_listOfHandlers); )


Slanje prvog paketa (stanje FirstPacketSending):

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord) ( connectionRecord.PacketCounter = 0; connectionRecord.SndNext = 0; connectionRecord.WindowLowerBound = 0; // ... // kreirajte zaglavlje paketa i pošaljite ga ReliableUdpHeaderUdrecord zaglavlje = ReliableUdpHeaderUdrepS Udreconnection. kabel ); ReliableUdpStateTools.SendPacket(connectionRecord, ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header)); // povećaj brojač connectionRecord.SndNext++; // pomakni prozor connectionRecord.WindowLowerBound++; // idi u stanje konekcije za slanje TbC. .States.SendingCycle; // Pokreni tajmer connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1); )


Nakon slanja prvog paketa, pošiljalac ulazi u stanje SendingCycle– sačekajte potvrdu isporuke paketa.
Strana koja prima, koristeći metodu EndReceive, prihvata poslani paket i kreira novi zapis veze i prosljeđuje ovaj paket, s unaprijed raščlanjenim zaglavljem, u metodu stanja ReceivePacket na obradu FirstPacketReceived

Kreiranje veze na prijemnoj strani:

private void EndReceive(IAsyncResult ar) ( // ... // primljen paket // raščlaniti zaglavlje paketa ReliableUdpHeader zaglavlje; if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header)) ( // stigao je pogrešan paket koji je vratio - odbacite ; ) // konstruira ključ za određivanje zapisa veze za paket Tuple ključ = novi tuple (connectedClient, header.TransmissionId); // dobijemo postojeći zapis veze ili kreiramo novi ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key, new ReliableUdpConnectionRecord(key, this, header. ReliableUdpMessageType)); // pokrećemo paket za obradu u državni stroj record.State.ReceivePacket(record, header, bytes); )


Primanje prvog paketa i slanje potvrde (FirstPacketReceived stanje):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte payload) ( if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket)) // odbaci paket paketa koji se vraća iz dizajna ... // po dizajnu 0 ; if (header.PacketNumber != 0) return; // inicijalizirati niz za pohranjivanje dijelova poruke ReliableUdpStateTools.InitIncomingBytesStorage(connectionRecord, header); // upisati podatke o paketu u niz ReliableUdpStateTools.WriteconnecterRecord,header payload); // prebroji broj paketa koji bi trebali stići connectionRecord.NumberOfPackets = (int)Math.Ceiling((double) ((double) connectionRecord.IncomingStream.Length/(double) connectionRecord.BufferSize)); // zabilježi broj posljednjeg primljenog paketa (0 ) connectionRecord.RcvCurrent = header.PacketNumber; // nakon toga se prozor primanja pomiče za 1 connectionRecord.WindowLowerBound++; // promijenite stanje connectionRecord.State = connectionRecord.Tcb.States.Assembling; if (/*ako mehanizam potvrde nije potreban*/) // ... else ( // pošalji potvrdu ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord, connectionRecord.ShortTimerPeriod, -1); ) )

Dublje u kod. Zatvaranje veze zbog isteka vremena

Rukovanje vremenskim ograničenjima je važan dio pouzdanog UDP-a. Razmotrimo primjer u kojem posredni čvor pokvari i isporuka podataka u oba smjera postaje nemoguća.

Dijagram zatvaranja veze po vremenskom ograničenju:



Kao što se vidi iz dijagrama, pošiljateljev radni tajmer počinje odmah nakon slanja bloka paketa. Ovo se dešava u metodi SendPacket stanja SendingCycle.

Omogućavanje radnog tajmera (SendingCycle stanje):

public override void SendPacket(ReliableUdpConnectionRecord connectionRecord) ( // poslati blok paketa // ... // ponovo pokrenite tajmer nakon slanja connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); if (connectionRecord.CloseWaitTimer! CloseWaitTimer.Change(-1, -1); )


Vremenski periodi se postavljaju kada se veza uspostavi. Podrazumevano, ShortTimerPeriod je 5 sekundi. U primjeru je postavljeno na 1,5 sekunde.

Za dolaznu vezu, tajmer počinje nakon prijema posljednjeg primljenog paketa podataka; to se događa u metodi ReceivePacket stanja Sastavljanje

Omogućavanje radnog tajmera (Stanje sklapanja):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader zaglavlje, byte payload) ( // ... // restart tajmera connectionRecord.TimerSecondTry = false; connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimer.ShortTimerTimer; = null) connectionRecord.CloseWaitTimer.Change(-1, -1); // ... )


Dolazna veza nije primila više paketa tokom vremena čekanja na radni tajmer. Tajmer se pokrenuo i pozvao metodu ProcessPackets, koja je po prvi put otkrila izgubljene pakete i poslala zahtjeve za ponovnu isporuku.

Slanje zahtjeva za ponovnu isporuku (stanje sklapanja):

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord) ( // ... if (/*provjera izgubljenih paketa */) ( // pošalji zahtjeve za ponovnu isporuku // postavi tajmer drugi put za ponovni pokušaj prijenosa if (!connectionRecord. TimerSecondTry) ( connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); connectionRecord.TimerSecondTry = true; return; ) // ako nakon dva pokušaja aktiviranja WaitForPacketTimer // nije bilo moguće primiti pakete, pokrenite timer za završetak veze StartCloseWaitTimer(connectionRecord); ) else if (/*poslednji paket stigao i uspješna provjera */) ( // ... StartCloseWaitTimer(connectionRecord); ) // ako je potvrda na blok paketa izgubljena inače ( if (! connectionRecord.TimerSecondTry) ( // ponovo pošalji ack connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); connectionRecord.TimerSecondTry = true; return; ) // pokreće tajmer za završetak veze StartCloseWaitTimer(connectionRecord); ) )


Varijabla TimerSecondTry je postavljena na istinito. Ova varijabla je odgovorna za ponovno pokretanje radnog tajmera.

Na strani pošiljaoca se takođe aktivira radni tajmer i ponovo se šalje poslednji poslani paket.

Omogućavanje tajmera zatvaranja veze (SendingCycle stanje):

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord) ( // ... // ponovo pošalji zadnji paket // ... // omogući CloseWait tajmer - da se čeka da se veza vrati ili da se završi StartCloseWaitTimer(connectionRecord); )


Nakon toga, u odlaznoj vezi se pokreće tajmer zatvaranja veze.

ReliableUdpState.StartCloseWaitTimer:

protected void StartCloseWaitTimer(ReliableUdpConnectionRecord connectionRecord) (if (connectionRecord.CloseWaitTimer != null) connectionRecord.CloseWaitTimer.Change(connectionRecord.LongTimerPeriod, -1); else connectionRecord.CloseWaitTimerRecord.CloseWaitTimerRecord period, -1) ; )


Podrazumevano vremensko ograničenje za tajmer zatvaranja veze je 30 sekundi.

Nakon kratkog vremena ponovo se aktivira radni tajmer na strani prijema, ponovo se šalju zahtjevi, nakon čega se pokreće tajmer zatvaranja veze za dolaznu vezu

Kada se aktiviraju tajmeri za zatvaranje, svi resursi oba zapisa veze se oslobađaju. Pošiljalac prijavljuje neuspjeh isporuke uzvodnoj aplikaciji ( pogledajte API Pouzdan UDP).

Otpuštanje zapisa veze"a resursi:

public void Dispose() ( pokušajte ( System.Threading.Monitor.Enter(this.LockerReceive); ) konačno ( Interlocked.Increment(ref this.IsDone); if (WaitForPacketsTimer != null) ( WaitForPacketsTimer.Dispose(); ) if (CloseWaitTimer != null) ( CloseWaitTimer.Dispose(); ) tok bajtova; Tcb.IncomingStreams.TryRemove(Key, izlazni tok); stream = null; Tcb.OutcomingStreams.TryRemove(Key, izlazni tok); sistemski tok .Threading.Monitor.Exit(this.LockerReceive); ) )

Dublje u kod. Oporavak prijenosa podataka

Dijagram oporavka prijenosa podataka u slučaju gubitka paketa:



Kao što je već rečeno u zatvaranju veze po isteku vremena, kada radni tajmer istekne, primalac će provjeriti da li postoje izgubljeni paketi. Ako dođe do gubitka paketa, sastaviće se lista brojeva paketa koji nisu stigli do primaoca. Ovi brojevi se unose u niz LostPackets određene veze i šalju se zahtjevi za ponovnu isporuku.

Slanje zahtjeva za ponovnu isporuku paketa (stanje sklapanja):

public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord) ( //... if (!ReliableUdpStateTools.CheckForNoPacketLoss(connectionRecord, connectionRecord.IsLastPacketReceived != 0)) ( // postoje izgubljeni paketi za njihovu vezu, pošaljite ih za povrat (pošaljite zahtjeve za ponovno povezivanje) LostPackets ) ( if (seqNum != 0) ( ReliableUdpStateTools.SendAskForLostPacket(connectionRecord, seqNum); ) ) // ... ) )


Pošiljalac će prihvatiti zahtjev za ponovnu dostavu i ponovo će poslati pakete koji nedostaju. Vrijedi napomenuti da je u ovom trenutku pošiljatelj već pokrenuo tajmer za zatvaranje veze i, po prijemu zahtjeva, on se resetuje.

Ponovno slanje izgubljenih paketa (SendingCycle stanje):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte payload) ( // ... connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1); // resetirajte tajmer za zatvaranje veze if (connectionWacord). connectionRecord .CloseWaitTimer.Change(-1, -1); // ... // ovo je zahtjev za ponovni prijenos - pošaljite potrebni paket inače )


Ponovo poslani paket (paket br. 3 na dijagramu) prima dolazna veza. Provjerava se kako bi se osiguralo da je prozor za prijem pun i da se normalan prijenos podataka vraća.

Provjera pogodaka u prozoru za prijem (Stanje sklapanja):

public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte payload) ( // ... // povećava brojač paketa connectionRecord.PacketCounter++; // upisuje broj trenutnog paketa u polje kontrole prozora connectionRecord.WindowControlPaArray = header; // postavljanje najvećeg primljenog paketa if (header.PacketNumber > connectionRecord.RcvCurrent) connectionRecord.RcvCurrent = header.PacketNumber; // restartovanje tajmera connectionRecord.TimerSecondTry = false; connectionRecord.WaitForPacketsTimer.Change(connectionRecord.Short); -1. connectionRecord.CloseWaitTimer != null) connectionRecord.CloseWaitTimer.Change(-1, -1); // ... // ako smo primili sve prozorske pakete, resetirajte brojač // i pošaljite potvrdni paket else if (connectionRecord .PacketCounter == connectionRecord. WindowSize) ( // resetiranje brojača. connectionRecord.PacketCounter = 0; // pomaknut prozor prijenosa connectionRecord.WindowLowerBound += connectionRecord.WindowSize; //poništavanje polja kontrole prijenosa connectionRecord.WindowControlArray.Nullify(); ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord); ) // ... )

Pouzdan UDP API

Za interakciju s protokolom prijenosa podataka, postoji otvorena klasa Reliable Udp, koja je omotač preko kontrolnog bloka prijenosa. Evo najvažnijih članova razreda:
javna zapečaćena klasa ReliableUdp: IDisposable ( // dobiva lokalnu krajnju točku public IPEndPoint LocalEndpoint // kreira instancu ReliableUdp i počinje // slušati dolazne pakete na navedenoj IP adresi // i portu. Vrijednost 0 za port znači korištenje / / dinamički dodijeljeni port public ReliableUdp (IPAddress localAddress, int port = 0) // pretplatite se na primanje dolaznih poruka public ReliableUdpSubscribeObject SubscribeOnMessages(ReliableUdpMessageCallback callback, ReliableUdpMessageTypes messageType = ReliableUdpMessageTypes messageType = ReliableUdpSubscribeObject SubscribeOnMessages ll) // odjaviti se od primanja poruka public void Unsubscribe(ReliableUdpSubscribeObject subscribeObject) // pošalji poruku asinkrono // Napomena: kompatibilnost sa XP i Server 2003 nije izgubljena, jer se koristi javni zadatak .NET Framework 4.0 SendMessageAsync(ReliableUdpMessage pouzdanUdpMessage, IPEndPoint remoteEndPoint, CancellationToken cToken) // započnite slanje asinkrone poruke javnog IAsyncResult BeginSendMessage(ReliableUdpMessage pouzdanUdpMessage, IPEndPoint remoteEndPoint, CancellationToken cToken) //započnite slanje javne IAsyncResult BeginSendMessage(ReliableUdpMessage pouzdanUdpMessage, IPEndPoint, IPEndPoint As remoteync status, ObjectPoint As remoteync) asinkrono slanje javnog bool EndSendMessage(IAsyncResult asyncResult) // očisti resurse public void Dispose() )
Prijem poruka se vrši putem pretplate. Potpis delegata za metodu povratnog poziva:
javni delegat void ReliableUdpMessageCallback(ReliableUdpMessage pouzdanUdpMessage, IPEndPoint remoteClient);
Poruka:
javna klasa ReliableUdpMessage ( // tip poruke, jednostavno nabrajanje public ReliableUdpMessageTypes Type ( get; privatni set; ) // javni bajt podataka poruke Body ( get; privatni set; ) // ako je postavljeno na true, mehanizam potvrde isporuke će biti onemogućen / / za prijenos određene poruke public bool NoAsk ( get; privatni set; ) )
Da biste se pretplatili na određeni tip poruke i/ili određenog pošiljatelja, koriste se dva opciona parametra: ReliableUdpMessageTypes messageType i IPEndPoint ipEndPoint.

Vrste poruka:
public enum ReliableUdpMessageTypes: short ( // Bilo Bilo Bilo = 0, // Zahtjev za STUN server StunRequest = 1, // Odgovor sa STUN servera StunResponse = 2, // Prijenos datoteke FileTransfer =3, // ... )

Poruka se šalje asinhrono; u tu svrhu protokol implementira model asinhronog programiranja:
javni IAsyncResult BeginSendMessage(ReliableUdpMessage pouzdanUdpMessage, IPEndPoint remoteEndPoint, AsyncCallback asyncCallback, stanje objekta)
Rezultat slanja poruke će biti istinit - ako je poruka uspješno stigla do primaoca i lažan - ako je veza prekinuta zbog isteka vremena:
public bool EndSendMessage(IAsyncResult asyncResult)

Zaključak

Mnogo toga nije opisano u okviru ovog članka. Mehanizmi koordinacije niti, rukovanje izuzetcima i greškama, implementacija asinhronih metoda slanja poruka. Ali srž protokola, opis logike za obradu paketa, uspostavljanje veze i određivanje vremena čekanja, trebalo bi da vam postane jasnije.

Demonstrirana verzija pouzdanog protokola isporuke je prilično robusna i fleksibilna, te ispunjava prethodno definirane zahtjeve. Ali želim da dodam da se opisana implementacija može poboljšati. Na primjer, da biste povećali propusnost i dinamički promijenili periode tajmera, možete dodati mehanizme kao što su klizni prozor i RTT protokolu; također će biti korisno implementirati mehanizam za određivanje MTU između čvorova veze (ali samo u slučaju slanja velike poruke).

Hvala vam na pažnji, radujem se vašim komentarima i komentarima.
Implementacija CLR modela asinhronog programiranja i kako implementirati IAsyncResult obrazac dizajna

  • Premještanje modela asinhronog programiranja na asinhroni obrazac zasnovan na zadatku (APM u TAP):
    TPL i tradicionalno .NET asinkrono programiranje
    Interoperacija s drugim asinkronim obrascima i tipovima
  • Ažuriranje: Hvala mayorovp-u i sidristiju na ideji dodavanja zadatka u interfejs.Kompatibilnost biblioteke sa starijim OS-ovima ne utiče, pošto 4. framework podržava i XP i 2003 server.

    UDP (User Datagram Protocol) je transportni protokol bez veze s nepouzdanom isporukom podataka. One. ne daje potvrdu isporuke paketa, ne čuva redoslijed dolaznih paketa i može izgubiti ili duplirati pakete. UDP radi slično kao IP, osim što se uvodi koncept portova. UDP je generalno brži od TCP-a zbog nižih troškova. Koriste ga aplikacije koje ne zahtijevaju pouzdanu isporuku ili ih same implementiraju. Na primjer, serveri imena, TFTP (Trivalni protokol za prijenos datoteka) usluga, SNMP (Simple Network Management Protocol), sistemi za autentifikaciju. Identifikator UDP protokola u polju Protokol IP zaglavlje – broj 17.

    Svaka aplikacija koja koristi UDP kao uslugu transportnog sloja mora sama osigurati mehanizme potvrde i sekvencijalni sistem numeriranja kako bi se osiguralo da se paketi isporučuju istim redoslijedom kojim su poslani.

    Odredišna luka

    Rice. Format zaglavlja UDP paketa

    Svrha polja udp paketa:

    Broj porta pošiljaoca –Izvor Port(16 bita) – sadrži broj porta sa kojeg je paket poslan, kada je to relevantno (na primjer, pošiljalac čeka odgovor). Ako se ovo polje ne koristi, popunjava se nulama.

    Broj odredišnog porta –Odredište Port(16 bita) – sadrži broj porta na koji će paket biti isporučen.

    dužina -Dužina(16 bita) – Sadrži dužinu ovog datagrama u bajtovima, uključujući zaglavlje i podatke.

    Polje kontrolne sume –Kontrolni zbroj(16 bita) – predstavlja bitnu dopunu 16-bitne sume 16-bitnih riječi. Količina uključena u izračunavanje količine je: paketni podaci sa 16-bitnim poljima za poravnanje granica (nula), zaglavlje UDP paketa, pseudo-zaglavlje (informacije iz IP protokola).

    tcp protokol

    TCP (Transmission Control Protocol) je transportni protokol orijentisan na vezu sa pouzdanom isporukom podataka. Zbog toga ima stroge algoritme za detekciju grešaka dizajnirane da osiguraju integritet prenetih podataka.

    Uzastopna numeracija i potvrda se koriste kako bi se osigurala pouzdana isporuka. Koristeći sekvencijalno numerisanje, određuje se redosled podataka u paketima i identifikuju se paketi koji nedostaju. Uzastopno numerisanje sa potvrdom omogućava vam da organizujete pouzdanu komunikaciju, koja se zove full duplex(pun dupleks). Svaka strana veze daje vlastitu numeraciju za drugu stranu.

    TCP je bajt serijski protokol. Za razliku od paketnih serijskih protokola, on dodjeljuje serijski broj svakom bajtu paketa koji se prenosi, a ne svakom paketu pojedinačno.

    Odredišna luka

    Broj potvrde

    Rice. Format zaglavlja TCP paketa