Khi nói đến mạng, chúng ta không thể không nhắc đến mô hình OSI.

Về mô hình này, ngày nay chúng ta quan tâm nhất là lớp vận chuyển (4).

Đây là cấp độ mà chúng tôi làm việc với dữ liệu di chuyển "từ điểm A đến điểm B". Nhiệm vụ chính của lớp vận chuyển là đảm bảo rằng một thông báo được gửi đến đích, đồng thời duy trì trình tự chính xác. Hai giao thức tầng vận chuyển phổ biến nhất là: TCP và UDP. Về mặt khái niệm, chúng hoạt động theo những cách khác nhau, nhưng mỗi cách đều có những ưu điểm riêng cho phép chúng giải quyết các vấn đề cụ thể.

Đầu tiên, hãy xem cách thức hoạt động của TCP.

TCP (Giao thức điều khiển truyền dẫn) là một giao thức mạng đảm bảo rằng kết nối giữa các máy chủ được thiết lập trước khi dữ liệu được trao đổi.

Đây là một giao thức rất đáng tin cậy, bởi vì mỗi khi gửi một gói dữ liệu khác, nó phải kiểm tra xem gói trước đó đã được nhận hay chưa.

Các gói được truyền đi được sắp xếp theo thứ tự và nếu có vấn đề với một gói nhất định (tức là bên nhận không xác nhận rằng gói đã đến), thì gói đó sẽ được gửi lại. Do đó, tốc độ truyền tương đối thấp, vì cần nhiều thời gian hơn để giám sát chặt chẽ và đảm bảo đặt hàng chính xác.

Đây là lúc "người anh em" của nó, giao thức UDP, xuất hiện. Không giống như TCP, UDP không thực sự quan tâm đến thứ tự và trạng thái của mỗi gói. Nó chỉ đơn giản là gửi dữ liệu mà không cần xác nhận giao hàng. Hơn nữa, nó không thiết lập kết nối và không phụ thuộc vào trạng thái kết nối theo bất kỳ cách nào.

Mục đích của nó chỉ đơn giản là gửi dữ liệu đến một địa chỉ. Và điều này làm phát sinh nhược điểm chính của giao thức, độ tin cậy thấp, vì nó có thể đơn giản làm mất các mẩu dữ liệu. Ngoài ra, người nhận phải chuẩn bị cho thực tế là dữ liệu có thể đến không theo thứ tự. Điều đó nói rằng, giao thức cũng có một lợi thế, tốc độ truyền cao hơn, do thực tế là giao thức bị giới hạn trong việc gửi dữ liệu.

Cũng có sự khác biệt trong cách truyền dữ liệu. Trong TCP, dữ liệu được truyền trực tuyến, nghĩa là dữ liệu không có ranh giới. Trong UDP, dữ liệu được truyền dưới dạng datagram và có ranh giới, người nhận kiểm tra tính toàn vẹn của dữ liệu nhưng chỉ khi nhận được thông báo thành công.

Hãy tóm tắt:

TCP là một giao thức đáng tin cậy và chính xác giúp ngăn ngừa mất dữ liệu. Một tin nhắn sẽ luôn được gửi với độ chính xác tối đa hoặc hoàn toàn không được gửi. Người nhận không cần logic để sắp xếp dữ liệu, vì dữ liệu đến đã được sắp xếp sẵn. UDP không đáng tin cậy bằng nhưng nó là giao thức truyền dữ liệu nhanh hơn. Bên gửi và bên nhận cần một số logic bổ sung để hoạt động với giao thức này. Nhưng hãy xem cách nó hoạt động bằng cách sử dụng ví dụ về trò chơi máy tính hoặc trò chơi di động được chơi qua mạng. Chúng tôi có thể không còn quan tâm đến những gì lẽ ra phải đến 5 giây trước và chúng tôi có thể bỏ qua một vài gói nếu chúng không đến đúng giờ — trò chơi có thể bị lag, nhưng bạn vẫn có thể chơi!

Trong Java, để làm việc với các datagram được truyền qua UDP, chúng ta sử dụng các đối tượng của lớp DatagramSocketDatagramPacket .

Để trao đổi dữ liệu, bên gửi và bên nhận tạo các datagram socket, tức là các thể hiện của lớp DatagramSocket . Lớp có một số hàm tạo. Sự khác biệt giữa chúng là nơi ổ cắm được tạo sẽ kết nối:

DatagramSocket () Kết nối với bất kỳ cổng khả dụng nào trên máy cục bộ
DatagramSocket (cổng int) Kết nối với cổng được chỉ định trên máy cục bộ
DatagramSocket (cổng int, địa chỉ InetAddress) Kết nối với cổng được chỉ định tại một địa chỉ trên máy cục bộ (addr)

Lớp này chứa nhiều phương thức để truy cập và quản lý các tham số của socket (chúng ta sẽ xem xét chúng sau), cũng như các phương thức để nhận và gửi datagram:

gửi (gói DatagramPacket) Gửi datagram được đóng gói thành gói
nhận (gói DatagramPacket) Nhận các datagram được đóng gói thành các gói

DatagramPacket là một lớp đại diện cho một gói datagram. Các gói datagram được sử dụng để triển khai dịch vụ phân phối gói không kết nối. Mỗi tin nhắn được định tuyến từ máy này sang máy khác chỉ dựa trên thông tin chứa trong gói tin đó. Nhiều gói được gửi từ máy này sang máy khác có thể được định tuyến khác nhau và có thể đến theo bất kỳ thứ tự nào. Giao hàng của các gói không được đảm bảo.

Nhà xây dựng:

DatagramPacket(byte[] buf, độ dài int) Tạo một DatagramPacket để chấp nhận các gói tin có độ dài .
DatagramPacket(byte[] buf, độ dài int, địa chỉ InetAddress, cổng int) Tạo một gói datagram để gửi các gói có độ dài đến số cổng được chỉ định trên máy chủ được chỉ định.
DatagramPacket(byte[] buf, int offset, int length) Tạo một DatagramPacket để chấp nhận các gói có độ dài length , chỉ định một phần bù trong bộ đệm.
DatagramPacket(byte[] buf, int offset, int length, địa chỉ InetAddress, int port) Tạo một gói datagram để gửi các gói có độ dài với độ lệch offset đến số cổng được chỉ định trên máy chủ được chỉ định.
DatagramPacket(byte[] buf, int offset, int length, địa chỉ SocketAddress) Tạo một gói datagram để gửi các gói có độ dài với độ lệch offset đến số cổng được chỉ định trên máy chủ được chỉ định.
DatagramPacket(byte[] buf, độ dài int, địa chỉ SocketAddress) Tạo một gói datagram để gửi các gói có độ dài đến số cổng được chỉ định trên máy chủ được chỉ định.

Chúng tôi nhắc lại rằng cách tiếp cận UDP không thiết lập kết nối. Các gói được gửi đi với hy vọng rằng người nhận đang mong đợi chúng. Nhưng bạn có thể thiết lập kết nối bằng phương thức connect(InetAddress addr, int port) của lớp DatagramSocket .

Kết nối một chiều được thiết lập với máy chủ dựa trên địa chỉ và cổng: để gửi hoặc nhận datagram. Kết nối có thể được kết thúc bằng phương thức ngắt kết nối () .

Hãy thử viết mã máy chủ dựa trên DatagramSocket để nhận dữ liệu:


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

Chúng tôi tạo một đối tượng DatagramSocket để lắng nghe trên cổng 1050. Khi nhận được một tin nhắn, nó sẽ in nó ra bàn điều khiển. Chúng tôi sẽ truyền từ "Xin chào", vì vậy chúng tôi giới hạn kích thước bộ đệm là năm byte.

Bây giờ chúng ta sẽ tạo lớp người gửi:


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

}

Trong phương thức sendMessage , chúng tôi tạo một DatagramPacketDatagramSocket và gửi tin nhắn của chúng tôi. Lưu ý rằng phương thức close() được sử dụng để đóng DatagramSocket sau khi gửi tin nhắn.

Mỗi giây, bảng điều khiển của người nhận hiển thị thông báo "Xin chào" do người gửi gửi đến. Điều này có nghĩa là thông tin liên lạc của chúng tôi đang hoạt động chính xác.