เมื่อเราพูดถึงระบบเครือข่าย เราจะไม่พูดถึงแบบจำลอง OSI ไม่ได้

ในแง่ของโมเดลนี้ วันนี้เราสนใจเลเยอร์การขนส่งมากที่สุด (4)

นี่คือระดับที่เราทำงานกับข้อมูลที่ย้าย "จากจุด A ไปยังจุด B" ภารกิจหลักของเลเยอร์การขนส่งคือเพื่อให้แน่ใจว่าข้อความถูกส่งไปยังปลายทางโดยรักษาลำดับที่ถูกต้องไว้ โปรโตคอลเลเยอร์การขนส่งที่ใช้บ่อยที่สุดคือ TCP และ UDP พวกเขาทำงานตามแนวคิดในรูปแบบต่างๆ กัน แต่แต่ละคนก็มีข้อดีในตัวเองที่ช่วยให้แก้ปัญหาเฉพาะได้

ขั้นแรก เรามาดูว่า TCP ทำงานอย่างไร

TCP (Transmission Control Protocol) เป็นโปรโตคอลเครือข่ายที่รับรองว่ามีการสร้างการเชื่อมต่อระหว่างโฮสต์ก่อนที่จะมีการแลกเปลี่ยนข้อมูล

นี่เป็นโปรโตคอลที่เชื่อถือได้มาก เพราะทุกครั้งที่ส่งแพ็กเก็ตข้อมูลอื่น จะต้องตรวจสอบว่าได้รับแพ็กเก็ตก่อนหน้าหรือไม่

แพ็กเก็ตที่ส่งจะถูกสั่งซื้อ และหากมีปัญหากับแพ็กเก็ตบางแพ็กเก็ต (เช่น ฝ่ายรับไม่ยืนยันว่าแพ็กเก็ตมาถึงแล้ว) แพ็กเก็ตนั้นจะถูกส่งอีกครั้ง เป็นผลให้อัตราการถ่ายโอนค่อนข้างต่ำ เนื่องจากต้องใช้เวลามากขึ้นในการตรวจสอบอย่างเข้มงวดและเพื่อให้แน่ใจว่ามีการสั่งซื้อที่ถูกต้อง

นี่คือที่มาของ "น้องชาย" ซึ่งเป็นโปรโตคอล UDP ซึ่งแตกต่างจาก TCP ตรง UDP ไม่สนใจเกี่ยวกับลำดับและสถานะของแต่ละแพ็กเก็ต เพียงแค่ส่งข้อมูลโดยไม่มีการยืนยันการจัดส่ง ยิ่งไปกว่านั้น มันไม่ได้สร้างการเชื่อมต่อและไม่ได้ขึ้นอยู่กับสถานะการเชื่อมต่อแต่อย่างใด

มีวัตถุประสงค์เพียงเพื่อส่งข้อมูลไปยังที่อยู่ และสิ่งนี้ทำให้โปรโตคอลมีข้อเสียเปรียบหลัก คือ ความน่าเชื่อถือต่ำ เนื่องจากอาจทำให้ข้อมูลบางส่วนสูญหายได้ นอกจากนี้ ผู้รับจะต้องเตรียมพร้อมสำหรับข้อเท็จจริงที่ว่าข้อมูลอาจมาถึงไม่เป็นระเบียบ ที่กล่าวว่าโปรโตคอลยังมีข้อได้เปรียบคืออัตราการถ่ายโอนที่สูงกว่าเนื่องจากโปรโตคอลนั้นจำกัดเฉพาะการส่งข้อมูลเท่านั้น

นอกจากนี้ยังมีความแตกต่างในวิธีการส่งข้อมูล ใน TCP ข้อมูลจะถูกสตรีม ซึ่งหมายความว่าข้อมูลไม่มีขอบเขต ใน UDP ข้อมูลจะถูกส่งเป็นดาตาแกรมและมีขอบเขต และผู้รับจะตรวจสอบความสมบูรณ์ของข้อมูล แต่เฉพาะเมื่อได้รับข้อความสำเร็จเท่านั้น

สรุป:

TCP เป็นโปรโตคอลที่เชื่อถือได้และแม่นยำซึ่งป้องกันข้อมูลสูญหาย ข้อความจะถูกส่งด้วยความแม่นยำสูงสุดเสมอ หรือไม่ได้ส่งเลย ผู้รับไม่ต้องการตรรกะในการสั่งซื้อข้อมูล เนื่องจากข้อมูลขาเข้าจะถูกสั่งซื้อแล้ว UDP ไม่น่าเชื่อถือ แต่เป็นโปรโตคอลการถ่ายโอนข้อมูลที่เร็วกว่า ฝ่ายส่งและฝ่ายรับต้องการตรรกะเพิ่มเติมเพื่อทำงานกับโปรโตคอลนี้ แต่ลองมาดูวิธีการทำงานโดยใช้ตัวอย่างเกมคอมพิวเตอร์หรือเกมมือถือที่เล่นผ่านเครือข่าย เราอาจไม่สนใจสิ่งที่ควรมาถึงเมื่อ 5 วินาทีที่แล้วอีกต่อไป และเราสามารถข้ามแพ็กเก็ตสองสามแพ็กเก็ตหากมาไม่ทัน — เกมอาจแลค แต่คุณยังสามารถเล่นได้!

ใน Java ในการทำงานกับดาต้าแกรมที่ส่งผ่าน UDP เราใช้อ็อบเจกต์ของคลาสDatagramSocketและDatagramPacket

ในการแลกเปลี่ยนข้อมูล ผู้ส่งและผู้รับสร้างซ็อกเก็ตดาตาแกรม นั่นคืออินสแตนซ์ของคลาสDatagramSocket คลาสมีตัวสร้างหลายตัว ความแตกต่างระหว่างพวกเขาคือตำแหน่งที่ซ็อกเก็ตที่สร้างขึ้นจะเชื่อมต่อ:

ดาตาแกรมซ็อกเก็ต () เชื่อมต่อกับพอร์ตที่มีอยู่บนเครื่องโลคัล
DatagramSocket (พอร์ต int) เชื่อมต่อกับพอร์ตที่ระบุบนเครื่องภายใน
DatagramSocket (พอร์ต int, InetAddress addr) เชื่อมต่อกับพอร์ตที่ระบุที่อยู่บนเครื่องท้องถิ่น (addr)

คลาสนี้มีวิธีการมากมายในการเข้าถึงและจัดการพารามิเตอร์ซ็อกเก็ต (เราจะดูในภายหลัง) รวมถึงวิธีการรับและส่งดาตาแกรม:

ส่ง (แพ็ค DatagramPacket) ส่งดาตาแกรมที่บรรจุลงในแพ็กเก็ต
รับ (แพ็ค DatagramPacket) รับดาตาแกรมที่บรรจุลงในแพ็กเก็ต

DatagramPacketเป็นคลาสที่แสดงถึงแพ็คเกจดาตาแกรม แพ็กเก็ต Datagram ใช้เพื่อใช้บริการส่งแพ็กเก็ตแบบไร้การเชื่อมต่อ แต่ละข้อความจะถูกส่งจากเครื่องหนึ่งไปยังอีกเครื่องหนึ่งโดยอิงจากข้อมูลที่มีอยู่ในแพ็คเก็ตนั้นเท่านั้น หลายแพ็กเก็ตที่ส่งจากเครื่องหนึ่งไปยังอีกเครื่องหนึ่งอาจถูกกำหนดเส้นทางแตกต่างกันและอาจมาถึงในลำดับใดก็ได้ ไม่รับประกันการจัดส่งแพ็คเก็ต

คอนสตรัคเตอร์:

DatagramPacket (ไบต์ [] buf ความยาว int) สร้างDatagramPacketเพื่อยอมรับแพ็กเก็ตที่มีความยาวlength
DatagramPacket (byte[] buf, ความยาว int, ที่อยู่ InetAddress, พอร์ต int) สร้างแพ็กเก็ตดาตาแกรมเพื่อส่งแพ็กเก็ตความยาวไปยังหมายเลขพอร์ตที่ระบุบนโฮสต์ที่ระบุ
DatagramPacket (byte[] buf, int offset, ความยาว int) สร้างDatagramPacketเพื่อยอมรับแพ็กเก็ตที่มีความยาวlengthโดยระบุออฟเซ็ตในบัฟเฟอร์
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) สร้างแพ็กเก็ตดาตาแกรมเพื่อส่งแพ็กเก็ตความยาวพร้อมออฟเซ็ตออฟเซ็ตไปยังหมายเลขพอร์ตที่ระบุบนโฮสต์ที่ระบุ
DatagramPacket (byte[] buf, int offset, ความยาว int, ที่อยู่ SocketAddress) สร้างแพ็กเก็ตดาตาแกรมเพื่อส่งแพ็กเก็ตความยาวพร้อมออฟเซ็ตออฟเซ็ตไปยังหมายเลขพอร์ตที่ระบุบนโฮสต์ที่ระบุ
DatagramPacket (byte[] buf ความยาว int ที่อยู่ SocketAddress) สร้างแพ็กเก็ตดาตาแกรมเพื่อส่งแพ็กเก็ตความยาวไปยังหมายเลขพอร์ตที่ระบุบนโฮสต์ที่ระบุ

เราจำได้ว่าแนวทาง UDP ไม่ได้สร้างการเชื่อมต่อ แพ็กเก็ตจะถูกส่งออกไปด้วยความหวังว่าผู้รับจะคาดหวัง แต่คุณสามารถสร้างการเชื่อมต่อโดยใช้เมธอดconnect(InetAddress addr, int port)ของคลาสDatagramSocket

การเชื่อมต่อทางเดียวถูกสร้างขึ้นกับโฮสต์ตามที่อยู่และพอร์ต: เพื่อส่งหรือรับดาตาแกรม การเชื่อมต่อสามารถยุติได้โดยใช้วิธีการตัดการเชื่อมต่อ ()

ลองเขียนโค้ดเซิร์ฟเวอร์ตามDatagramSocketเพื่อรับข้อมูล:

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

เราสร้าง วัตถุ DatagramSocketเพื่อฟังบนพอร์ต 1050 เมื่อได้รับข้อความ จะพิมพ์ไปยังคอนโซล เราจะส่งคำว่า "สวัสดี" ดังนั้นเราจึงจำกัดขนาดบัฟเฟอร์ไว้ที่ห้าไบต์

ตอนนี้เราจะสร้างคลาสผู้ส่ง:

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

}

ใน เมธอด sendMessageเราสร้างDatagramPacketและDatagramSocketและส่งข้อความของเรา โปรดทราบว่า เมธอด close()ใช้เพื่อปิดDatagramSocketหลังจากส่งข้อความ

ทุกวินาทีคอนโซลของผู้รับจะแสดงข้อความ "สวัสดี" ขาเข้าที่ส่งโดยผู้ส่ง ซึ่งหมายความว่าการสื่อสารของเราทำงานได้อย่างถูกต้อง