Când vorbim de networking, nu putem să nu menționăm modelul OSI.

În ceea ce privește acest model, astăzi suntem cel mai interesați de stratul de transport (4).

Acesta este nivelul la care lucrăm cu datele care se deplasează „din punctul A în punctul B”. Sarcina principală a stratului de transport este să se asigure că un mesaj este livrat la destinație, menținând în același timp secvența corectă. Cele mai comune două protocoale de nivel de transport sunt: ​​TCP și UDP. Aceștia lucrează conceptual în moduri diferite, dar fiecare are propriile sale avantaje care le permit să rezolve probleme specifice.

Mai întâi, să vedem cum funcționează TCP.

TCP (Transmission Control Protocol) este un protocol de rețea care asigură stabilirea unei conexiuni între gazde înainte ca datele să fie schimbate.

Acesta este un protocol foarte fiabil, deoarece de fiecare dată când trimite un alt pachet de date, trebuie să verifice dacă pachetul anterior a fost primit.

Pachetele transmise sunt ordonate, iar dacă există probleme cu un anumit pachet (adică partea care primește nu confirmă că pachetul a sosit), atunci pachetul este trimis din nou. Ca urmare, rata de transfer este relativ scăzută, deoarece este nevoie de mai mult timp pentru monitorizarea strictă și pentru asigurarea unei comenzi corecte.

Aici intervine „fratele” său, protocolul UDP. Spre deosebire de TCP, UDP nu prea îi pasă de ordinea și starea fiecărui pachet. Pur și simplu trimite date fără confirmare de livrare. În plus, nu stabilește o conexiune și nu depinde în niciun fel de starea unei conexiuni.

Scopul său este pur și simplu de a trimite date la o adresă. Și acest lucru dă naștere la principalul dezavantaj al protocolului, fiabilitatea scăzută, deoarece poate pierde pur și simplu bucăți de date. În plus, destinatarul trebuie să fie pregătit pentru faptul că datele pot ajunge nefuncționale. Acestea fiind spuse, protocolul are și un avantaj, o rată de transfer mai mare, datorită faptului că protocolul se limitează la trimiterea de date.

Există, de asemenea, diferențe în modul în care datele în sine sunt transmise. În TCP, datele sunt transmise în flux, ceea ce înseamnă că datele nu au granițe. În UDP, datele sunt transmise ca datagrame și au limite, iar destinatarul verifică integritatea datelor, dar numai dacă mesajul este primit cu succes.

Să rezumăm:

TCP este un protocol fiabil și precis care previne pierderea datelor. Un mesaj va fi întotdeauna livrat cu acuratețe maximă sau nu va fi livrat deloc. Destinatarul nu are nevoie de logică pentru comandarea datelor, deoarece datele primite vor fi deja comandate. UDP nu este la fel de fiabil, dar este un protocol de transfer de date mai rapid. Părțile de expediere și de primire au nevoie de o logică suplimentară pentru a lucra cu acest protocol. Dar să aruncăm o privire la modul în care funcționează folosind exemplul unui joc pe computer sau al unui joc mobil jucat în rețea. S-ar putea să nu ne mai pese de ceea ce ar fi trebuit să sosească acum 5 secunde și putem sări peste câteva pachete dacă nu ajung la timp — jocul poate rămâne în întârziere, dar încă poți să joci!

În Java, pentru a lucra cu datagrame transmise prin UDP, folosim obiecte din clasele DatagramSocket și DatagramPacket .

Pentru a face schimb de date, expeditorul și receptorul creează socket-uri de datagramă, adică instanțe ale clasei DatagramSocket . Clasa are mai mulți constructori. Diferența dintre ele este locul unde se va conecta priza creată:

DatagramSocket () Se conectează la orice port disponibil de pe computerul local
DatagramSocket (port int) Se conectează la portul specificat de pe computerul local
DatagramSocket (port int, adresa InetAddress) Se conectează la portul specificat la o adresă de pe mașina locală (adr)

Clasa conține multe metode de accesare și gestionare a parametrilor socketului (le vom analiza puțin mai târziu), precum și metode de primire și trimitere a datagramelor:

trimite (pachet DatagramPacket) Trimite datagrame împachetate în pachete
primiți (pachet DatagramPacket) Primește datagrame împachetate în pachete

DatagramPacket este o clasă care reprezintă un pachet de datagrame. Pachetele de datagrame sunt folosite pentru a implementa un serviciu de livrare de pachete fără conexiune. Fiecare mesaj este rutat de la o mașină la alta pe baza exclusiv informațiilor conținute în acel pachet. Pachetele multiple trimise de la o mașină la alta pot fi direcționate diferit și pot ajunge în orice ordine. Livrarea pachetelor nu este garantată.

Constructori:

DatagramPacket(byte[] buf, lungime int) Creează un DatagramPacket pentru a accepta pachete de lungime .
DatagramPacket(byte[] buf, lungime int, adresa InetAddress, port int) Creează un pachet de datagramă pentru a trimite pachete de lungime la numărul de port specificat pe gazda specificată.
DatagramPacket(byte[] buf, int offset, int lungime) Creează un DatagramPacket pentru a accepta pachete de lungime , specificând un offset în buffer.
DatagramPacket(byte[] buf, int offset, int lungime, adresa InetAddress, int port) Creează un pachet de datagramă pentru a trimite pachete de lungime cu decalaj la numărul de port specificat pe gazda specificată.
DatagramPacket(byte[] buf, int offset, int lungime, adresa SocketAddress) Creează un pachet de datagramă pentru a trimite pachete de lungime cu decalaj la numărul de port specificat pe gazda specificată.
DatagramPacket(byte[] buf, lungime int, adresa SocketAddress) Creează un pachet de datagramă pentru a trimite pachete de lungime la numărul de port specificat pe gazda specificată.

Amintim că abordarea UDP nu stabilește o conexiune. Pachetele sunt trimise cu speranța că destinatarul le așteaptă. Dar puteți stabili o conexiune folosind metoda connect(InetAddress addr, int port) a clasei DatagramSocket .

Se stabilește o conexiune unidirecțională cu gazda pe baza unei adrese și a unui port: fie pentru a trimite, fie pentru a primi datagrame. Conexiunea poate fi încheiată folosind metoda disconnect() .

Să încercăm să scriem cod de server pe baza DatagramSocket pentru a primi date:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

class Recipient {

   public static void main(String[] args) {
       try {
           DatagramSocket ds = new DatagramSocket(1050);

           while (true) {
               DatagramPacket pack = new DatagramPacket(new byte[5], 5);
               ds.receive(pack);
               System.out.println(new String(pack.getData()));
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}

Creăm un obiect DatagramSocket pentru a asculta pe portul 1050. Când primește un mesaj, îl imprimă pe consolă. Vom transmite cuvântul „Bună ziua”, așa că limităm dimensiunea tamponului la cinci octeți.

Acum vom crea clasa expeditorului:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

class Sender {
   private String host;
   private int port;

   Sender(String host, int port) {
       this.host = host;
       this.port = port;
   }

   private void sendMessage(String mes) {
       try {
           byte[] data = mes.getBytes();
           InetAddress address = InetAddress.getByName(host);
           DatagramPacket pack = new DatagramPacket(data, data.length, address, port);
           DatagramSocket ds = new DatagramSocket();
           ds.send(pack);
           ds.close();
       } catch (IOException e) {
           System.err.println(e);
       }
   }

   public static void main(String[] args) {
   Sender sender = new Sender("localhost", 1050);
   String message = "Hello";

   Timer timer = new Timer();
   timer.scheduleAtFixedRate(new TimerTask() {
       @Override
       public void run() {
           sender.sendMessage(message);
       }
   }, 1000, 1000);
}

}

În metoda sendMessage , creăm un DatagramPacket și DatagramSocket și trimitem mesajul nostru. Rețineți că metoda close() este utilizată pentru a închide DatagramSocket după ce mesajul este trimis.

În fiecare secundă, consola destinatarului afișează mesajul „Salut” trimis de expeditor. Aceasta înseamnă că toate comunicările noastre funcționează corect.