När vi talar om nätverk kan vi inte undgå att nämna OSI-modellen.

När det gäller denna modell är vi idag mest intresserade av transportskiktet (4).

Det är den nivån på vilken vi arbetar med data som rör sig "från punkt A till punkt B". Huvuduppgiften för transportlagret är att säkerställa att ett meddelande levereras till destinationen, samtidigt som rätt sekvens bibehålls. De två vanligaste transportlagerprotokollen är: TCP och UDP. De fungerar konceptuellt på olika sätt, men var och en har sina egna fördelar som gör att de kan lösa specifika problem.

Låt oss först titta på hur TCP fungerar.

TCP (Transmission Control Protocol) är ett nätverksprotokoll som säkerställer att en anslutning mellan värdar upprättas innan data utbyts.

Detta är ett mycket tillförlitligt protokoll, för varje gång det skickar ett annat datapaket måste det kontrollera att det föregående paketet togs emot.

De överförda paketen beställs, och om det är problem med ett visst paket (dvs. den mottagande parten bekräftar inte att paketet har kommit) så skickas paketet igen. Som ett resultat är överföringshastigheten relativt låg, eftersom det krävs mer tid för den strikta övervakningen och för att säkerställa korrekt beställning.

Det är här dess "bror", UDP-protokollet, kommer in. Till skillnad från TCP bryr sig UDP inte om ordningen och statusen för varje paket. Den skickar helt enkelt data utan leveransbekräftelse. Dessutom upprättar den ingen anslutning och är inte beroende av en anslutningsstatus på något sätt.

Dess syfte är helt enkelt att skicka data till en adress. Och detta ger upphov till protokollets största nackdel, låg tillförlitlighet, eftersom det helt enkelt kan förlora databitar. Dessutom måste mottagaren vara beredd på att uppgifterna kan komma ur funktion. Som sagt har protokollet också en fördel, en högre överföringshastighet, på grund av att protokollet är begränsat till att skicka data.

Det finns också skillnader i hur själva data överförs. I TCP strömmas data, vilket innebär att data inte har några gränser. I UDP överförs data som datagram och har gränser, och mottagaren kontrollerar dataintegriteten, men bara om meddelandet tas emot.

Låt oss sammanfatta:

TCP är ett pålitligt och korrekt protokoll som förhindrar dataförlust. Ett meddelande kommer alltid att levereras med maximal noggrannhet, eller levereras inte alls. Mottagaren behöver ingen logik för att beställa data, eftersom inkommande data redan kommer att beställas. UDP är inte lika tillförlitligt, men det är ett snabbare dataöverföringsprotokoll. De sändande och mottagande parterna behöver lite extra logik för att kunna arbeta med detta protokoll. Men låt oss ta en titt på hur det fungerar med exemplet med ett datorspel eller mobilspel som spelas över nätverket. Vi kanske inte längre bryr oss om vad som skulle ha kommit för 5 sekunder sedan, och vi kan hoppa över ett par paket om de inte kommer fram i tid — spelet kan släpa, men du kan fortfarande spela!

I Java använder vi objekt av klasserna DatagramSocket och DatagramPacket för att arbeta med datagram som överförs över UDP.

För att utbyta data skapar sändaren och mottagaren datagramsockets, dvs instanser av klassen DatagramSocket . Klassen har flera konstruktörer. Skillnaden mellan dem är var den skapade uttaget kommer att ansluta:

DatagramSocket () Ansluts till valfri tillgänglig port på den lokala maskinen
DatagramSocket (int port) Ansluts till den angivna porten på den lokala datorn
DatagramSocket(int-port, InetAddress-adress) Ansluter till den angivna porten på en adress på den lokala datorn (addr)

Klassen innehåller många metoder för att komma åt och hantera sockets parametrar (vi kommer att titta på dem lite senare), samt metoder för att ta emot och skicka datagram:

skicka (DatagramPacket pack) Skickar datagram packade i paket
ta emot (DatagramPacket-paket) Tar emot datagram packade i paket

DatagramPacket är en klass som representerar ett datagrampaket. Datagrampaket används för att implementera en anslutningslös paketleveranstjänst. Varje meddelande dirigeras från en maskin till en annan baserat enbart på informationen i det paketet. Flera paket som skickas från en maskin till en annan kan dirigeras på olika sätt och kan komma fram i valfri ordning. Leverans av paket är inte garanterad.

Konstruktörer:

DatagramPacket(byte[] buf, int längd) Skapar ett DatagramPacket för att acceptera paket med längdlängd .
DatagramPacket(byte[] buf, int längd, InetAddress adress, int port) Skapar ett datagrampaket för att skicka paket med längdlängd till det angivna portnumret på den angivna värden.
DatagramPacket(byte[] buf, int offset, int length) Skapar ett DatagramPacket för att acceptera paket med längdlängd , och specificerar en offset i bufferten.
DatagramPacket(byte[] buf, int offset, int length, InetAddress adress, int port) Skapar ett datagrampaket för att skicka paket med längdlängd med offsetoffset till det angivna portnumret på den angivna värden.
DatagramPacket(byte[] buf, int offset, int length, SocketAddress-adress) Skapar ett datagrampaket för att skicka paket med längdlängd med offsetoffset till det angivna portnumret på den angivna värden.
DatagramPacket(byte[] buf, int längd, SocketAddress-adress) Skapar ett datagrampaket för att skicka paket med längdlängd till det angivna portnumret på den angivna värden.

Vi minns att UDP-metoden inte upprättar en koppling. Paketen skickas iväg i hopp om att mottagaren väntar dem. Men du kan upprätta en anslutning med hjälp av connect(InetAddress adr, int port) -metoden för DatagramSocket -klassen.

En enkelriktad anslutning upprättas med värden baserat på en adress och port: antingen för att skicka eller ta emot datagram. Anslutningen kan avslutas med metoden disconnect() .

Låt oss försöka skriva serverkod baserat på DatagramSocket för att ta emot data:


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();
       }
   }
}

Vi skapar ett DatagramSocket- objekt för att lyssna på port 1050. När det tar emot ett meddelande skriver det ut det till konsolen. Vi kommer att sända ordet "Hej", så vi begränsar buffertstorleken till fem byte.

Nu skapar vi avsändarklassen:


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);
}

}

I sendMessage- metoden skapar vi ett DatagramPacket och DatagramSocket och skickar vårt meddelande. Observera att metoden close() används för att stänga DatagramSocket efter att meddelandet har skickats.

Varje sekund visar mottagarens konsol det inkommande "Hej"-meddelandet som skickats av avsändaren. Det betyder att all vår kommunikation fungerar korrekt.