PROTOCOLUL SCTP – STUDIU ŞI IMPLEMENTARE
Introducere
Scopul lucrării 3
Capitolul 1 – Prezentare teoretică
1.1 Prezentarea protocolului UDP 4
1.2 Prezentarea protocolului TCP 5
1.3 Prezentarea protocolului SCTP 8 1.3.1 Limitările TCP şi UDP 9
1.3.2 Arhitectura SCTP 10
1.3.3 Caracteristicile SCTP 18
1.3.4 Tratarea erorilor în SCTP 26
1.3.5 Securitatea în SCTP 27
1.4 Comparaţie teoretică între UDP, TCP şi SCTP 28
1.5 Prezentarea protocolului HTTP 29
1.5.1 Modul de funcţionare 30
1.5.2 Terminolgie 30
1.5.3 Mesaje HTTP 31
1.5.4 De ce HTTP peste SCTP? 36
1.6 Prezentare generală RRDtool 38
Capitolul 2 – Explicarea aplicaţiei
2.1 Scopul aplicaţiei 40
2.2 Conţinutul aplicaţiei 40
Capitolul 3 – Explicarea testelor
3.1 Rularea testelor
3.1.1 Mediul de testare 43
3.1.2 Cazurile testate 43
3.1.3 Rezultate 44
3.2 Analiza rezultatelor 50
Capitolul 4 – Concluzii finale 51
Anexe
Anexa A – Tabel abrevieri 52
Anexa B – Programe si scrip-uri folosite 53
Bibliografie 75
Această lucrare descrie performanţele protocolului HTTP (Hypertext Transfer Protocol) utilizat peste TCP (Transmission Control Protocol) în comparaţie cu performanţele acestui protocol utilizat peste SCTP (Stream Control Transmission Protocol). SCTP este un protocol de transport modern, care acoperă, în primul rând, o parte din neajunsurile protocolului TCP, dar şi ale protocolului UDP (User Datagram Protocol).
În primul capitol vor fi prezentate cele trei protocoale de transport şi va fi descris motivul utilizării protocolului SCTP ca alternativă pentru TCP în servirea web.
Capitolele al doilea şi al treilea oferă o descriere a programului folosit şi a testelor efectuate. De asemenea, sunt prezentate si verificate doua din cracteristicile protocolului SCTP: inchiderea asocierii in 3 pasi, si mecanismul de tratare a congestiilor.
Capitolul final prezinta concluziile personale referitoare la statisticile obţinute.
Protocolul UDP este cel mai simplu dintre protocoalele de transport. Întrebarea unora este: care ar fi avantajul folosirii protocolui UDP, când există deja TCP? Protocolul TCP, deşi prezintă unele avantaje fiind mai complex, nu este cel mai potrivit pentru anumite aplicaţii, cum ar fi cele în timp real. Protocolul TCP retransmite pachetele neconfirmate, ceea ce ar însemna mult timp pierdut, şi totodată utilizarea inadecvată a resurselor de bandă, în condiţiile în care retransmiterea pachetelor învechite este reduntantă.
Totodata, mecanismul de control al fluxului şi de utilizare a numerelor de secvenţă pentru reordonarea pachetelor sunt lipsite de utilitate pentru transmisiunile în timp real.
De asemenea, UDP este un protocol fără conexiune. Entitatea emitentă începe transmisiune pachetelor adresate fără a se asigura de disponibilitatea receptorului, şi fără a avea loc un schimb de mesaje anterior, pentru a se stabili o conexiune.
Structura antetului UDP este foarte simplă, dupa cum se poate vedea în figura de mai jos:
Port Sursă (16 biti) |
Port Destinaţie (16 biti) |
Lungime(16 biti) |
Suma de control (16 biti) |
Figura 1.1
Portul sursă
Indică portul procesului emitent. Este portul către care trebuie trimise răspunsurile.
Portul destinaţie
Precizează portul procesului destinaţie de pe hostul destinaţie.
Lungime
Este lungimea în octeţi a datagramei utilizatorulu, şi inclusiv a antetului.
Suma de control
Este un câmp opţional de 16 biţi. Reprezintă complementul faţă de unu al sumei în complement faţă de unu a antetului pseudo-IP , antetului UDP şi datelor UDP. Antetul pseudo-IP conţine adresele IP sursă şi destinaţie, protocolul şi lungimea UDP.
Adresa IP Sursă |
||
Adresa IP Destinaţie |
||
Zero |
Protocol |
Lungime UDP |
Figura 1.2
Antetul pseudo-IP extinde efectiv suma de control pentru a include datagrama IP originală (nefragmentată).
UDP este în esenţă o interfaţă de aplicaţie către IP. El nu adaugă nici un fel de siguranţă, control al transmisiei sau recuperare de erori la IP, acestea fiind asigurate la nivelul aplicaţie. El foloseşte portul drept multiplexor/demultiplexor pentru trimiterea şi primirea datagramelor. Numărul portului este cel care face diferenţa între aplicaţii, peste aceeaşi entitate de protocol.
Aceste considerente fac protocolul UDP rapid şi potrivit următoarelor aplicaţii:
Trivial File Transfer Protocol (TFTP)
Serverul de nume DNS (Domain Name System)
Remote Procedure Call (RPC), utilizat de către NFS (Network File System)
Simple Network Management Protocol (SNMP)
Lightweight Directory Access Protocol (LDAP)
Principalul scop al TCP este de a asigura un circuit logic sigur sau un serviciu de conexiune între două procese pereche. El nu se bazează pe siguranţa altor protocoale de nivel inferior (cum este IP), aşa că TCP trebuie să garanteze el însuşi siguranţa transmisiei.
TCP poate fi caracterizat prin următoarele facilităţi pe care le asigură pentru aplicaţiile care îl utilizează:
Transfer de date în şir (Stream Data Transfer)
Din punctul de vedere al aplicaţiei, TCP transferă un şir (stream) continuu de octeţi prin reţea. Aplicaţia nu trebuie să-şi facă griji cu segmentarea datelor în blocuri de bază sau datagrame. O face TCP prin gruparea octeţilor în fragmente TCP, care sunt transferate nivelui reţea (IP) pentru a fi transmise la destinaţie. De asemenea, însuşi TCP decide cum să segmenteze datele şi poate trimite datele mai departe într-un mod convenabil.
Uneori, o aplicaţie are nevoie să fie asigurată că toate datele transmise către TCP au fost în realitate transmise la destinaţie. Pentru aceasta, o funcţie push este definită. Ea va forţa transmiterea tuturor segmentelor TCP rămase în memorie către destinaţie. Funcţia de închidere (close) normală forţeaza şi ea transmiterea datelor la destinaţie.
Siguranţă
TCP atribuie un număr de secvenţă fiecărui octet transmis şi aşteaptă o confirmare (acknowledgement - ACK) de la aplicaţia care recepţioneaza datele TCP. Dacă confirmarea nu vine într-un interval de timp prestabilit, datele sunt transmise din nou. Deoarece datele sunt transmise în blocuri (segmente TCP) numai numărul de secvenţă al primului octet este trimis calculatorului destinaţie. Aplicaţia TCP destinaţie foloseşte numerele de secvenţă pentru a le ordona atunci când sosesc neordonate şi să elimine segmentele duplicate.
Controlul transmisiei (Flow Control)
Aplicaţia TCP destinaţie, când transmite o confirmare (ACK) către entitatea emitentă, indică de asemenea numărul de octeţi pe care îi poate recepţiona pe langă ultimul segment TCP primit, fără să se suprîncarce sau să se confrunte cu depăşirea memoriilor tampon (internal
buffers) ale sale. Acesta este trimis în ACK sub forma numărului cel mai mare de secvenţă pe care îl poate recepţiona fără probleme. Acest mecanism este cunoscut sub numele de fereastră.
Multiplexarea
Este realizată prin utilizarea porturilor, la fel ca şi la UDP.
Conexiunile logice
Siguranţa şi mecanismele de control ale transmisiei descrise mai înainte necesită ca TCP să iniţializeze şi menţină câteva informaţii referitoare la starea fiecărui flux de date (data stream). Această informaţie de stare ce include socketurile, numerele de secvenţă, dimensiunile ferestrelor se numeşte conexiune logică. Fiecare conexiune este unic identificată de către o pereche de socketuri utilizate de către procesele emitente şi receptoare.
Full Duplex
TCP asigură două fluxuri simultane de date în ambele sensuri.
Antetul TCP este mult mai complex decat cel UDP:
Portul Sursă |
Port Destinaţie |
|||||||
Număr de secvenţă |
||||||||
Număr de secvenţă confirmat |
||||||||
Data Offset |
Rezervat |
U R G |
A C K |
P S H |
R S T |
S Y N |
F I N |
Fereastră |
Sumă de control |
Pointer-ul de urgenţă |
|||||||
Opţiuni |
Umplutură |
Figura 1.3
Portul sursă
Numărul portului sursă pe 16 biţi folosit de receptor pentru a raspunde.
Portul destinaţie
Numărul portului destinaţie pe 16 biţi.
Numărul de secvenţă
Numărul de secvenţă al primului octet de date din acest segment. Dacă bitul de control SYN este setat, numărul de secvenţă este numărul de secvenţă iniţial (n) şi primul octet de date este n+1.
Numărul de confirmare
Dacă bitul de control ACK este setat, acest câmp conţine valoarea următorului număr de secvenţă pe care receptorul se aşteaptă să-l primească.
Data Offset
Numărul de cuvinte de 32 biţi din antetul TCP. El indică unde încep datele.
Reserved
Şase biţi rezervaţi pentru utilizare în viitor; trebuie să fie zero.
URG
Precizează că articolul pointer de urgenţă are semnificaţie în acest segment.
ACK
Precizează că articolul de "Număr de secvenţă confirmat" are semnificaţie în acest segment.
PSH
Funcţia push.
RST
Resetează conexiunea.
SYN
Sincronizează numerele de secvenţă.
FIN
Semnalează că nu mai sunt date de la emitent.
Fereastră
Folosit în segmentele ACK. El specifică numărul de octeţi de date începand cu acela indicat în "numărul de secvenţă confirmat" pe care receptorul (adică emitentul acestui segment) doreşte (şi poate) să-l accepte.
Suma de control
Complementul faţă de unu al sumei în complement faţă de unu al tuturor cuvintelor de 16 biţi din pseudo-antet, antetul TCP şi datele TCP. La calcularea sumei de control, câmpul "Sumă de control" este considerat zero.
Pseudo-antetul este acelaşi ca cel folosit de către UDP pentru calculul sumei sale de control. Este un antet pseudo-IP folosit doar pentru calculul sumei de control cu formatul similar, prezentat în figura 1.2.
Pointerul de urgenţă
Indică primul octet de date ce urmează datelor urgente. Are semnificaţie numai atunci când bitul de control URG este setat.
Opţiuni
La fel ca în cazul opţiunilor datagramei IP, opţiunile pot avea doua formate:
-Un singur octet care conţine numărul opţiunii, sau
-O opţiune de lungime variabilă cu următorul format:
Opţiune (16 biţi) |
Lungime (16 biţi) |
Date (lungime -2 octeţi) |
Figura 1.4
Umplutura
Toţi octeţii sunt setaţi zero şi au rol de completare a antetului TCP la lungimea reală care este multiplu de 32 de biţi.
Înainte ca orice dată să fie transmisă, trebuie stabilită o conexiune între cele două procese. Unul dintre procese (de regulă serverul) lansează un apel OPEN pasiv, celălalt un apel OPEN activ. Apelul OPEN pasiv aşteaptă până când un alt proces încearca să se conecteze la el printr-un apel OPEN activ.
Prin reţea sunt schimbate trei segmente:
proces 1 |
|
proces 2 |
|
|
Apelul OPEN pasiv, |
Apelul OPEN activ trimite SYN, secv = n |
|
|
|
|
Recepţionează SYN |
Receptioneaza SYN + ACK. Trimite ACK m+1 |
|
|
Figura 1.5
Conexiunea este acum stabilita şi două fluxuri (streamuri) de date (câte unul pentru fiecare sens) au fost iniţializate (numerele de secvenţă).
Întregul proces este cunoscut ca un "three-way handshake".
De reţinut că segmentele TCP schimbate includ numere de secvenţă iniţiale de la ambele procese pentru a fi folosite la transferurile de date ulterioare. Închiderea conexiunii este facută implicit prin trimiterea unui segment TCP cu bitul FIN setat (cu semnificaţia: nu mai sunt date). Deoarece conexiunea este full-duplex (adică există două fluxuri de date, câte unul pentru fiecare sens), segmentul FIN închide transferul numai într-o direcţie. Celălat proces va trimite acum restul de date pe care-l mai are de transmis şi termină şi el cu un segment în care bitul FIN este setat. Conexiunea este ştearsă (informaţiile despre stare ale ambelor părti) odată ce fluxul de date este închis în ambele direcţii.
Protocolul TCP este folosit în urmatoarele aplicaţii:
File Transfer Protocol (FTP)
Telnet Protocol
Simple Mail Transfer Protocol (SMTP)
Hypertext Transfer Protocol (HTTP)
Post Office Protocol (POP3)
Border Gateway Protocol (BGP)
Pentru a întelege nevoia dezvoltării unui nou protocol şi a utilităţii acestuia corespunzatoare dinamicii aplicaţiilor moderne, prima parte a acestui capitol va descrie limitările protocoalelor mai vechi: TCP şi UDP.
Protocoalele TCP şi UDP sunt cele mai folosite protocoale de transport. Totuşi, seviciile de transfer de date oferite de acestea sunt inadecvate cererilor unei mari plaje de aplicaţii comerciale, cum ar fi cele media în timp real. Aceastea au nevoie de un protocol robust, care să combine flexibilitatea UDP-ului şi siguranţa TCP-ului, pentru transmiterea datelor între două capete. Aceste limitări au condus la dezvoltarea SCTP-ului.
Limitările TCP-ului sunt urmatoarele:
TCP oferă siguranţa transmiterii datelor, dar le transmite în secvenţe ordonate. Unele aplicaţii au nevoie de asigurarea datelor, dar nu neapărat într-o secvenţă, preferând o ordonare parţială, în subşiruri ale datelor. Păstrarea şi transmiterea ordonată a datelor în TCP nu face numai partiţionarea imposibilă, dar generează întârzieri în procesul de tansmitere. Mai mult de atât, daca un singur pachet este pierdut, livrarea pachetelor care urmează este blocată până când pachetul pierdut este retransmis. Acesta cauzează o blocare de tip HOL (head-of-line).
TCP transmite datele într-un flux. Aceasta presupune ca aplicaţiile să folosească propriile marcaje pentru delimitarea mesajelor. Ele trebuie să utilizeze un fanion PUSH în antetul TCP, pentru a se asigura că un mesaj este transmis într-o perioadă de timp rezonabilă.
Într-o conexiune TCP, fiecare terminal poate solicita o singură interfaţă de reţea, conexiunea fiind stabilită între cele doua interfeţe. Ca urmare, în cazul în care conexiunea pică din pricina unei probleme de mediu, transmisiunea se blochează până la restabilirea conexiunii.
TCP-ul este vulnerabil la atacurile de tip DoS (denial of service), cum ar fi difuzarea pachetelor SYN. Un DoS apare când un host altereaza un pachet IP, incluzând un IP fals, iar apoi trimite un numar mare de pachete SYN host-ului victimă. De fiecare dată când stiva TCP a host-ului victimă primeşte un nou pachet SYN, aceasta solicită resurse ale nucleului (kernel) pentru a servi noul pachet SYN. Când stiva este suprasolicitată de astfel de mesaje, host-ul victimă nu mai poate servi mesajele SYN corecte, din lipsă de resurse.
Limitările UDP sunt urmatoarele:
În cazul UDP, transmiterea datelor este nesigură, pentru că este un protocol fără conexiune. Într-o conexiune UDP, aplicaţia nu poate verifica dacă pachetele au ajuns la destinaţie.
UDP nu include un mecanism de control al congestiilor. Ca urmare, mai multe date pot fi trimise pe un mediu deja blocat în urma congestiilor, rezultatul constând în pierderea datelor.
Dacă se includ condiţii de verificare a datelor în aplicaţii care folosesc UDP, implementarea poate produce o complexitate suprasolicitantă a aplicaţiei.
SCTP a fost proiectat să întâmpine limitările TCP. Utilizează un mecanism, numit “four-way handshake” pentru a preveni atacurile DoS. Arhitectura SCTP defineşte pachete cu un nou format, cu o serie de câmpuri adiţionale, cum ar fi cookie şi eticheta de verificare pentru a preveni flood-ul cu pachete SYN. Arhitectura include algoritmi îmbunataţiţi de control al congestiilor, potriviţi mediilor de transmisiune instabile.
Figura următoare prezintă stiva IP şi localizarea protocolului SCTP:
|
|
Figura 1.6
Stiva IP prezintă mai multe nivele, fiecare oferind funcţionalitaţi diferite:
Nivelul fizic stabileşte condiţiile pentru trasmiterea datelor prin mediul fizic
Nivelul legătură de date transmite datele între entităţile de reţea, realizând şi detectarea şi corectarea erorilor apărute la nivelul fizic
Nivelul reţea ruteaza pachetele de date de la emiţător până la receptor, prin reţea. Cel mai utilizat protocol de reţea este IP.
Nivelul transport realizează transferul datelor între capete, folosindu-se de serviciile nivelului reţea. Acest nivel are doua protocoale primare: TCP şi UDP. TCP ofera suport pentru transmisiunile ordonate şi sigure, prin mecanisme de control al erorilor şi fluxului. UDP este un protocol simplu bazat pe mesaj, fără conexiune. SCTP este tot un protocol de transport, pe care dezvoltatorii de aplicaţii îl pot folosi pentru transmiterea între capete a datelor.
Nivelul socket oferă nivelului transport o interfaţă prin care acesta poate interacţiona cu nivelul aplicaţie. Nivelul socket conţine un set de API-uri (application programming interface) care facilitează interacţiuni între nivelul transport şi aplicaţie.
Nivelul aplicaţie furnizează aplicaţiilor o interfaţă de comunicare şi transfer al datelor prin reţea. Toate aplicaţiile de la acest nivel se folosesc de nivelul socket pentru a interacţiona mai departe cu nivelul transport.
Acest subcapitol va descrie modalitatea de conectare între doua capete în TCP şi SCTP. Ambele protocoale iniţiaza o conexiune cu ajutorul unui pachet care declanşează un mecanism handshake. TCP foloseşte un astfel de mecanism în 3 paşi, descris în capitolul anterior, iar SCTP foloseşte un mecanism asemanator, dar în 4 paşi, pentru a stabili o nouă conexiune.
Figura următoare descrie mecanismul handshake în 3 paşi:
|
Figura 1.7
Paşii sunt următorii:
Host-ul A trimite un pachet SYN catre host-ul B
După primirea pachetului SYN, host-ul B alocă resurse pentru a stabili conexiunea şi trimite un pachet SYN-ACK (synchronize-acknowledge) host-ului A
Host-ul A trimite un pachet ACK pentru a confirma primirea pachetului SYN-ACK
Conexiunea a fost stabilită între cele doua host-uri, iar host-ul A poate începe transmiterea datelor.
Următoarea figură descrie mecanismul handshake în 4 paşi pentru SCTP.
Figura 1.8
Următorii paşi descriu acest mecanism:
Host-ul A iniţiaza o asociere prin trimiterea pachetului INIT către host B
Host-ul B răspunde cu un pachet INIT-ACK care conţine următoarele câmpuri:
O etichetă de verificare
Un cookie
Un pachet SYN-ACK TCP nu conţine aceste câmpuri. Câmpul cookie conţine informaţiile de stare necesare, pe care serverul le foloseşte pentru a aloca resurse necesare asocierii. Câmpul cookie include o amprentă pentru autentificare şi un indicator de timp pentru a preveni atacurile cu alte cookie-uri mai vechi. Spre deosebire de TCP, host-ul B în cazul SCTP nu alocă resurse în acest punct al conexiunii. Eticheta de verificare generează o cheie care forţează host-ul A să verifice că pachetul SCTP aparţine asocierii curente.
Host-ul A trimite un pachet INIT-ECHO host-ului B. Dacă host-ul A are un IP alterat, el nu primeşte niciodată INIT-ACK. Aceasta previne host-ul A de la a trimite INIT-ECHO. Ca rezultat, schimbul de mesaje se termină înainte ca serverul să aloce resurse pentru conexiunea în cauză.
Host-ul B raspunde cu o secvenţă (chunck) COOKIE-ACK şi alocă resurse pentru noua asociere.
Conexiunea e acum stabilită între cele doua capete. Host-ul A poate începe acum transmiterea datelor.
În cazul SCTP-ului, transferul datelor poate fi întarziat datorită pasului suplimentar. Din aceasta cauză, mecanismul handshake în 4 paşi pare mai puţin eficient decât cel în 3 paşi. Pentru a depăşi acest neajuns, SCTP permite schimbul datelor în secvenţele COOKIE-ECHO şi COOKIE-ACK.
SCTP transmite datele sub formă de mesaje, şi fiecare mesaj conţine unul sau mai multe pachete.
Port sursă
Port destinaţie
Eticheta de verificare
Suma de verificare
Câmpul tipului secvenţei (chunk)
Fanionul secvenţei
Lungimea secvenţei
Secvenţa de date
. . .
. . .
. . .
Câmpul tipului secvenţei (chunk)
Fanionul secvenţei
Lungimea secvenţei
Secvenţa de date
Figura 1.9
Primele 4 câmpuri (port sursă, port destinaţie, eticheta de verificare şi suma de verificare) formează antetul SCTP, iar urmatoarele grupări de câte 4, secvenţa de control sau date.
În ceea ce priveşte antetul SCTP, porturile sursă şi destinaţie folosesc la multiplexarea diferitelor asocieri SCTP la aceeaşi adresă, eticheta de vericare de 32 de biţi previne inserarea unor mesaje false, alterate sau vechi în asocierea SCTP, iar suma de control de 32 de biţi realizeaza detectarea de erori. Aceasta poate fi fie CRC pe 16 biţi sau Adler pe 32 de biţi.
O secvenţă (chunk) poate fi ori de control, ori de date. Un chunk de control include diferite fanioane şi parametrii, în funcţie de tipul chunk-ului. Chunk-ul de tip DATA include fanioane pentru controlul segmentării şi reasamblării, şi parametrii pentru transmiterea numărului de secvenţă (TSN – transmission sequence number), identificatorului de şir (SID – stream identifier), numărul de secvenţă al şirului (SSN – stream sequence number), şi un
identificator pentru încărcătura de protocol (payload protocol ID). Chunk-ul de date conţine chiar încărcătura în sine.
Fiecare chunk, indiferent de tipul său (date sau control) conţine următoarele informaţii:
Tipul chunk-ului:
Acest câmp identifică tipul de informaţie conţinut în câmpul secvenţei de date. Valoarea câmpului tipului de chunk are valori între 0 şi 254. Valorea 255 este rezervată pentru o utilizare viitoare, ca un câmp extins. SCTP constă în chunk-ul de date şi 12 chunk-uri de control.
Tabelul următor conţine lista şi definiţiile parametrilor diferitelor tipuri de chunk:
Chunk
Definiţie
Folosit pentru transferul datelor
Iniţiază o asociere SCTP între doua capete.
Initiation Acknowledgement (INIT ACK)
Confirmă primirea unui chunk INIT. Receptarea unui chunk INIT-ACK stabileşte o asociere
Selective Acknowledgement (SACK)
Confirmă primirea unor chunk-uri de date şi de asemenea raportează lipsuri în cadrul datelor.
Folosit în timpul procesului de iniţializare. Capătul iniţiator al unei asocieri trimite COOKIE ECHO capătului pereche.
Cookie Acknowledgement (COOKIE ACK)
Confirmă receptarea unui chunk COOKIE ECHO. COOKIE ACK trebuie obligatoriu trimis înaintea oricărui chunk de date sau chunk SACK trimis în asociere, însa poate fi grupat cu acestea, cu condiţia să le preceadă.
Testează conectivitatea cu o destinaţie anume din asociere.
Heartbeat Acknowledgement (HEARTBEAT ACK)
Confirmă primirea unui chunk HEARTBEAT.
Informează capătul pereche de închiderea asocierii. Chunk-ul ABORT informează de asemenea receptorul de motivele închiderii asocierii.
Raportează erorile şi tipul acestora.
Shutdown Association (SHUTDOWN)
Generează o închidere a asocierii cu un capăt pereche.
Shutdown Acknowledgement
Este o confirmare pentru iniţiatorul închiderii asocierii.
Shutdown Complete (SHUTDOWN COMPLETE)
Finiseaza procedura de închidere a asocierii.
Tabelul 1.1
Fanionul secvenţei:
Acest câmp conţine fanioanele, precum U(unordered bit), B(beginning fragment bit – bit care delimitează începutul unui fragment) şi E (ending fragment bit – bit care desemnează sfârşitul unui fragment). Utilizarea acestui bit depinde de tipul chunk-ului specificat în câmpul destinat tipului. Daca nu este specificat, SCTP seteaza acest camp la 0 cât realizeaza transmisiunea şi ignora fanionul la receptarea pachetului.
Lungimea secvenţei:
Acest câmp reprezintă lungimea câmpurilor: tip, fanion, lungime şi valoare a chunk-ului în octeţi.
Datele secvenţei:
Acest câmp conţine chiar informaţia care va fi transferată în cadrul chunk-ului. Modul de utilizare şi formatul acestui câmp depinde de tipul chunk-ului.
Numărul de chunk-uri dintr-un pachet SCTP este determinat de dimensiunea MTU (Maximum Transmission Unit) a căii de transmisiune. Mai multe chunk-uri pot fi grupate într-un pachet SCTP, mai puţin INIT, INIT ACK şi SHUTDOWN COMPLETE. Dimensiunea pachetului SCTP nu trebuie să o depăşească pe cea a MTU.
Formatul pachetului SCTP suportă gruparea mai multor chunk-uri de date şi control în acelaşi pachet, pentru îmbunătăţirea eficienţei transportului. O aplicaţie poate controla această grupare pentru a preveni acest procedeu în timpul transmisiei iniţiale. Deobicei gruparea apare în cazul retransmisiei chunk-urilor de date, pentru a reduce riscurile unei congestii. Dacă datele depăşesc capacitatea unui pachet, SCTP fragmenteaza datele în mai multe chunk-uri.
SCTP foloseşte o variaţiune de algoritmi pentru controlul congestiilor pentru a trata eficient defecţiunile din reţea sau supraaglomerarea neaşteptata a reţelei, şi pentru a asigura o revenire rapidă dintr-o congestie. SCTP şi TCP folosesc acelaşi set de algoritmi de control. Cei patru algoritmi sunt:
Slow start şi Congestion Control
Fast Retransmit şi Fat Recovery
Totuşi, în SCTP, aceşti algoritmi sunt adaptaţi cerinţelor şi caracteristicilor protocolului.
Aceşti doi algoritmi sunt folosiţi pentru a controla cantitatea de informaţie neprelucrată care este transmisă. SCTP foloseste slow start la începutul transmisiunii, când conditiile de mediu sunt necunoscute, şi de asemenea în a recupera pierderile detectate de ceasul de retransmisie. SCTP testeaza întai mediul de transmisiune pentru a determina capacitatea disponibilă a mediului, pentru a evita congestiile. În cazul în care detectează un mediu congestionat, intră în funcţiune algoritmul de evitare, pentru a trata corespunzator congestia.
Aceşti algoritmi folosesc următorele variabile de control:
Fereastra de congestie (cwnd):
Specifică limitele cantităţii de date pe care emitorul le poate transmite prin reţea, înainte de a primi o confimare. Această variabila este menţinută pentru fiecare adresă destinaţie.
Fereastra de recepţie (rwnd):
Specifică limita receptorului în ceea ce priveşte datele primite. Valoarea minima dintre cwnd şi rwnd determină cantitatea de date transmise.
Pragul slow start (ssthresh):
Determină care dintre cei doi algorimi (slow start sau avoidance) trebuie utilizat pentru a controla transmisiunea.
Partial Bytes Acknowledged (partial_byte_acked):
Ajusteaza parametrul cwnd.
Într-o conexiune SCTP, emitorul foloseşte algoritmul slow start dacă valoarea cwnd este mai mică decat ssthresh. Dacă este mai mare, foloseşte algotitmul de evitare a congestiilor. Dacă cele două valori sunt egale, emitorul poate utiliza oricare din cei doi algoritmi. Spre deosebire de TCP, un emitor SCTP trebuie să memoreze variabilele cwnd, ssthresh şi partial_byte_acked pentru fiecare adresă destinaţie pereche. Totuşi, este suficient să reţina o singură valoare rwnd pentru întreaga asociere, indiferent dacă prechea conţine o singură adresă sau este o asociere multi-home.
Algoritmul de control fast retransmit este utilizat pentru a retransmite cât mai optim segmente lipsă într-o asociere SCTP. Când un receptor într-o asociere SCTP primeste un chunk de date neordonat, acesta trimite un pachet SACK cu un TSN neordonat emitorului. Algoritmul fast retransmit foloseşte patru pachete SACK pentru a indica pierderi de date şi retransmite date fără a aştepta ca ceasul de retransmitere să expire. Dupa ce se transmit datele care par să lipseasca, algoritmul de revenire rapidă controleaza transmisiunea noilor date până când orice segment pierdut este retransmis.
Multihomingul este abilitatea ca un singur capăt dintr-o asociere SCTP să conţină mai multe interfeţe cu adrese IP diferite. Într-o conexiune single-homed, un capăt conţine o singură interfaţa de reţea şi o singură adresa IP.
În figura de mai jos este prezentată o conexiune single-homed:
|
Figura 1.10
În figura 1.10, host-ul A conţine o singura interfaţa de reţea (network interface NI), şi la fel şi host-ul B. Doar aceste două interfeţe vor interacţiona.
Când are loc o căderea în reţea, captele sunt complet izolate de reţea. Multihomingul asigură mai multe şanse de menţinere a conexiunii în cazul unei căderi de reţea, comparativ cu TCP-ul. Suportul pentru multihoming în SCTP permite unei singure asocieri SCTP să creeze mai multe legături şi căi, pentru a obţine redundanţă. Aceasta permite unei asocieri SCTP o revenire mai rapidă de la o legătura la alta sau de la o cale la alta, cu un minimum de întreruperi în transmisiune.
Figura următoare prezintă o asociere multi-homed.
|
Figura 1.11
În această figură, host-ul A are mai multe interfeţe de reţea care pot interacţiona cu host-ul B, care la rândul sau are mai multe interfeţe.
SCTP alege o singură adresă ca fiind primară, şi o foloseşte ca destinaţie pentru toate transmisiunile normale de chunk-uri de date. Toate celălalate adrese sunt alternative. SCTP foloseşte aceste adrese pentru a retransmite date şi pentru a îmbunătăţi probabilitatea de a ajunge la capăt. Retransmiterea poate avea loc ca urmare a eşecurilor repetate de a transmite datele la adresa primară. Ca urmare, toate datele sunt transmise adreselor alternative pana cand un chunk HEARTBEAT stabileşte o legătură cu adresa primară.
În timpul iniţierii unei asocieri, capetele schimbă o listă de adrese IP, astfel încat fiecare capăt să poată primi mesaje de la oricare adresă asociată cu capatul pereche. Din motive de securitate, SCTP trimite mesaje de raspuns adresei sursă aflate în mesajul care cere raspuns.
Un capat poate primi mesaje care nu sunt ordonate sau cu perechi de adrese diferite, pentru că multi-homing suportă mai multe adrese IP. Pentru a preveni această problemă, SCTP include proceduri care tratează încercările simultane de iniţiere a unei singure asocieri.
Multistreaming-ul face posibil ca datele să fie transmise în mai multe fluxuri, paralele, independente, astfel încat pierderile de date dintr-un flux să nu afecteze sau să opreasca livrarea datelor pe celălalte fluxuri. Fiecare flux dintr-o asociere SCTP foloseşte două seturi de numere de secvenţă: transmission sequence number (TSN) care adminstrează transmiterea mesajelor şi detectarea de mesaje pierdute, şi perechea SID/SSN care determină secvenţa datelor primite.
TCP transmite datele secvenţial în formă de octeţi într-un singur flux şi se asigură că toţi octeţii au fost livraţi într-o anume ordine. Ca urmare, un al doilea octet este trimis numai dupa ce primul octet a ajuns la destinaţie. Livrarea ordonată a datelor cauzează întarzieri când au loc pierderi de mesaje sau erori de secvenţă într-o reţea. O întarziere adiţionala are loc când TCP se opreşte din transmisie până când ordinea pachetelor este restabilita, în urma primirii unui mesaj neorodonat, sau când retransmite mesaje pierdute.
Respectarea strictă a ordinii mesajelor în TCP determină limitări importante pentru anumite aplicaţii. Aceste aplicaţii necesită o ordonare a mesajelor care afectează aceeaşi resursă (cum ar fi acelaşi apel sau acelaşi canal), astfel că mesajele sunt parţial corelate şi pot fi livrate fără a menţine întreaga succesiune a mesajelor.
Multistreaming-ul în SCTP, unde transmisiunea sigură şi livrarea datelor sunt idependente una de cealaltă, poate preveni aceasta problemă. Această caracteristică evită de asemenea blocajele de tip HOL (head-of-line). Această independenţă subliniaza flexibilitatea unei aplicaţii, permiţându-i să defineasca semantic diferite şiruri de date în cadrul aceluiasi flux SCTP, şi forţând ordonarea mesajelor numai în cadrul fiecărui şir în parte. Ca urmare, pierderile de date pe un şir nu afecteaza alte şiruri. Receptorul poate verifica imediat dacă au existat pierderi în secvenţa de transmisiune (cauzate de exemplu de pierderile de mesaje), şi de asemenea poate determina dacă mesajele primite după dectarea pierderilor fac parte din şirul afectat. Dacă SCTP primeşte un mesaj aparţinând unui şir afectat, un gol corespondent este înregistrat în SSN. Emitorul poate continua livrarea mesajelor şirurilor neafectate, în timp ce memoreaza mesajele din şirul afectat până în momentul retransmiterii.
Figura următoare ilustrează funcţionarea multi-homing-ului într-o asociere SCTP.
Figura 1.12
În mod implicit, SCTP conţine două şiruri. SCTP foloseşte şirul 0 ca şir implicit pentru transmiterea datelor. Aplicaţiile pot modifica numărul de şiruri folosite pentru transmiterea datelor. |
|
În SCTP, o aplicaţie emitentă poate contrui un mesaj dintr-un bloc de date, şi poate instrui SCTP-ul să transporte mesajele unei aplicaţii receptoare. SCTP-ul garantează livrarea acestor mesaje (a blocului) în întregime. De asemenea, informează receptorul atât despre începutul cât şi sfârşitul blocului. Aceasta se numeşte conservarea limitelor blocului. TCP nu păstrează aceste limite. El tratează toate datele care îi sunt pasate de aplicaţie ca pe un şir de octeţi de date, iar mai apoi le transmite receptorului în aceeaşi ordine în care le-a primit. TCP nu păstrează limitele nici în cazul în care pachetele sosesc neordonat. Ca urmare, receptorul nu poate rearanja pachetele. Trebuie să aştepte până pachetele sosesc în ordine, începand de la ultimul pachet nereceptat până la pachetul primit neordonat.
SCTP nu oferă suport pentru o conexiune “semi-deschisă”, cum poate avea loc în TCP. Într-o conexiune semi-deschisă, chiar daca un capăt indică finalul transmisiunii, celălalt capăt continuă să trimită date pentru un timp nedefinit. SCTP, pe de alta parte, presupune că atunci când procedura de închidere a asocierii a început, amandouă captele vor stopa transmisia. De asemenea presupune că este necesară numai eliminarea confirmărilor datelor trimise anterior.
Procedura de închidere foloseşte un mechanism din 3 mesaje, în care fiecare capăt a confirmat chunk-ul de date înainte de completarea procedurii de închidere. Când o terminare imediata este necesară, SCTP trimite un mesaj ABORT unui capăt.
Figura următoare ilustrează închiderea unei conexiuni pentru TCP şi SCTP.
|
(b)
Figura 1.13
Figura 2.13 (a) reprezintă starea semi-deschisă specifică TCP, iar (b) procedeul de inchidere prin 3 mesaje, specific SCTP.
SCTP oferă suport atât pentru adrese IPv4, cât şi pentru IPv6, pe un socket SCTP. Când negocierile pentru o asociere sunt începute, capetele fac schimb de liste de adrese în chunk-urile INIT şi INIT ACK. Adresele capetelor sunt prezentate de următorii parametrii: o adresă IPv4 are asociat un parametru cu valoarea 5, şi o adresă IPv6 un parametru cu valoarea 6. Un chunk INIT poate conţine mai multe adrese, care pot fi atât versiunea 4, cât şi 6.
În SCTP, datele sunt transmise sub formă de pachete. Fiecare pachet conţine un chunk de date şi unul de control. Un capăt SCTP confirmă primirea unui chunk de date prin trimiterea unui chunk SACK capătului pereche. Chunk-ul SACK indica intervalul de TSN-ului cumulative şi non-cumulative, în cazul în care există. TSN-urile non-cumulative indică goluri în secvenţa TSN primită. Când SCTP identifică astfel de goluri, retransmite chunk-urile de date lipsă celuilalt capăt. SCTP foloseşte metoda confirmarilor
întarziate pentru a trimite SACK-uri. În această metodă, SACK este trimis pentru fiecare al doilea pachet, dar cu o limită superioară asupra întarzierilor între SACK-uri. Frecvenţa transmiterii SACK-urilor creşte cu câte unu per pachet primit în cazul in care sunt detectate goluri în secvenţa TSN.
SCTP include diferiti algoritmi de control al congestiilor, cum ar fi slow start, evitarea congestiilor, revenire rapidă şi retransmitere rapidă, pentru a controla fluxul şi retransmiterea datelor. În cadrul acestor algoritmi, receptorul face cunoscută fereastra de primire, şi emitorul anunţă o fereastră de congestie per cale pentru a controla congestiile. Algoritmii de control şi evitare a congestiilor din SCTP sunt asemănători celor din TCP, exceptând faptul că în SCTP capetele trateaza conversia prin octeţii primiţi şi emişi, şi prin TSN-urile emise şi primite. Asta deorece un TSN este ataşat numai unui chunk.
O aplicaţie poate specifica un timp de viaţă pentru datele de transmis. Dacă timpul de viaţă a expirat şi datele nu au fost trimise, o parte din aceste date, cum ar fi mesajele semnalizatoare sensibile la timp, pot fi aruncate. Dacă timpul de viaţă a expirat şi datele au fost trimise, aceste date trebuie să fie şi livrate pentru a se evita orice gol în secvenţa TSN.
SCTP seteaza un capăt să reconfigureze dinamic informaţiile legate de adresele IP, pentru o anumită conexiune. Când capatele schimbă informaţii în timpul iniţializării asocierii, şi utilizarea SCTP este îmbunătăţită, fără a face efectiv modificări în protocol. Această caracteristica este utilă pentru aplicaţiile de reţea şi computaţionale, care adaugă sau întrerup sau reconfigureaza dinamic setări de reţea pentru anumite interfeţe fizice şi au nevoie ca modificarile să fie acceptate dinamic. Această caracteristică de asemenea setează ca un capăt sa-şi stabileasca o adresă primară destinaţie, ca atunci când propria sa adresă primară este ştearsă, capătul pereche este informat de noua adresă unde să trimita datele.
Pentru a seta SCTP să reconfigureze adresele IP dinamic, un pachet SCTP conţine următoarele tipuri de chunk:
Address Configuration Change Chunk (ASCONF):
Chunk-ul ASCONF comunică modificările de configuraţie care interesează, capătului pereche.
Address Configuration Acknowledgment (ASCONF-ACK):
Acest chunk este folosit de receptorul unui ASCONF pentru a confirma primirea unui chunk ASCONF.
Când un pachet este pierdut sau aruncat din cauza unei erori, alta decât o congestie, un capăt poate interpreta greşit erorare cu pricina, şi să presupună că este chiar o congestie în mediu. Această interpretare greşita poate determina capătul să stopeze transmisiunea datelor, ceea ce ar duce la utilizarea inadecvată şi ineficientă a mediului. În funcţie de severitatea erorii, emitorul poate ramane într-o stare de congestie, ceea ce afectează starea ascocierii.
SCTP include chunk-ul PKTDROP care descoperă astfel de pachete, aruncate din motive de eroare, altele decat congestiile. După primirea unui chunk PKTDROP, un capat işi poate informa perechea că a primit un pachet cu o suma de control CRC32c sau Adler-32 incorectă. Atunci perechea poate retransmite pachetul fără a modifica fereastra de congestie.
Odată cu creşterea interesului şi dezvoltării aplicaţiilor în timp real şi a serviciilor de transport sensibile la întârzieri şi pierderi de pachete, raportarea la pachetele pierdute pentru a presupune existenţa unei congestii nu este suficientă. Algoritmii de management al congestiilor ai SCTP-ului au tehnici deja implementate, cum ar fi retransmiterea rapidă sau revenirea rapidă, care minimalizeaza impactul pierderilor.
Aceste mecanisme văd reţeaua ca pe un black box, şi continuă să trimită pachete până când pachetele sunt aruncate din cauza congestiilor. Totuşi, aceste mecanisme nu au ca scop să ajute aplicaţiile sensibile la întârzieri sau pierderi de pachete.
Odată cu implementarea tehnicilor de management al cozilor (queues) active în infrastructura Internetului, routerele pot porticipa la tratarea congestiilor.
Când are loc o congestie şi emitorul continuă să trimită pachete, numărul de pachete din coada de aşteptare a routerului creşte semnificativ, producând sugrumări ale traficului la nivelul echipamentului. În acest caz, routerul marcheaza pachetele cu biţi care să indice că au trecut printr-o congestie (CE – congestion experienced), şi le trimite mai departe receptorului pentru a indica formarea unei congestii, în loc să arunce aceste pachete.
Notificarea explicită a congestiilor (ECN – explicit congestion notification) este un algoritm de management al congestiilor care utilizează o metodă directă de tratare a lor. ECN foloseste câmpul ECN şi câmpul CE în antetul IP pentru a marca pachetele. Câmpul ECN include şi câmpul ECT (ECN-Capable Trasport), care este setat de emitor pentru a indica faptul că cele 2 capete cunosc şi pot utiliza ECN. Bitul CE este setat de router pentru a indica congestiile. Emitorii folosesc ECT (0) sau ECT (1) pentru a indica ECT pentru fiecare punct.
ECN foloseste următoarele informaţii pentru a genera notificările legate de congestii:
Negocieri între capete în timpul stabilirii conexiunii pentru a verifica dacă amândouă cunosc şi pot folosi ECN (sunt ECN-Capable)
Un fanion ECN-Echo (ECNE) în antetul IP, care seteaza receptorul să informeze emitorul de căte ori primeşte un pachet CE
Un fanion pentru fereastra de congestie redusă (cwr) în antetul IP, care setează emitorul să informeze receptorul că fereastra de congestie a fost redusă.
Neajunsul ECN este că o implementare neadecvată a receptorului sau un element de reţea precum router, firewal, sisteme de detectare a intruziunilor pot şterge fanionul ECNE care oferă semnalul de congestie emitorului.
Acest lucru se întamplă deoarece ECN nu are mecanisme de prevenire împotriva ştergerii acestui fanion de către echipamentele de reţea. Mai mult, ECN se bazează pe cooperarea receptorului de a returna emitorului semnale specifice întalnirii unor congestii. Dacă receptorul şterge aceste semnale şi nu le întoarce emitorului, emitorul nu-şi adaptează comportarea condiţiilor de mediu.
SCTP ofera suport pentru ECN şi este expus de asemenea riscurilor metionate mai sus. Acestea includ necunoaşterea semnalelor ECNE care pot cauza ca un emitor SCTP să fie agresiv faţa de fluxurile permise pe reţea. În schimb, SCTP oferă suport pentru ECN-nonce pentru a evita comportamentul deviant al receptorilor faţa de semnalele de congestie. De asemenea ECN-nonce protejează emitorii de alte forme de comportament neadecvate, cum ar fi notificările false şi duplicate incorecte ale notificărilor TSN.
ECN-nonce este o modificare a mecanismului de semnalizare ECN. Îmbunataţeşte controlul congestiilor prin împiedicarea receptorilor de a exploata ECN-ul şi de a câştiga pe nedrept resurse de bandă din reţea. Practic, receptorii nu mai pot modifica şi pachetele aruncate sau marcate. Ca şi în cazul ECN, ECN-nonce utilizează punctele de cod ECN(0) şi ECN (1), antetul IP, fereastra cwr şi biţii ECNE.
ECN-nonce foloseşte 2 biţi din antetul IP numiţi biţi ECN. Emitorul generează aleator un singur bit nonce şi îl codează în punctele de cod ECT(0) şi ECT(1). Pentru a indica congestiile din mediul de transmisiune, routerele suprascriu punctele de cod ECT cu biţi CE. Suma nonce NS este adunarea pe un bit cu transport a nonce-urilor primite de la receptor. Receptorul calculează suma nonce şi o returnează în fanionul NS al chunk-ului SACK, iar emitorul îi verifică valoarea. O sumă nonce incorectă arată că unul sau mai multe nonce-uri lipsesc la recepţie, deorece pentru o sumă corectă sunt necesari toţi nonce. De asemenea, dacă o sumă nonce incorecta este raportată de emitor fără semnale ECNE, emitorul poate presupune ca receptorul se confruntă cu probleme de notificare.
Suportul pentru ECN-nonce din SCTP include urmatoarele:
Un singur parametru nonce în chunk-urile INIT si INIT-ACK, pentru a indica ambelor capete dacă există suport pentru ECN-nonce
Un singur bit de fanion în chunk-ul SACK numit NS, al carui rol este descris mai sus.
SCTP oferă suport pentru transmisiunile de date parţial sigure (PR-SCTP) care setează un emitor SCTP să semnaleze un receptor că nu trebuie să aştepte date de la emitor. PR-SCTP
realizează un serviciu de transmisiuni ordonate, dar nesigure, între două capete, comparativ cu transmisiunile UDP neordonate şi nesigure. PR-SCTP oferă algoritmi similari de evitare şi control al congestiilor ca şi SCTP, atât pentru traficul de date sigur şi partial sigur.
Capacitaţile SCTP-ului de detecţie şi protecţie împotriva picajelor de reţea sunt aplicabile de asemenea traficului parţial sigur. PR-SCTP setează un capăt să detecteze rapid o adresă detinaţie picată şi să treacă la urmatoarea disponibilă. De asemnea notifică când adresa destinaţie devine inaccesibilă.
Capacitatea de grupare a SCTP-ului face posibilă multiplexarea mesajelor printr-o asociere PR-SCTP. Multiplexarea face posibilă transmisia mai multor tipuri de mesaje prin acelaşi protocol (SCTP), fără a necesita şi alte protocoale separate.
SCTP include următorii parametrii şi chunk-uri pentru a face posibil serviciul de transmisiune parţial sigur:
Parametrul Forward-TSN-Supported:
Acesta este un parametru opţional din chunk-urile INIT şi INIT-ACK. Când o asociere este iniţializată, emitorul SCTP trebuie să includă acest parametru în chunk-urile menţionate pentru a anunţa capătul pereche de faptul că ofera suport pentru transmisiunile parţial sigure.
Chunk-ul Forward Cumulative TSN (FORWARD TSN):
Receptorul trimite acest chunk emitorului pentru a-l informa de suportul pentru PR-SCTP. Un emitor SCTP foloseşte acest chunk pentru a determina receptorul să deplaseze suma TSN-urilor primite, pentru că TSN-urile lipsă sunt asociate cu chunk-uri de date care nu pot fi transmise sau retransmise emitorului.
Serviciul de fiabilitate temporală este un exemplu de serviciu parţial sigur pe care SCTP îl oferă nivelurilor superioare folosind PR-SCTP. Acest serviciu setează aplicaţia utilizatorului să indice o limită a duratei în timp în care emitorul trebuie să transmită sau retransmită un mesaj.
Dacă un capăt SCTP oferă suport pentru chunk-ul FORWARD TSN, poate include parametrul Forward-TSN-supported în chunk-ul INIT pentru a indica suportul oferit. Dacă un capăt alege să nu includă parametrul Forward-TSN-supported, nu poate trimite sau procesa un chunk FORWARD TSN niciodată pe timpul duratei de viaţă a unei asocieri. În schimb, trebuie să se comporte de parcă nu ar oferi un astfel de suport, şi să genereze o eroare de câte ori primeşte un chunk FORWARD TSN.
Când receptorul unui chunk INIT sau INIT-ACK detecteaza un parametru Forward-TSN-Supported, dar nu oferă suport pentru Forward-TSN, receptorul poate, opţional, să raspunda cu un parametru Unsupported Parameters parameter, definit în RFC 2960, sectiune 3.3.3.
Un receptor poate efectua următoarele sarcini în cazul în care primeşte un chunk INIT care nu include parametrul Forward-TSN-Supported:
Să includă parametrul Forward-TSN-Supported în INIT-ACK
Să memoreze informaţia că perechea nu oferă suport pentru chunk-urile FORWARD TSN
Să nu trimită chunk-uri FORWARD TSN pe durata de viaţă a unei asocieri
Să verifice nivelurile superioare dacă a fost sau nu cerută informaţie privind suportul oferit de capătul pereche pentru parametrul Forward-TSN-Supported.
Traficul în reţele este imprevizibil. Căderi de reţea neasteptate sau burst-uri de trafic pot avea loc, ceea ce poate duce la inaccesibilitatea unui punct. O asemenea reţea este predispusă la erori şi o aplicaţie emitoare trebuie să fie prudentă când transmite şi retransmite date, deorece capătul receptor ar putea să nu fie disponibil. Această indisponibilitate a receptorului poate fi cauzată atât de o eroare de cale sau o cădere chiar a receptorului.
SCTP oferă metode de tratare a erorilor adecvate pentru a depaşi aceste probleme. Înainte de a transmite date, SCTP trimite chunk-uri de informaţie pentru a verifica dacă o destinaţie este activă. Chiar înainte de a alege o nouă cale pentru a atinge o destinaţie sau inainte de a inchide o asociere, SCTP se asigură că adresa destinaţie este inaccesibilă sau inactivă.
SCTP foloseşte următoarele metode de tratare a erorilor:
Retransmiterea chunk-urilor de date
Emiterea de HEARTBEAT-uri pentru a verifica căderile din reţea
Emiterea de HEARTBEAT-uri pentru a verifica erorile la nivelul endpoint-ului
SCTP foloseste chunk-urile de date pentru a schimba informaţie între două adrese. După primirea unui chunk DATA, adresa receptoare trimite o confirmare adresei emitoare. Dacă adresa receptoare nu primeşte chunk-ul de date corect, trimite un pachet SACK care determină adresa emitoare să retransmită chunk-ul de date. De asemenea, emitorul va retransmite datele dacă ceasul de retransmisie expiră.
SCTP limitează rata de retransmisie a chunk-urilor de date pentru a reduce şansele de congestie. Modifică valoarea ceasului de retransmisie (RTO – retransmission time out), bazat pe estimarea round trip delay, şi reduce rata de transmisie exponential, când pierderile de mesaj cresc.
Într-o asociere SCTP activă, cu transmisiuni constante de date, pachetele SACK au o probabilitate mai mare de cauzare a retransmisiei, decât expirarea ceasului de retransmisie. Pentru a reduce cât mai mult cantitatea datelor retransmise, SCTP foloseşte regula celor patru pachete SACK, astfel încat SCTP să retransmită un chunk de date numai dupa primirea celor patru SACK, care se vor referi la un chunk de date lipsă. De asemenea, SCTP foloseşte cele patru pachete SACK pentru a evita retansmiterea unor pachete primite neordonat.
SCTP trimite periodic chunk-uri HEARTBEAT către destinaţii inactive, sau adreselor alternative pentru a identifica o eroare de cale. SCTP păstreaza un counter pentru a reţine numărul de heartbeat-uri care sunt trimise destinaţiilor inactive, fără a primi confirmarea normală Heatbeat ACK. Când counterul atinge o valoare maximă specificată, şi protocolul declară adresa destinaţie inactivă. Apoi anunţa aplicaţia de situaţia aparută şi începe să folosească adresele alternative pentru a continua transimisia chunk-urilor de date.
Totuşi, SCTP continuă să trimită heartbeat-uri destinaţiei inactive până primeşte o confirmare. La primirea unui ACK, SCTP consideră destinaţia activă din nou.
Rata cu care SCTP emite aceste heartbeat-uri depinde de suma valorilor RTO şi a parametrului de întarziere, care permite ca traficul de heartbeat-uri să fie ajustat nevoilor aplicaţiei.
Metoda de determinare a acestor căderi sau erori este similară cu cea prezentată anterior, pentru cale.
SCTP păstrează un counter pentru toate adresele destinaţie, pentru a reţine numarul de heartbeat-uri transmise fără a primi confirmare. Când, pentru toate adresele, s-a ajuns la o valoare maximă, prestabilită, SCTP declară capătul pereche picat şi închide asocierea.
SCTP foloseste doua mecanisme de securitate:
- mecanismul cookie
- eticheta de verificare.
Un mecanism cookie este declanşat în timpul iniţializării unei asocieri, pentru a asigura protecţie împotriva atacurilor. Acest mecanism foloseşte un handshake în patru paşi, ultimii doi paţi putând purta şi date pentru un start mai rapid.
Mecanismul cookie pazeşte împotriva atacurilor nedetctabile care generează chunk-uri INIT, care vor suprasolicita resursele serverului SCTP, forţându-l pe acesta să aloce memorie şi resurse pentru a trata noul chunk INIT. În loc să aloce memorie pentru un TCB (transmission Control Block), server-ul creeaza un parametru cookie cu informaţie TCB, împreuna cu un timp de viaţă valid şi o amprenta pentru antentificare, şi trimite acestea înapoi într-un chunk INIT-ACK. Atacatorul nu va primi niciodată acest chunk, deorece el este directat către adresa destinaţie, adică adresa sursă a chunk-ului INIT. Un client SCTP valid va primi cookie-ul şi va întoarce un chunk COOKIE ECHO, pe care serverul va putea să-l preia şi să-l folosească pentru
recontruirea TCB-ului. Cookie-ul este creat de server, iar formatul şi cheia secretă raman pe server, deorece nu schimbă aceste informaţii cu clientul.
Eticheta de verificare este un întreg unasigned de 32 de biţi (U32) care este generat aleator pentru a verifica dacă pachetul SCTP analizat aparţine asocierii curente, sau este un pachet rătăcit, corespunzător unei asocieri anterioare. SCTP aruncă pachetele primite fără eticheta de verificare aşteptată, pentru a proteja de atacuri mascate sau pachete din asocieri anterioare.
Eticheta de verificare se aplică în cazul pachetelor SCTP care nu conţin următoarele chunk-uri: INIT, SHUTDOWN COMLETE, COOKIE ECHO, ABORT sau SHUTDOWN ACK.
În timp ce trimite un pachet SCTP, un terminal trebuie să completeze eticheta de verificare a pachetului de ieşire, cu valoarea parametrului Initiate Tag din INIT sau INIT ACK primite de la terminalul pereche.
După primirea unui pachet SCTP, terminalul trebuie să se asigure că valoarea din câmpul etichetei de verificare a pachetului se potriveşte cu propria etichetă. Dacă această potrivire nu există, receptorul aruncă pachetul şi nu îl mai proceseaza în niciun fel.
Eticheta de verificare este aleasă de fiecare terminal în parte, în etapa de stabilire a unei asocieri.
Deşi capitolele anterioare au subliniat şi detaliat diferenţele fundamentale între cele trei protocoale de trasport, o rezumare şi o categorisire a acestor diferenţe este necesară.
Tabelul urmator compara caracteristicile SCTP, TCP si UDP
Caracteristica
SCTP
TCP
UDP
Cunoaşterea stării pentru fiecare terminal
Da
Da
Nu[1]
Orientat pe conexiune
Da
Da
Nu
Transfer de date sigur
Da
Da
Nu
Controlul şi evitarea congestiilor
Da
Da
Nu
Conservarea limitelor mesajelor
Da
Nu[2]
Da
Fragmentarea mesajelor şi descoperirea MTU unei căi
Da
Da[2]
Nu
Gruparea mesajelor
Da
Da[2]
Nu
Suport pentru multi-home
Da
No
Nu
Suport pentru multi-stream
Da
Nu
Nu
Livrare neordonată a datelor
Da
Nu
Da
Protecţie împotriva atacurilor cu pachete SYN
Da
Nu
Nu
Folosirea heartbeat-urilor (verificarea disponibilităţii)
Da
Nu[3]
[1] În UDP, un nod poate cumunica cu un alt nod fără a trece printr-o procedură de pregătire sau fără a schimba informaţii de stare. Totuşi, fiecare pachet UDP conţine informaţiile de stare necesare pentru formarea unei conexiuni, astfel încat să nu fie necesară menţinerea unei stări anume la nivelul fiecărui terminal.
[2] TCP nu păstrează nici o limită a mesajelor. Tratează toate datele primite de la nivele superioare ca pe şiruri de octeţi neformataţi. Totuşi, deoarece TCP transferă datele în secvenţe de octeţi, poate automat să redimensioneze toate datele în noi segmente TCP potrivite noiului MTU, înainte de transmiterea lor.
[3] TCP implemenentează un mecanism de keep-alive, similar cu chunk-ul HEARTBEAT din SCTP. În TCP, totuşi, intervalul de keep-alive este, în mod implicit, la două ore, pentru a reseta starea. În SCTP, chunk-ul HEARTBEAT este folosit pentru a facilita detectarea rapida a unor căderi.
Tabelul 1.2
Deşi protocolul SCTP pare să aibă multe in comun cu UDP-ul, el de asemenea este un protocol de încredere, ca TCP-ul. Este un protocol bine gândit, care uneşte mai multe caracteristici ale TCP-ului şi UDP-ului, făcându-l, cel puţin în teorie, mai bun decât cele două.
Protocolul de transport al hiper-textelor HTTP (Hyper-Text Transport Protocol) este un protocol bazat pe stiva de protocoale TCP/IP, destinat sistemelor hipermedia conlucrând în medii distribuite. HTTP a început să fie proiectat şi utilizat din anul 1990, dezvoltîndu-se împreuna cu spaţiul WWW.
Prima versiune, referită ca HTTP/0.9, a reprezentat un simplu protocol de transfer de date prin Internet. Urmatoarea versiune, HTTP/1.0, definită de RFC 1945, a îmbunătăţit transferul de mesaje, permiţându-se mesaje în format MIME (Multipurpose Internet Mail Extensions), conţinând meta-informaţii despre datele transmise şi despre semantica dialogului cererilor şi raspunsurilor dintre clienţii şi serverele Web.
A urmat HTTP/1.1 care oferă mai multe funcţionalităţi suplimentare, precum mecanisme de căutare, adnotare şi actualizare, având suport pentru URI (Uniform Resource Identifier), specificând adresele ca locaţie (prin URL) sau prin nume (via URN). HTTP de asemeni este utilizat ca protocol generic pentru comunicarea între agenţii utilizator şi porţile (gateways) către alte sisteme Internet, incluzând suport pentru protocoale ca SMTP (Simple Mail Transfer Protocol), NNTP (Network News Transfer Protocol), FTP (File Transfer Protocol), Gopher. În acest mod, HTTP permite accesul hipermedia la resurse disponibile din diverse aplicaţii.
Protocolul HTTP este un protocol sigur, de tip cerere/raspuns, comunicaţiile decurgând peste conexiunile TCP/IP, portul standard de acces fiind portul 80.
HTTP oferă o tehnică de comunicare prin care paginile web se pot transmite de la un terminal aflat la distanţă spre propriul terminal. Dacă se apelează un link sau o adresă de web cum ar fi http://www.example.com, atunci se cere calculatorului host să afişeze o pagină web (index.html sau altele). În prima fază numele (adresa) www.example.com este convertit de protocolul DNS într-o adresă IP. Urmează transferul prin protocolul TCP pe portul standard 80 al serverului HTTP, ca răspuns la cererea HTTP-GET. Informaţii suplimentare, cum ar fi indicaţii pentru browser, limba dorită şi altele se pot adăuga în header-ul (antetul) pachetului HTTP. În urma cererii HTTP-GET urmează, din partea serverului, răspunsul cu datele cerute, ca de exemplu: pagini în (X)HTML, cu fişiere ataşate ca imagini, fişiere de stil (CSS), scripturi (Javascript), dar pot fi şi pagini generate dinamic (SSI, JSP, PHP şi ASP.NET). Dacă dintr-un anumit motiv informaţiile nu pot fi transmise, atunci serverul trimite înapoi un mesaj de eroare.
conexiune
Un circuit virtual la nivelul transport stabilit între doua programe care doresc să comunice.
mesaj
Unitatea de baza a unei comunicaţii HTTP, constând dintr-o secvenţă structurată de octeţi, transmisă printr-o conexiune.
cerere
Un mesaj de cerere HTTP.
raspuns
Un mesaj de raspuns HTTP.
resursa
Un obiect de date sau serviciu care poate fi identificat de un URI. Resursele pot avea reprezentări multiple (e.g. formate, mărimi, rezoluţii etc.).
entitate
Informaţia transferată într-o operaţie de cerere sau de raspuns. O entitate cuprinde atît meta-informaţii, cît şi conţinutul propriu-zis.
reprezentare
O entitate inclusă într-un raspuns ce reprezintă o negociere între client şi server.
negociere
Mecanism de selectare a reprezentării potrivite a unei date solicitate.
client
Un program care stabileşte conexiuni cu scopul de a trimite cereri.
agent-utilizator
Clientul ce iniţiaza o cerere (navigator, editor, robot de traversare Web). Navigatoarele cele mai cunoscute sunt Internet Explorer, Mozilla, Opera.
server
O aplicaţie care acceptă conexiuni, raspunzând la anumite cereri transmise de clienţi. Un program poate juca rol atât de server, cât şi de client. Un server se poate comporta ca server iniţial, poartă, tunel sau proxy Web în funcţie de natura cererii. Cele mai cunoscute servere Web sînt: Apache, Netscape Enterprise Server, Sun Web Server, Windows NT Web Server, Stronghold.
proxy (intermediar)
Un program intermediar care ruleaza ca server, cât şi drept client pentru a transmite cereri altor servere. Cererile trimise unui proxy pot fi rezolvate intern sau transmise mai departe, către alte servere (posibil translatate). Un proxy trebuie să implementeze cerinţele de server şi de client ale specificaţiei HTTP.
poartă
Un server care lucrează ca intermediar pentru alte servere, în mod transparent, fiind şi un translator de protocoale.
tunel
Un program intermediar funcţionând ca mijlocitor între două conexiuni.
cache
Un depozit local de memorare a mesajelor (datelor) de raspuns şi un subsistem de control al acestuia. Memoria cache reduce timpul de raspuns şi congestia reţelei. Orice client şi server poate include un cache.
Un mesaj HTTP este divizat într-o parte de antet şi o parte corp. Antetul cuprinde o serie de câmpuri (unele dintre ele obligatorii) oferind informaţii despre versiunea de protocol folosit, codificarea datelor, tipul de medii, lungimea şi tipul mesajului etc. Sintaxa unui antet de mesaj este:
message-header ::= field-name ":" [ field-value ] CRLF
Formatul unei cereri este următorul:
Request ::= Method Request-URI ProtocolVersion CRLF
[ message-header ] [ CRLF data ]
Method ::= string
data ::= MIME-data
Raspunsul la o cerere are următoarea sintaxa:
status-line ::= HTTP-version status-code reason CRLF
status-code ::= digit digit digit
reason ::= string
Orice mesaj HTTP trebuie să debuteze cu un câmp indicând versiunea protocolului în prima linie a mesajului:
HTTP-Version ::= "HTTP-Version" ":" "HTTP" "/" digit "." digit
În prezent este operaţional protocolul 1.1, deci toate mesajele de cerere şi de raspuns vor începe cu linia HTTP/1.1.
Mesajele pot fi codificate conform autorităţii IANA (Internet Assigned Numbers Authority) fiind permise codificarile:
gzip (GNU zip) este un cod Lempel-Ziv (LZ77) cu suma de control pe 32 de biţi
compress este un cod produs de programul compress din toate mediile UNIX, după codificarea Lempel-Ziv-Welch (LZW)
Aceste codificări sunt specificate de câmpul Content-Transfer-Encoding.
Pentru MIME, se specifică tipul şi subtipul mediului de informaţii (de exemplu: text/html, text/plain, image/jpeg, video/mpeg etc.) în câmpul Content-Type. Un mesaj poate fi transmis în format multipart, constând din mai multe entităţi, toate având o sintaxă comună. Dacă o aplicaţie recepţioneaza un subtip nerecunoscut, în mod automat îl va trata ca multipart/mixed.
Câmpul Accept dintr-un mesaj de cerere poate specifica mulţimea de tipuri de date returnate de server ca răspuns:
Accept ::= "Accept" ":" (media-range [ accept-params ])+
media-range ::= ("*/*" | type "/*" | type "/" subtype)*
accept-params ::= ";" "q=" qvalue accept-extension*
accept-extension ::= ";" token [ "=" (token | string) ]
type ::= string
subtype ::= string
qvalue ::= digit "." digit
Simbolul "*" specifică toate tipurile/subtipurile de medii dintr-o anumită categorie. De exemplu, pentru a accepta doar imagini, indiferent de format, se va transmite Accept: image/*.
Pot fi specificaţi unul sau mai mulţi factori de calitate relativă. De pildă, cererea Accept: audio/*; q=0.2, audio/basic este interpretată astfel: "se prefera tipul audio/basic dar serverul va trebui sa trimita toate tipurile audio având calitatea de cel putin 80%".
Locaţia unei resurse HTTP va fi dată de cîmpul Content-Location în formatul URI, conform schemei http:
Content-Location ::= "Content-Location" ":" http-URL
http-URL ::= "http://" host [ ":" port ] [ abs_path ]
De exemplu: http://www.infoiasi.ro/~busaco/egon/http.html
Dacă portul nu apare, se ia prin definiţie portul 80, rezervat protocolului HTTP. Adresele host vor fi tratate indiferent de scrierea cu caractere majuscule sau nu (case-insensitive), dar calea de directoare abs_path este tratată case-sensitive (majusculele sunt diferite de minuscule).
Dacă abs_path nu apare, se va considera "/", iar caracterele rezervate vor fi echivalente cu codul lor în hexa, precedat de "%" (în forma "% hex hex").
De exemplu, aceste trei URI sunt echivalente:
http://www.infoiasi.ro:80/~busaco/poems.html
http://www.infoiasi.ro/%7Ebusaco/poems.html
http://WWW.InfoIasi.Ro/%7ebusaco/poems.html
Corpul mesajului va conţine informaţiile propriu-zise ale unei cereri sau raspuns, specificate de o entitate. O entitate-corp diferă de corpul mesajului numai dacă sunt aplicate codificări.
Linia de cerere a unui mesaj HTTP este definită astfel:
Request-Line ::= Method Separator Request-URI Separator HTTP-Version CRLF
Method ::= "OPTIONS" | "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE"
Request-URI ::= "*" | absolute-URI | abs_path
Serverul va returna codul 405 (Method Not Allowed) sau 501 (Not Implemented) dacă se va trimite numele unei metode inexistente.
Descrierea metodelor permise urmează mai jos:
OPTIONS
Reprezintă o cerere de informaţii despre opţiunile de comunicare disponibile într-un dialog cerere/raspuns.
GET
Reprezintă o cerere de accesare a unor informaţii (entităţi) identificate de Request-URI. Semantica metodei GET se schimbă în cerere conditionata dacă mesajul de cerere include cîmpuri antet If-Modified-Since, If-Match, If-Range etc. Dacă se specifică un cîmp Range, atunci GET va specifica o cerere parţiala.
HEAD
Este similară cu GET, dar serverul va returna un mesaj având informaţii doar în antetul lui. Meta-informaţiile din antetele HTTP din raspunsul la o cerere HEAD vor fi identice cu cele din raspunsul la o cerere GET. Metoda HEAD este folosită deseori pentru testarea validităţii, accesibilităţii şi modificărilor recente ale unei legaturi hipertext. Pentru documente HTML, o cerere HEAD va avea ca raspuns
doar antetul paginii, adică informaţiile cuprinse între marcatorii <head>...</head>.
POST
Metoda e utilizată să identifice dacă serverul acceptă o entitate înglobata în cadrul cererii. POST este proiectată să implementeze o metodă uniformă pentru funcţiile: adnotarea resurselor, trimiterea unui mesaj într-o lista de ştiri, trimiterea datelor unui formular Web, extinderea unei baze de date printr-o operaţiune de adăugare. Semantica exactă a metodei este definită de server. Raspunsul serverului poate fi 200 (OK), 201 (Created) sau 204 (No Content).
PUT
Specifică faptul ca o entitate inclusă în mesaj să fie stocată la adresa dată de Request-URI. Dacă resursa deja există, se consideră o actualizare a ei. Diferenţa fundamentală între PUT şi POST este reflectată de modul diferit de manipulare a adresei REquest-URI. Într-o cerere POST, URI identifică resursa care va prelucra entitatea înglobată de mesaj. Acea resursă poate fi un proces de prelucrare a datelor, o poartă către alt protocol, o entitate separată acceptând adnotări. Prin contrast, URI dintr-un PUT identifică entitatea inclusă în mesajul de cerere. O unică resursă poate fi identificată de URI-uri multiple.
DELETE
Cere ca serverul să şteargă resursa identificată de Request-URI.
TRACE
Invocă o cerere de diagnosticare (trasaj).
Pentru fiecare cerere a unui client, serverul HTTP raspunde cu o serie de coduri de stare a operaţiei solicitate, dintre care menţionăm:
Acestea dau informaţii despre o anumită acţiune:
100 Continue - clientul poate continua cererea, trebuind să trimită următoarea parte a unui mesaj parţial;
101 Switching Protocols - serverul înţelege cererea, dar necesită recepţionarea unui câmp Upgrade pentru a şti ce tip de protocol va fi folosit la nivelul aplicaţiei (e.g. pentru transmiterea de informaţii multimedia, când poate fi utilizat un protocol sincron, în timp-real);
Acestea raportează efectuarea cu succes a unei operaţiuni:
200 Ok - cererea a fost rezolvată cu succes;
201 Created - o nouă resursă a fost creată cu succes. Resursa creată poate fi identificată de URI-ul returnat de entitatea-raspuns, trebuind a fi generată înainte de returnarea codului (în caz contrar se trimite 202);
202 Accepted - cererea a fost acceptata spre procesare, dar înca n-a fost satisfacută în întregime. Nu există nici o facilitate pentru retransmiterea unui cod de stare în urma execuţiei unei operaţii asincrone;
204 No Content - serverul a rezolvat cererea, dar nu are ce returna;
Indică o redirectare, către altă locaţie/server a cererii (de exemplu, la apariţia marcatorului <meta name="refresh" content="..."> în cadrul unui document HTML):
300 Multiple Choices - resursa cerută corespunde uneia dintre reprezentarile multiple pe care le are;
301 Moved Permanently - resursa cerută a fost asignată unui URI nou şi orice referinţa viitoare la ea trebuie sa se realizeze prin acest URI returnat;
302 Moved Temporarily - resursa cerută are un alt URI, pentru o perioadă temporara;
303 See Other - raspunsul la cerere poate fi găsit la o altă locaţie URI şi trebuie accesat folosind o metodă GET;
305 Use Proxy - resursa cerută trebuie accesată printr-un proxy desemnat de câmpul Location;
Indică apariţia unei erori pe partea clientului:
400 Bad Request - cererea n-a putut fi înţeleasă de server din cauza unei sintaxe eronate a metodei;
401 Unauthorized - cererea necesită autentificarea utilizatorilor, prin transmiterea unui câmp Authorization;
403 Forbidden - serverul întelege cererea, dar refuză s-o satisfacă din diverse metode;
404 Not found - serverul nu gaseşte resursa specificată de Request-URI;
406 Not Acceptable - resursa este incompatibilă cu antetul cererii;
Sunt coduri semnificând o eroare pe partea serverului:
501 Not Implemented - serverul nu are implementată o funcţionalitate necesară satisfacerii unei cereri;
502 Bad Gateway - serverul, lucrând ca poartă sau proxy, a recepţionat un răspuns invalid de la alt server căruia i-a trimis cererea;
503 Service Unavailable - serverul nu poate satisface cererea, din cauza supraîncărcării temporare sau a unor raţiuni de administrare;
504 Gateway Timeout - timpul de aşteptare a raspunsului a expirat.
Pot fi transmise şi coduri de avertisment, cu valori cuprinse între 0 şi 99.
Este importantă înţelegerea mijlocului prin care un client descarcă informaţie de pe o pagină web. Folosind HTTP 1.0, un client întâi deschide o conexiune TCP, descarcă codul care descrie pagina, apoi deschide noi conexiuni pentru a descărca imagini şi alte informaţii.
Figura 1.14 – o pagina web tipică
Figura 1.15 – pagina web numai cu imagini
Pentru prima figură corespund patru conexiuni, iar pentru cea de-a doua zece.
Este evident că paginile care conţin informaţii suplimentare bogate pe lângă text suprasolicită resursele de reţea.
Versiune 1.1 de HTTP încearcă o rezolvare a acestei situaţii încercând să minimizeze numărul de conexiuni TCP folosite. În particular poate folosi ceea ce se cheamă conexiuni persistente pentru a atinge acest scop. În aceste conexiuni persistente se pot folosi stive sau cozi, care permit clienţilor HTTP să genereze mai multe cereri fără a mai aştepta raspunsurile pentru cererile deja lansate şi aflate în aşteptare. Aceasta poate îmbunataţi preformanţele HTTP peste TCP, dar are un dezavantaj important. În HTTP 1.0 fiecare cerere este separată, fiind pe conexiuni separate, însa în HTTP 1.1, această caracteristică este distrusă, prin folosirea cozilor de aşteptare, şi rămane la latitudinea browser-ului să trateze răspunsurile asincrone.
HTTP-ul nu este limitat la TCP în ceea ce priveşte protocol de transport. RFC-ul subliniază faptul că orice protocol de reţea şi de transport poate fi folosit, atâta timp cât oferă o conexiune sigură şi stabilă.
SCTP este un astfel de protocol de transport. Dupa cum am precizat deja, SCTP include o caracteristică numită “multi-stream”. Scopul acestei lucrări este de a verifica dacă această caracteristică de multistreaming poate fi folosită pentru a creşte throughput-ul HTTP-ului şi dacă performanţele SCTP-ul îl fac pe acesta comparabil sau chiar mai bun decat TCP-ul, şi dacă multistreaming-ul poate fi de ajutor în reţelele predispuse la aruncarea pachetelor, aşa cum presupune teoria.
RRDtool se referă la Round Robin Database tool. Round robin este o tehnică care lucrează cu o cantitate fixă de date şi un pointer la elementul curent. Analogia ar fi cu un cerc cu niste puncte schiţate pe margine, reprezentând locurile în care pot fi stocate datele. Pointer-ul ar fi o săgeată cu orginea în centrul cerului şi vârful indicând punctele schiţate. Când datele curente sunt citite sau scrise, pointer-ul se mută la urmatoarea locaţie, până parcurge toate locaţiile, deci practic tot cercul. În acest moment, cele mai vechi locaţii vor fi refolosite. În acest fel, baza de date nu va creşte în dimensiune, şi ca urmare nu necesită întreţinere. RRDtool lucrează cu RRDs (Round Robin Databases). Primeşte şi memorează date de la acestea.
Practic, RRDtool permite memorarea şi analizarea datelor obţinute de la tot felul de surse de date (DS – data sources). Partea de analiză se bazează pe capacitatea RRDtool de a genera rapid reprezentări grafice ale valorilor datelor colectatd într-un timp predefinit.
În general man pages descriu comenzile care pot fi folosite pentru a genera statistici cu RRDtool, dar acestea poate fi organizate într-o stivă (scrip), când este nevoie să execute multe operaţiuni într-un timp scurt, şi atunci folosim noţiunea de “telecomandă” (remote control) .
Crearea unei noi baze de date Round Robin
Update:
Înregistrarea unor noi date în RRD.
Graph:
Generarea unor grafice pe baza datelor aflate în una sau mai multe RRD. În afară de generarea graficelor, datele mai pot fi extrase către stdout (standard output)
Dump:
Depozitarea conţinutului unei RRD într-un fişier ASCII. În raport cu restore, dump poate fi folosit şi pentru a transporta o RRD de la o arhitectura la alta
Restore:
Refacerea unei RRD din format XML într-o RRD binară.
Fetch:
Aducerea datelor pentru o anumită perioada de timp din RRD. Funcţia graph foloseşte fetch pentru a-şi obţine datele din RRD.
Tune:
Modificarea unei RRD.
Last:
Găsirea ultimului update dintr-o RRD.
Info:
Afişarea unor informaţii despre RRD.
Rrdresize:
Schimbarea dimensiunii unei RRD (nerecomandat).
Xport:
Exportarea datelor primite de la una sau mai multe RRD.
Rrdcgi:
Aceasta este o unealta independentă pentru generarea graficelor pe loc.
Achiziţionarea datelor:
Când se monitorizează starea unui sistem, este convenabil ca datele să fie disponibile la intervale de timp constante. Din pacate nu este tocmai posibil lucrul acesta, aşa că fişierele cu log-uri pot fi updatate oricând. RRDtool va insera automat valorile de la DS la ultima etichetă de timp oficială.
Consolidarea:
Să presupunem că utlizatorul doreşte să memoreze un set de date la fiecare minut, dar vrea acest lucru pentru un an de zile. Evident, acest lucru este realizabil, dar consumator de resurse, iar apoi datele devin imposibil de analizat. Prin caracteristica sa de consolidare, RRDtool oferă o soluţie acestei probleme. Când se defineşte o nouă RRD, se poate stabili la ce interval să se facă consolidarea şi ce funcţie de consolidare (CF) să se folosească (media, minimum, maximum, ultimul) pentru a consolida datele. Se pot defini oricâte moduri de consolidare pentru o RRD. Se va updata pe măsura ce noi date vor fi introduse.
Arhivele Round Robin:
Valorile datelor din cadrul aceleiaşi consolidări sunt păstrate într-o arhiva (RRA – Round Robin Archive). Acesta este un mod foarte eficient de păstrare a datelor pentru o anumită perioada de timp.
Funcţionează în felul urmator: dacă dorim memorarea a 1000 de valori la fiecare 5 minute, RRDtool va aloca spaţiu pentru cele 1000 de valori şi pentru un antet. În antet va stoca un pointer către ultima valoare adaugată. Noile valori vor fi scrise precum s-a descris mai sus, limitând automat history-ul la ultimele 1000 de valori. Pentru că pot fi definite mai multe RRA pentru o singura RRD, se mai poate crea o RRA de 750 de valori scrise la fiecare 2 ore, şi se pot păstra datele ultimelor două luni la o rezoluţie mult mai mică.
Folosirea RRA garantează că RRD nu creşte în timp şi că datele vechi sunt automat eliminate. Folosit împreună cu nişte funcţii de consolidare, se pot păstra datele pentru perioade mult mai lungi.
Date necunoscute:
Precum s-a menţionat mai devreme, RRD memorează date la intervale de timp constante. Să presupunem că la momentul înregistrării datele nu sunt disponibile. RRDtool rezolvă aceste situaţii prin stocarea unor date necunoscute marcate cu valoarea *UNKNOWN*. Această valoare este tolerată de toate funcţiile de consolidare, iar în momentul stocării în RRA, se realizează un calcul de validitate pentru a verifica că numărul datelor necunoscute nu depăşeşte o limită.
Trasarea graficelor:
RRDtool permite generarea de rapoarte numerice şi grafice bazat pe datele din RRD. Toate detaliile graficelor pot fi definite de utlizator: dimensiune, culoare, conţinut.
Având în vedere caracteristicile deosebite ale protocolului SCTP, aplicaţia îşi propune să verifice o parte dintre acestea, şi de asemenea să ofere o comparaţie cu protocolul TCP din punct de vedere al comportării vis-a-vis de protocolul HTTP.
Se va verifica comportarea SCTP-ului în medii congestionate, generând 100 de pachete ICMP per secundă, de dimensiuni mult mai mari decât cea implicită de 56 de biţi, folosind comanda:
ping –f –S 100000 10.10.10.10
Se va verifica procedeul de închidere a ascocierii, atât pe server, cât şi pe client, observând că odată iniţiat procesul de închidere pe unul dintre acestea, socket-ul va fi închis pe ambele instanţe. Aceasta va fi realizată folosind aplicaţia tshark. Comanda folosită este:
tshark –i eth0
Pentru a compara SCTP cu TCP, aplicaţia sctp_client.c se conecteaza la server.c printr-o singură adresă, folosind unul şi respectiv 50 de stream-uri, apoi se conectează pe patru adrese, şi foloseşte în prima etapă un singur stream, apoi, într-o a doua etapă 50 de stream-uri.
Aplicatia sctp_client.c este adaptată şi cazul TCP, astfel că se observă şi comportarea TCP-ului în aceleaşi condiţii impuse protocolului SCTP.
Script-ul care realizează testarea automată impune clientului să genereze cereri HTTP-GET o dată la fiecare minut, iar RRDtool înregistrează numărul de kilobiţi şi timpul în care se realizeaza transferul în microsecunde. Apoi, folosind comanda ./graph_both se generează grafice de viteză a reţelei, în cele 3 cazuri.
Aplicaţia este compusă din 10 fişiere.
Acest fişier este un server web, care foloseşte ca protocol de transport SCTP. În prima parte a programului sunt incluse librăriile folosite, apoi sunt declarate constantele: PORT, BUFSIZE, INSTREAMS şi OUTSTREAMS care reprezintă portu-ul pe care se vor face asocierile între server şi client, dimensiunea unui buffer folosit pentru stocarea temporară a diferitelor date folosite pe parcursul programului şi numarul maxim de stream-uri folosite.
A doua parte a programului se ocupă de comportarea HTTP-ului. Întai sunt incluse răspunsul cu versiunea şi antetul HTTP pentru indica o cerere reuşită. Apoi, un raspuns, antent şi
conţinut care vor indica faptul că o cerere nu a fost inţeleasă, că documentul nu a fost găsit sau că metoda folosită nu este cunoscută.
Apoi este definit un pointer la structura de tip addrinfo, pentru a stabili adresele folosite pentru multihoming. De asemenea este realizată o verificare a lor, folosind funcţia getaddrinfo.
Întai este stabilită o referenţiere la structura addrinfo, hints, în care se stabilesc fanioanele, familia de adrese şi tripul protocolului.
Funcţia getaddrinfo întoarce -1 în caz de eroare, şi programul execută exit(1) (se iese din program). Structura hints din aceasta funcţie va fi apelată în următoarea funcţie, cu argumentele: AF_UNSPEC si SOCK_SEQPACKET, adică orice familie de adrese de reţea şi protocol SCTP.
În urma fiecărei alocări sau folosiri a buffer-elor acestea sunt eliberate cu funcţia free().
Toate procesele child terminate sunt închise şi resursele eliberate, pentru ca aceste procese să nu devină orpahns, când procesul părinte este oprit. Aceasta se realizează folosind un handler al semnalului SIGCHLD.
Următoarea parte a programului realizează partea de GET a unei pagini PAGE şi întoarce rezultatul unui descriptor, CONNECTION_FD. Pentru aceasta se verifică întâi dacă pagina cerută începe cu un slash (/) şi nu conţine alte slash-uri, deorece nu este suportată transmisiunea subdirectoarelor. Dacă această condiţie este verificată, se contruieşte un modul, definit într-o structură de tip server_module, prin adăugarea extensiei “.so”. Dacă rezultatul este NULL, fie pagina conţinea erori, fie modulul nu a fost gasit. În oricare din aceste cazuri, este generat mesajul de eroare 404, Not Found, apoi este trimis folosind funcţia sctp_sendmsg().
Dacă pagina a fost încarcată cu succes, se generează mesajul de ok_response, apoi se trimite modulul.
Următoarea parte a programului tratează conexiunile dintre server şi client. Pentru aceasta se foloseşte o functie read, care citeşte datele primite de la client. În cazul în care datele au fost citite cu succes, buffer-ul în care au fost stocate datele este eliberat, şi se trece la procesarea mesajelor primite de la client, pentru a le verifica validitatea. Prima linie este cererea HTTP, care trebuie să conţină: metoda, pagina cerută şi versiunea protocolului. Este posibil ca un client să trimită şi date în plus, cum ar fi diferite antete. Server-ul citeşte toate datele până primeşte o linie necompletată, pe care o consideră sfârşit de antet.
Pentru citire, server-ul foloseşte funcţia sctp_recvmsg().
Dacă ultima citire nu a fost efectuată cu succes, programul consideră că este o problemă de conexiune, şi închide asocierea folosind funcţia close().
Dacă până în acest punct toate condiţiile au fost verificate, începe verificarea datelor din prima linie a cererii HTTP. Dacă versiunea sau metoda nu sunt valide, se trimite un mesaj de eroare folosind funcţia sctp_sendmsg(). Dacă acestea sunt valide, se apelează funcţia handle_get().
Până în acest punct a fost partea de definire a funcţiilor. Funcţia principală, server_run(), stabileşte apelarea şi comportarea acestora.
Funcţia începe cu declararea globală a interfeţelor şi structurilor folosite. Apoi iniţializează handler-ul care termină procesele child.
Apoi se deschid socket-urile SCTP. Funcţiile folosite sunt cele specifice SCTP-ului: socket(), sctp_bind_arg_list() care include sctp_bindx(), listen() şi accept().
Funcţiile setsockopt() şi getsockop() setează şi întorc parametrii socket-ului. Acestea sunt folosite pentru a stabili numărul de sream-uri folosite şi pentru a verifica construirea corectă a socket-ului.
După ce conexiunea este terminată, tot în funcţia principală, se generează un mesaj prin care se stabileşte că a fost acceptată o conexiune, şi sunt date datele clientului. Aceasta este facută cu ajutorul funcţiei getpeername().
Ultima parte a funcţiei principale se ocupă cu crearea unui proces child, pentru a trata conexiunile cu un server concurent.
Acest program este un browser web care funcţionează pe protocolul SCTP. El foloseşte metoda GET pentru a cere date server-ului web.
Partea de început descrie librăriile folosite şi defineşte constantele folosite.
Următoarele doua funcţii, sctpstr_cli() şi sctpstr_cli_multistream() tratează datele primite de la server pe unul sau mai multe stream-uri. Se folosesc funcţiile sctp_sendmsg() şi stp_recvmsg() specifice SCTP-ului:
Funcţia main() deschide socket-ii pentru stabilire unei asocieri SCTP, folosind funcţia socket(). Apoi alocă resurse de memorie pentru datele primite de la server.
Acest program încarca modulele necesare tratării cererilor HTTP către server.
În prima parte, acest program defineşte o serie de buffere pentru răspunsurile la cererile HTTP.
Directoarele care trebuiesc trimise clientului sunt deschise, apoi parcurse, şi trimise clientului HTTP.
Acest script compliează sursa clientului.
Acest script compliează sursa server-ului.
Aceste fişiere reprezintă baza de date de tip RRD în care sunt stocate rezultatele testelor.
Acest script porneşte simultan 4 instanţe de client.
Asigură updatarea bazelor de date RRD.
Acest fişier asigură generarea cererilor HTTP-GET o data la 60 de secunde.
Testele au fost efectuate pe două maşini rulând sub Linux Debian 5.0.1,având versiunea compilatorului gcc 2.6.26 şi bibliotecile sctp versiunea 1. Maşinile au fost conectate în reţea print-un comutator (switch) 10/100Mbps.
Pentru demonstrarea multihoming-ului s-au folosit interfaţa fizica eth0 şi 3 subinterfeţe virtuale ale acesteia. Din păcate, acesta se observa numai în pachetele HEARTBEAT şi arp trimise de clienţi, deorece maşinile folosite pentru testare nu dispun decât de o singură interfaţa fizică de reţea.
În primul caz au fost folosite două instanţe simultane ale clientului, funcţionând pe port-uri diferite, folosind un stream de date, pentru o interfaţa şi pentru 4 interfeţe.
Pentru a putea realiza o comparaţie între cele două protocoale, TCP şi SCTP, aplicaţia a fost întâi adaptată cazului TCP, adică protocolul a fost adaptat unui singur stream de date, şi o singură interfaţă de conectare.
Fişierul transferat a fost de aproximativ 30 MB.
În al doilea caz, s-au folosit tot două instanţe ale clientului, pentru una şi respectiv 4 interfeţe, dar folosind 50 de stream-uri de date. De această dată cei doi clienţi au lucrat separat, pentru a se putea observa performanţele SCTP-ului independent, şi intr-o conexiune concurentă, cum este cazul 1.
Datele au fost prelevate pentru cel puţin 20 minute în fiecare caz, timp în care au fost generate următoarele două evenimente:
Congestionarea reţelei folosind comanda ping cu 100 de pachete pe secundă, de dimensiune 100000 biţi.
Deconectarea cablului, şi reintroducerea sa, urmărind reînceperea conexiunii, datorată pachetelor HEARTBEAT.
Într-o ultimă etapă se va observa modul diferit de închidere a unei asocieri între TCP şi SCTP.
S-au pornit cele două instanţe ale clientului şi cele două instanţe corespunzatoare ale server-ului pe două porturi diferite.
Grafic 3.1
In dreptul orei 3:45 a avut loc primul eveniment:
Printscreen 3.1
In drepul orei 3:55 a avut loc al doilea eveniment, de cable out. Cât timp cablul este deconectat, putem observa mesajele HEARTBEAT trimise catre lista de adrese pe care clientul multihomed a făcut bind.:
Printscreen 3.2
Apoi, în momentul în care cablul este reconectat, 4:00, sunt trimise pachete SCTP INIT, pentru a se relua conexiunea.
Se deschid instanţele pentru cei doi clienti şi server-ele corespunzatoare:
Pentru 4 interfeţe rezultatele sunt următoarele:
Grafic 3.2
Cableout/cablein
Printscreen 3.3
Printscreen 3.4
-pentru o interfaţă rezultatele sunt următoarele:
Grafic 3.3
Cableout/cablein
Printscreen 3.5
Printscreen-ul de mai jos prezintă schimbul de date între client şi server. Server-ul trimite pachete SCTP DATA, iar clientul le confirmă cu pachetele SCTP SACK.
Printscreen 3.6
Printscreen 3.
În primul caz, reprezentat de graficul 3.1, putem observa că cei doi clienţi realizează în condiţii de mediu optime un transfer de 9,5 MBps pentru SCTP si 10MBps pentru TCP. Aceste valori mari se poat explica şi datorită faptului că singurul echipament dintre cele două terminale este un switch, destul de rapid, care permite un astfel de transfer.
Când reţeaua este forţată să răspundă cu 100 de pachete ICMP pe secunda, transferul scade numai în cazul clientului SCTP cu maximum 1MBps, dar pierderile de date sunt nule.
La puţin timp după trimiterea pachetelor ICMP, cablul este scos din switch, şi putem observa în Printscreen 3.1 cum clientul multihomed timite pachete HEARBEAT pentru a detecta momentul în care legatura este restabilită.
Aceste pachete sunt trimise pe toate interfeţele pe care s-a facut bind.
În momentul în care cablul este reintrodus în switch, clientul multihomed detectează restabilirea legăturii şi trimite pachetele SCTP INIT, pentru a redeschide asocierea. Transferul de date este reluat.
Acest caz este comparabil cu TCP. Singura diferentă notabilă se înregistrează în generarea pachetelor HEARTBEAT de către SCTP, dar având în vedere caracterul semi-deschis al unei conexiuni TCP, cele doua protocoale detectează într-un anumit intreval de timp posibilitatea unei reconectări
4 interfeţe
În momentul 4:20, reţeaua este deja afectata de pachetele ICMP. Observăm că viteza de transfer este afectată, scanzând de la 9.5 MBps pana la 8,5MBps. Apoi cablul este deconectat în momentul 4:25.
În Printscreen 3.3, observăm cum protocolul încearcă să determine adresele MAC ale interfeţelor pe care a facut bind. În momentul în care cablul este reconectat se transmit pachetele ICMP, conexiunea este restabilită, şi se ajunge la un transfer de aproximativ 10MBps.
Diferenţele dintre un stream de date şi 50 nu sunt foarte relevante, perfomanţele fiind foarte asemănătoare în cazul SCTP-ului.
1 interfaţă
Porţiunea de grafic care interesează este între momentul 4:45 si 5:05. Observam o viteză de 8-9MBps.
În momentul 4:45 este dată comanda ping, observând aceeaşi o scădere mai accentuata a traficului, însa fără pierderi de date. În momentul opririi comenzii, viteza de transfer revine la aproximativ 8MBps.
La fel ca în cazurile precedente, când cablul este deconectat, SCTP-ul verifică reluarea legăturii prin pachete SCTP HEARTBEAT. Când cablul este reconectat, conexiunea este reluată imediat, şi se ajunge la aproximativ aceeaşi viteză de transfer: 8-9 MBps.
Se observă că în cazul SCTP-ului, după generarea unui număr de pachete SHUTDOWN SACK, se iniţiază procedura de închidere a unei asocieri, asupra căreia ambele entităţi de reţea acţionează, prin SHUTDOWN ACK şi SHUTDOWN COMPLETION.
Aceste teste au demonstrat că pentru un singur stream de date, performanţele SCTP-ului sunt chiar mai scăzute decât în cazul TCP-ului.
Deşi SCTP este un protocol foarte flexibil datorită proprietăţii de multihoming, prezentând astfel un avantaj în faţa TCP-ului, totuşi nu este cu mult mai rapid.
Un avantaj major, care asigură o viteză mai ridicată, este proprietatea de multistreaming.
Deşi este un protocol interesant, cu multe caracteristici deosebite, fiind totuşi nou, şi dezvoltat în timp ce protocolul TCP a fost îmbunătăţit, aceste caracterstici nu se fac simţite în totalitate în aplicaţiile vechi.
Totuşi, diferenţele cu adevărat notabile nu pot fi analizate decât pentru aplicaţiile în timp real, pentru care de altfel acest protocol a fost implementat.
UDP |
User Datagram Protocol |
TCP |
Transmission Control Protocol |
SCTP |
Stream Control Transmission Protocol |
HTTP |
Hypertext Transfer Protocol |
TSN |
Transmission Sequence Number |
SSN |
stream sequence numbers |
SID |
Stream ID |
CRC |
cyclic redundancy check |
PPID |
Payload process ID |
MTU |
Maximum Transmission Unit |
HOL |
Head-of-line |
ECN |
Explicit Congestion Notification |
ECNE |
ECN-Echo |
ECT |
ECN-Capable Trasport |
TCP/IP |
Transmission Control Protocol/Internet Protocol |
RRD |
Round Robin Database |
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <netdb.h>
#include <signal.h>
#include <netinet/sctp.h>
#define ECHO_PORT 2019
#define BUFSIZE 128
#define INSTREAMS 50
#define OUTSTREAMS 50
#include "server.h"
/* HTTP response and header for a successful request. */
static char* ok_response =
"HTTP/1.0 200 OK\n"
"Content-type: text/html\n"
"\n";
/* HTTP response, header, and body indicating that the we didn't
understand the request. */
static char* bad_request_response =
"HTTP/1.0 400 Bad Request\n"
"Content-type: text/html\n"
"\n"
"<html>\n"
" <body>\n"
" <h1>Bad Request</h1>\n"
" <p>This server did not understand your request.</p>\n"
" </body>\n"
"</html>\n";
/* HTTP response, header, and body template indicating that the
requested document was not found. */
static char* not_found_response_template =
"HTTP/1.0 404 Not Found\n"
"Content-type: text/html\n"
"\n"
"<html>\n"
" <body>\n"
" <h1>Not Found</h1>\n"
" <p>The requested URL %s was not found on this server.</p>\n"
" </body>\n"
"</html>\n";
/* HTTP response, header, and body template indicating that the
method was not understood. */
static char* bad_method_response_template =
"HTTP/1.0 501 Method Not Implemented\n"
"Content-type: text/html\n"
"\n"
"<html>\n"
" <body>\n"
" <h1>Method Not Implemented</h1>\n"
" <p>The method %s is not implemented by this server.</p>\n"
" </body>\n"
"</html>\n";
extern struct sctp_sndrcvinfo sri;
extern struct sctp_initmsg initmsg;
extern struct sctp_status status;
struct sctp_event_subscribe evnts;
extern char *addr_ip_list;
int sendmesg(int sock_fd, long int stream, char *msg) {
int sz, msg_flags;
struct sockaddr_in serv_addr;
size_t adrlen;
char readbuf[256];
struct sctp_sndrcvinfo sri;
memset(&sri, 0, sizeof(sri));
memset(readbuf, 0, sizeof(readbuf));
sri.sinfo_stream = stream;
sz = strlen(msg);
sz = sctp_sendmsg (sock_fd, msg, sz, NULL, 0,
0, 0, sri.sinfo_stream, 0, 0);
printf ("sctp_sendmsg:[l:%d, e:%d]\n", sz, errno);
return 0;
}
struct addrinfo *
host_serv(const char *host, const char *serv, int family, int socktype)
{
int n;
struct addrinfo hints, *res;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_ALL; /* always return canonical name */
hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */
hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) {
printf("host_serv error for %s, %s: %s",
(host == NULL) ? "(no hostname)" : host,
(serv == NULL) ? "(no service name)" : serv,
gai_strerror(n));
exit(1);
}
return(res); /* return pointer to first on linked list */
}
int delip(int sock_fd, char *iptext)
{
int err,count;
struct sockaddr* addressArray;
count = sctp_getladdrs(sock_fd, 0, &addressArray);
// printAddressArray(addressArray, count);
// serv_addr.sin_addr.s_addr = ip
err = sctp_bindx(sock_fd, addressArray, 2, SCTP_BINDX_REM_ADDR);
sctp_freepaddrs(addressArray);
count = sctp_getladdrs(sock_fd, 0, &addressArray);
// printAddressArray(addressArray, count);
if (err == 0) {
// printf ("Removed %s:%d as association\n", inet_ntoa(serv_addr.sin_addr), port);
} else {
//printf ("failed to remove association %s:%d [l:%d, e:%d]\n", inet_ntoa(serv
}
if(count > 0) {
sctp_freepaddrs(addressArray); // free allocated memory
}
return 0;
}
int
sctp_bind_arg_list(int sock_fd, char **adr_list, int adr_count)
{
struct addrinfo *addr;
char *bindbuf, *p, portbuf[10];
int addrcnt=0;
int i;
/* Allocate space for bind arguments */
bindbuf = (char *) calloc(adr_count, sizeof(struct sockaddr_storage));
p = bindbuf;
/* Process arguments */
sprintf(portbuf, "%d", ECHO_PORT);
for( i=1; i<adr_count; i++ ) {
addr = host_serv(adr_list[i], portbuf, AF_UNSPEC, SOCK_SEQPACKET);
memcpy(p, addr->ai_addr, addr->ai_addrlen);
freeaddrinfo(addr);
addrcnt++;
p += addr->ai_addrlen;
printf("binding on %s\n",adr_list[i]);
}
/* Call binding function */
sctp_bindx(sock_fd,(struct sockaddr *)bindbuf,addrcnt,SCTP_BINDX_ADD_ADDR);
/* Return success */
free(bindbuf);
return(0);
}
/*
static void sigint_handler (int signo) {
// delip(sock , "10.10.10.13");
printf("blah");
fflush(stdout);
return ;
}
*/
static void clean_up_child_process (int signal_number)
{
int status;
wait (&status);
}
/* Process an HTTP "GET" request for PAGE, and send the results to the
file descriptor CONNECTION_FD. */
static void handle_get (int connection_fd, const char* page,struct sockaddr *client_addr)
{
struct server_module* module = NULL;
int len ;
/* Make sure the requested page begins with a slash and does not
contain any additional slashes -- we don't support any
subdirectories. */
if (*page == '/' && strchr (page + 1, '/') == NULL) {
char module_file_name[64];
/* The page name looks OK. Construct the module name by appending
".so" to the page name. */
snprintf (module_file_name, sizeof (module_file_name),
"%s.so", page + 1);
/* Try to open the module. */
module = module_open (module_file_name);
}
if (module == NULL) {
/* Either the requested page was malformed, or we couldn't open a
module with the indicated name. Either way, return the HTTP
response 404, Not Found. */
char response[1024];
/* Generate the response message. */
snprintf (response, sizeof (response), not_found_response_template, page);
/* Send it to the client. */
len = sizeof(client_addr);
sctp_sendmsg(connection_fd, response, BUFSIZE,(struct sockaddr *)&client_addr,
len,
sri.sinfo_ppid,
sri.sinfo_flags,
sri.sinfo_stream,
0, 0);
} else {
/* The requested module was loaded successfully. */
/* Send the HTTP response indicating success, and the HTTP header
for an HTML page. */
sctp_sendmsg(connection_fd, ok_response, BUFSIZE,(struct sockaddr *)&client_addr,
len,
sri.sinfo_ppid,
sri.sinfo_flags,
sri.sinfo_stream,
0, 0);
/* Invoke the module, which will generate HTML output and send it
to the client file descriptor. */
(*module->generate_function) (connection_fd);
/* We're done with the module. */
module_close (module);
}
}
/* Handle a client connection on the file descriptor CONNECTION_FD. */
static void handle_connection (int connection_fd,struct sockaddr *client_addr)
{
char buffer[256];
ssize_t bytes_read;
int msg_flags ;
int len ;
// signal(SIGINT,sigint_handler);
/* Read some data from the client. */
len = sizeof(client_addr);
bytes_read = sctp_recvmsg(connection_fd, buffer, sizeof(buffer),
(struct sockaddr *)&client_addr, &len, &sri,&msg_flags);
printf("received buffer: %s\n",buffer);
if (bytes_read > 0) {
char method[sizeof (buffer)];
char url[sizeof (buffer)];
char protocol[sizeof (buffer)];
/* Some data was read successfully. NUL-terminate the buffer so
we can use string operations on it. */
buffer[bytes_read] = '\0';
/* The first line the client sends is the HTTP request, which is
composed of a method, the requested page, and the protocol
version. */
sscanf (buffer, "%s %s %s", method, url, protocol);
/* The client may send various header information following the
request. For this HTTP implementation, we don't care about it.
However, we need to read any data the client tries to send. Keep
on reading data until we get to the end of the header, which is
delimited by a blank line. HTTP specifies CR/LF as the line
delimiter. */
printf("method: %s\n",method);
printf("url: %s\n",url);
printf("protocol: %s\n",protocol);
bytes_read = sctp_recvmsg(connection_fd, buffer, sizeof(buffer),
(struct sockaddr *)&client_addr, &len, &sri,&msg_flags);
printf("user agent: %s\n",buffer);
/* Make sure the last read didn't fail. If it did, there's a
problem with the connection, so give up. */
if (bytes_read == -1) {
close (connection_fd);
return;
}
/* Check the protocol field. We understand HTTP versions 1.0 and
1.1. */
if (strcmp (protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) {
/* We don't understand this protocol. Report a bad response. */
printf("we dont understand the protocol\n");
sctp_sendmsg(connection_fd,bad_request_response, strlen (bad_request_response) ,(struct sockaddr *)&client_addr,
len,
sri.sinfo_ppid,
sri.sinfo_flags,
sri.sinfo_stream,
0, 0);
}
else if (strcmp (method, "GET")) {
/* This server only implements the GET method. The client
specified some other method, so report the failure. */
char response[1024];
printf("got method GET\n");
snprintf (response, sizeof (response),bad_method_response_template, method);
sctp_sendmsg(connection_fd, response, strlen (response) ,(struct sockaddr *)&client_addr,
len,
sri.sinfo_ppid,
sri.sinfo_flags,
sri.sinfo_stream,
0, 0);
}
else
/* A valid request. Process it. */
handle_get (connection_fd, url,(struct sockaddr *)&client_addr);
}
else if (bytes_read == 0)
/* The client closed the connection before sending any data.
Nothing to do. */
;
else
/* The call to read failed. */
system_error ("read");
}
void server_run (char* const argv[],int nr_ips)
{
struct sockaddr_in socket_address;
int rval;
struct sigaction sigchld_action;
int server_socket;
int len ;
/* Install a handler for SIGCHLD that cleans up child processes that
have terminated. */
memset (&sigchld_action, 0, sizeof (sigchld_action));
sigchld_action.sa_handler = &clean_up_child_process;
sigaction (SIGCHLD, &sigchld_action, NULL);
/* Create a TCP socket. */
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (server_socket == -1)
system_error ("socket");
if(sctp_bind_arg_list(server_socket, argv, nr_ips))
perror("Can't bind the address set");
memset(&initmsg, 0, sizeof(initmsg));
initmsg.sinit_max_instreams = INSTREAMS;
initmsg.sinit_num_ostreams = OUTSTREAMS;
printf("Asking for: input streams: %d, output streams: %d\n",
initmsg.sinit_max_instreams,
initmsg.sinit_num_ostreams);
if (setsockopt(server_socket, IPPROTO_SCTP,SCTP_INITMSG, &initmsg, sizeof(initmsg))) {
perror("set sock opt\n");
}
rval = listen (server_socket, 10);
if (rval != 0)
perror ("listen");
/* Loop forever, handling connections. */
while (1) {
struct sockaddr_in remote_address;
socklen_t address_length;
int connection;
pid_t child_pid;
/* Accept a connection. This call blocks until a connection is
ready. */
address_length = sizeof (remote_address);
connection = accept (server_socket, (struct sockaddr *)&remote_address, &address_length);
if (connection == -1) {
/* The call to accept failed. */
if (errno == EINTR)
/* The call was interrupted by a signal. Try again. */
continue;
else
/* Something else went wrong. */
system_error ("accept");
}
memset(&status, 0, sizeof(status));
len = sizeof(status);
if (getsockopt(connection, IPPROTO_SCTP,
SCTP_STATUS, &status, &len) == -1) {
perror("get sock opt");
}
printf("Got: input streams: %d, output streams: %d\n",
status.sstat_instrms,
status.sstat_outstrms);
/* We have a connection. Print a message if we're running in
verbose mode. */
if (verbose) {
socklen_t address_length;
/* Get the remote address of the connection. */
address_length = sizeof (socket_address);
rval = getpeername (connection, (struct sockaddr *)&socket_address, &address_length);
assert (rval == 0);
/* Print a message. */
printf ("connection accepted from %s\n",
inet_ntoa (socket_address.sin_addr));
}
/* Fork a child process to handle the connection. */
child_pid = fork ();
if (child_pid == 0) {
/* This is the child process. It shouldn't use stdin or stdout,
so close them. */
close (STDIN_FILENO);
/* Also this child process shouldn't do anything with the
listening socket. */
close (server_socket);
bzero(&evnts, sizeof(evnts));
evnts.sctp_data_io_event = 1;
if (setsockopt(connection,IPPROTO_SCTP, SCTP_EVENTS,
&evnts, sizeof(evnts)) ) {
perror("set sock opt\n");
}
/* Handle a request from the connection. We have our own copy
of the connected socket descriptor. */
handle_connection (connection,(struct sockaddr *) &remote_address);
/* All done; close the connection socket, and end the child
process. */
close (connection);
exit (0);
}
else if (child_pid > 0) {
/* This is the parent process. The child process handles the
connection, so we don't need our copy of the connected socket
descriptor. Close it. Then continue with the loop and
accept another connection. */
close (connection);
}
else
/* Call to fork failed. */
system_error ("fork");
}
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <dirent.h>
#include <netinet/sctp.h>
#include "rrd.c"
#define SERV_PORT 2016
#define SERV_MAX_SCTP_STRM 100
#define MAXLINE 800
#define FILE_NAME_MAX 255
#define landing_path "/home/dr/files_received/"
// http request
static char* http_request = "GET /files HTTP/1.0\r\n\r\n" ;
// dummy user_agent
static char* user_agent =
"User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.7) Gecko/2009032803 Iceweasel/3.0.6 (Debian-3.0.6-1)";
//int serv_port ;
struct addrinfo *
host_serv(const char *host, const char *serv, int family, int socktype)
{
int n;
struct addrinfo hints, *res;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_ALL; /* always return canonical name */
hints.ai_family = family; /* 0, AF_INET, AF_INET6, etc. */
hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */
if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) {
printf("host_serv error for %s, %s: %s",
(host == NULL) ? "(no hostname)" : host,
(serv == NULL) ? "(no service name)" : serv,
gai_strerror(n));
exit(1);
}
return(res); /* return pointer to first on linked list */
}
void printAddressArray(struct sockaddr* addressArray, int len) {
int i, error;
struct sockaddr *address;
char host[100];
char port[10];
for(i = 0;i < len; i++) {
printf("%d/%d: ",i + 1, len);
memset(host, 0, sizeof(host));
memset(port, 0, sizeof(port));
address = &addressArray[i];
error = getnameinfo(address, sizeof(address),
host, sizeof(host),
port, sizeof(port),
NI_NUMERICHOST);
if (error != 0) {
fprintf(stderr, "getnameinfo error\n");
printf("(unknown address)\n");
} else {
printf("%s:%s\n", host, port);
}
}
}
int listip(int sock_fd) {
struct sockaddr* addressArray;
int count;
count = sctp_getladdrs(sock_fd, 0, &addressArray);
if(count > 0) {
printf("SCTP association local addresses:\n");
printAddressArray(addressArray, count);
sctp_freepaddrs(addressArray); // free allocated memory
}
count = sctp_getpaddrs(sock_fd, 0, &addressArray);
if(count > 0) {
printf("SCTP association remote addresses:\n");
printAddressArray(addressArray, count);
sctp_freepaddrs(addressArray); // free allocated memory
}
return 0;
}
int
sctp_connect_arg_list(int sock_fd, char **adr_list, int adr_count)
{
struct addrinfo *addr;
char *bindbuf, *p, portbuf[10];
int addrcnt=0;
int i,ret;
/* Allocate space for sctp_connectx arguments */
bindbuf = (char *) calloc(adr_count, sizeof(struct sockaddr_storage));
p = bindbuf;
/* Process arguments */
sprintf(portbuf, "%d", SERV_PORT);
for( i=3; i<adr_count; i++ ) {
addr = host_serv(adr_list[i], portbuf, AF_UNSPEC, SOCK_SEQPACKET);
memcpy(p, addr->ai_addr, addr->ai_addrlen);
freeaddrinfo(addr);
addrcnt++;
p += addr->ai_addrlen;
}
/* Call binding function */
ret = sctp_connectx(sock_fd,(struct sockaddr *)bindbuf,addrcnt,NULL);
if (ret < 0 ) {
perror("sctp_connectx");
}
/* Return success */
free(bindbuf);
return(0);
}
int main(int argc, char *argv[]) {
int sockfd;
int len , tlen , ret;
struct sockaddr_in serv_addr;
int port = SERV_PORT;
struct sctp_initmsg initmsg;
struct sctp_status status;
struct sockaddr_in peeraddr;
struct sctp_sndrcvinfo sri;
struct sctp_event_subscribe evnts;
struct timeval tv1 , tv2 ;
char *req , *ptr , *temp_ptr ;
char method[]="GET ";
char path[]="/files ";
char protocol[]="HTTP/1.0" , *proto;
char sendline[MAXLINE], recvline[MAXLINE];
char file_name[FILE_NAME_MAX] , file_size_buf[16];
int out_sz,rd_sz , n , written , useconds ;
int msg_flags;
FILE *destination_file ;
DIR *run_dir ;
int file_size=0 , count=0 , streams;
char *buffer , *sbuf;
socklen_t slen , tolen ;
struct dirent *dirp ;
if (argc < 2 ) {
printf("arguments: <tcp/sctp> <nr_streams> <ip list>\n");
exit(1);
}
if (argv[2]!=NULL){
streams = atoi(argv[2]);
}
if (argv[1]!=NULL){
proto = strdup(argv[1]);
printf("protocol: %s\n",proto);
}
/*
if (argv[3]!=NULL){
serv_port = atoi(argv[3]);
}
*/
/* create endpoint */
if (!strcmp(proto,"sctp")){
sockfd = socket(AF_INET, SOCK_STREAM,
IPPROTO_SCTP
);
} else {
sockfd = socket(AF_INET, SOCK_STREAM,
IPPROTO_TCP
);
}
if (sockfd < 0) {
perror("socket creation");
exit(2);
}
/* connect to server */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[3]);
serv_addr.sin_port = htons(port);
// serv_addr.sin_port = htonl(serv_port);
memset(&initmsg, 0, sizeof(initmsg));
initmsg.sinit_max_instreams = streams;
initmsg.sinit_num_ostreams = streams;
printf("Asking for: input streams: %d, output streams: %d\n",
initmsg.sinit_max_instreams,
initmsg.sinit_num_ostreams);
if (!strcmp(proto,"sctp")){
if (setsockopt(sockfd, IPPROTO_SCTP,
SCTP_INITMSG, &initmsg, sizeof(initmsg))) {
perror("set sock opt\n");
}
if (sctp_connect_arg_list(sockfd, argv , argc-3 ) < 0) {
perror("connectx");
exit(3);
}
} else {
connect(sockfd,(struct sockaddr *) &serv_addr , sizeof(serv_addr) );
}
run_dir = opendir(".");
dirp = readdir (run_dir);
len = sizeof(status);
memset(&status, 0, len);
if (!strcmp(proto,"sctp" )){
printf("getsockopt\n");
if (getsockopt(sockfd, IPPROTO_SCTP,
SCTP_STATUS, &status, &len) == -1) {
perror("get sock opt");
}
printf("Got: input streams: %d, output streams: %d\n",
status.sstat_instrms,
status.sstat_outstrms);
bzero(&evnts, sizeof(evnts));
evnts.sctp_data_io_event = 1;
if (setsockopt(sockfd,IPPROTO_SCTP, SCTP_EVENTS,
&evnts, sizeof(evnts)) ) {
perror("set sock opt\n");
}
} else {
}
// listip(sockfd);
req=malloc(128);
memset(req,0,128);
ptr = req ;
memcpy(req,method,strlen(method));
req = req + strlen(method) ;
memcpy(req,path,strlen(path));
req= req+strlen(path) ;
memcpy(req,protocol,strlen(protocol));
req= req+strlen(protocol) ;
memcpy(req,"\r\n\r\n",4);
tolen = sizeof(serv_addr);
if (!strcmp(proto,"sctp")) {
printf("sending request\n");
sri.sinfo_stream = 1 ;
out_sz = strlen(http_request);
if (sctp_sendmsg(sockfd, http_request , out_sz, (struct sockaddr *) &serv_addr, tolen,
0, 0,
sri.sinfo_stream,
0, 0) < 0 ) {
perror("sctp_sendmsg");
exit(1);
}
out_sz = strlen(user_agent);
if (sctp_sendmsg(sockfd, user_agent , out_sz, (struct sockaddr *) &serv_addr, tolen,
0, 0,
sri.sinfo_stream,
0, 0) < 0 ) {
perror("sctp_sendmsg");
exit(1);
}
} else {
// write
}
printf("starting file transfer\n");
fflush(stdout);
gettimeofday(&tv1,NULL);
/*
if (!strcmp(proto,"sctp")) {
while(1){
len = sizeof(serv_addr);
rd_sz = sctp_recvmsg(sockfd, recvline, sizeof(recvline),
(struct sockaddr *)&serv_addr, &len,
&sri,&msg_flags);
if (rd_sz < 0 ) {
perror("-") ;
}else {
break ;
}
}
} else {
// read
}
*/
while(1) {
memset(recvline,0,MAXLINE);
memset(file_name,0,FILE_NAME_MAX);
if (!strcmp(proto,"sctp")) {
len = sizeof(serv_addr);
rd_sz = sctp_recvmsg(sockfd, recvline, sizeof(recvline),
(struct sockaddr *)&serv_addr, &len,
&sri,&msg_flags);
if (rd_sz < 1 ) {
break ;
}
} else {
printf("tcp here");
// read
}
printf("receiving file : %s\n",recvline);
fflush(stdout);
if ((recvline[0] == 'f' ) && (recvline[1] == 't' ) ) {
// printf("file transfer header: %s\n",recvline);
ptr = strchr(recvline+2,'|');
len = ptr - recvline - 2 ;
temp_ptr = strchr(ptr+1,'|');
len = temp_ptr - ptr ;
memcpy(file_name,ptr+1,len-1);
temp_ptr = strchr(ptr+3,'|');
len = strlen(recvline);
tlen = temp_ptr - ptr;
memcpy(file_size_buf,temp_ptr+1,len-tlen);
printf("file_name: %s\n",file_name);
printf("file_size: %s\n",file_size_buf);
file_size= atoi(file_size_buf);
}
fflush(stdout);
chdir(landing_path);
if (file_name!=NULL) {
destination_file=fopen(file_name,"w");
} else {
printf("filename ?");
}
if (!strcmp(proto,"sctp")) {
while (
(rd_sz = sctp_recvmsg(sockfd, recvline, sizeof(recvline),
(struct sockaddr *)&serv_addr, &len,
&sri,&msg_flags)) > 0 ) {
if (strstr( recvline , "-EOF-" )){
break ;
}
count = count + rd_sz ;
if ( written = fwrite(recvline , rd_sz , 1 , destination_file) < 0 ) {
perror("fwrite error");
}
// printf("written %d bytes\n",rd_sz);
}
} else {
printf("tcp here\n");
// read
}
if (destination_file !=NULL) {
fclose(destination_file);
} else {
printf("invalid destination file ");
exit(1);
}
memset(file_name,0,FILE_NAME_MAX);
memset(file_size_buf,0,16);
} //while
gettimeofday(&tv2,NULL);
close(sockfd);
useconds = tv2.tv_sec - tv1.tv_sec ;
useconds *= 1000000;
useconds += tv2.tv_usec - tv1.tv_usec ;
// printf("received %4.2lf bytes in %2.d seconds\n",(float)count/1024, seconds );
printf("received %d bytes in %d microseconds\n",count, useconds );
sbuf = malloc(128);
memset(sbuf,0,128);
chdir(getenv("PWD"));
if (count!=0) {
if ((argc < 5) && (streams==2) ) {
sprintf(sbuf,sctp_test_1_int_1_stream_rrd,count,useconds);
printf("%s\n",sbuf);
ret = system(sbuf);
if (ret < 0 ) {
perror("system");
}
exit(0);
} else if ((streams == 2 )) {
sprintf(sbuf,sctp_test_4_int_1_stream_rrd,count,useconds);
printf("%s\n",sbuf);
ret = system(sbuf);
if (ret < 0 ) {
perror("system");
}
exit(0);
}else if ((argc < 3) && (streams==50)) {
sprintf(sbuf,sctp_test_1_int_50_stream_rrd,count,useconds);
printf("%s\n",sbuf);
ret = system(sbuf);
if (ret < 0 ) {
perror("system");
}
exit(0);
} else if ((streams==50)) {
sprintf(sbuf,sctp_test_4_int_50_stream_rrd,count,useconds);
printf("%s\n",sbuf);
ret = system(sbuf);
if (ret < 0 ) {
perror("system");
}
exit(0);
}
}
fflush(stdout);
exit(0);
}
#define path_to_rrd "/home/dr/sctp_proj/rrd"
#define sctp_test_4_int_1_stream_rrd \
"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_4_int_1_stream.rrd \
N:%d:%d"
#define sctp_test_1_int_1_stream_rrd \
"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_1_int_1_stream.rrd \
N:%d:%d"
#define sctp_test_4_int_50_stream_rrd \
"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_4_int_50_stream.rrd \
N:%d:%d"
#define sctp_test_1_int_50_stream_rrd \
"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_1_int_50_stream.rrd \
N:%d:%d"
#define sctp_test_tcp_rrd \
"/usr/bin/rrdtool update " path_to_rrd "/sctp_test_tcp.rrd \
N:%d:%d"
#!/bin/bash
#
rrdtool create sctp_test_4_int_1_stream.rrd --step 60 \
DS:count_4_int:GAUGE:1800:0:U \
DS:time_4_int:GAUGE:1800:0:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:10:1008 \
RRA:AVERAGE:0.5:48:2520 \
RRA:MAX:0.5:1:1440 \
RRA:MAX:0.5:10:1008 \
RRA:MAX:0.5:48:2520
rrdtool create sctp_test_1_int_1_stream.rrd --step 60 \
DS:count_1_int:GAUGE:1800:0:U \
DS:time_1_int:GAUGE:1800:0:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:10:1008 \
RRA:AVERAGE:0.5:48:2520 \
RRA:MAX:0.5:1:1440 \
RRA:MAX:0.5:10:1008 \
RRA:MAX:0.5:48:2520
rrdtool create sctp_test_4_int_50_stream.rrd --step 60 \
DS:count_4_int:GAUGE:1800:0:U \
DS:time_4_int:GAUGE:1800:0:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:10:1008 \
RRA:AVERAGE:0.5:48:2520 \
RRA:MAX:0.5:1:1440 \
RRA:MAX:0.5:10:1008 \
RRA:MAX:0.5:48:2520
rrdtool create sctp_test_1_int_50_stream.rrd --step 60 \
DS:count_1_int:GAUGE:1800:0:U \
DS:time_1_int:GAUGE:1800:0:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:10:1008 \
RRA:AVERAGE:0.5:48:2520 \
RRA:MAX:0.5:1:1440 \
RRA:MAX:0.5:10:1008 \
RRA:MAX:0.5:48:2520
rrdtool create tcp_test.rrd --step 60 \
DS:count_tcp:GAUGE:1800:0:U \
DS:time_tcp:GAUGE:1800:0:U \
RRA:AVERAGE:0.5:1:1440 \
RRA:AVERAGE:0.5:10:1008 \
RRA:AVERAGE:0.5:48:2520 \
RRA:MAX:0.5:1:1440 \
RRA:MAX:0.5:10:1008 \
RRA:MAX:0.5:48:2520
gcc -ggdb -o sctp_client02 sctp_client02.c -lsctp
# C source files for the server.
SOURCES = server.c module.c common.c main.c
# Object files corresponding to source files.
OBJECTS = $(SOURCES:.c=.o)
# Server module shared library files.
MODULES = files.so
CLIENT = sctp_client.c
### Rules. ############################################################
.PHONY: all clean
# Default target: build everything.
all: server sctp_client $(MODULES)
# Clean up build products.
clean:
rm -f $(OBJECTS) $(MODULES) server
# The main server program. Link with -Wl,-export-dyanamic so
# dynamically loaded modules can bind symbols in the program. Link in
# libdl, which contains calls for dynamic loading.
server: $(OBJECTS)
$(CC) $(CFLAGS) -ggdb -Wl,-export-dynamic -o $@ $^ -ldl -lsctp
client: $(OBJECTS)
$(CC) $(CFLAGS) -ggdb -Wl,-export-dynamic -o $@ $^ -lsctp
# All object files in the server depend on server.h. But use the
# default rule for building object files from source files.
$(OBJECTS): server.h
# Rule for building module shared libraries from the corresponding
# source files. Compile -fPIC and generate a shared object file.
$(MODULES): \
%.so: %.c server.h
$(CC) $(CFLAGS) -fPIC -shared -o $@ $<
#!/bin/bash
my_pwd="/home/dr/sctp_proj/4clienti/"
#$my_pwd/sctp_client_1_int_1_stream sctp 2 10.10.10.10
#$my_pwd/sctp_client_4_int_1_stream sctp 2 10.10.10.10 10.10.10.11 10.10.10.12 10.10.10.13
$my_pwd/sctp_client_1_int_50_stream sctp 50 10.10.10.10
$my_pwd/sctp_client_4_int_50_stream sctp 50 10.10.10.10 10.10.10.11 10.10.10.11 10.10.10.12 10.10.10.13
[1] Henrik ¨ Osterdahl – “A comparison of TCP and SCTP performance
using the HTTP protocol”
[2] Matthew Stafford – “Signaling and switching for packet telephony”
[3] SCTP Programmer's Guide: HP-UX 11i v2, HP-UX 11i v3
[4] http://www.linuxjournal.com/article/9749
[5] http://www.ietf.org/rfc/rfc2960.txt
[6] W. Richard Stevens – “UNIX Network Programming: Networking APIs: Sockets and XTI; Volume 1”
[7] Taarik Hassenmahomed – “Stream Control Transmission Protocol”
[8] Linux ManPages
[9] Brian W. Kernighan, Dennis M. Ritchie – “The C Programming Language”
[10] http://www.ietf.org/rfc/rfc0768.txt
[11] http://www.faqs.org/rfcs/rfc793.html