當我們談到網絡時,我們不能不提到 OSI 模型。

就此模型而言,今天我們最感興趣的是傳輸層 (4)。

這是我們處理“從 A 點到 B 點”移動數據的級別。傳輸層的主要任務是確保將消息傳遞到目的地,同時保持正確的順序。兩種最常見的傳輸層協議是:TCP 和 UDP。他們在概念上以不同的方式工作,但每個人都有自己的優勢,使他們能夠解決特定的問題。

首先,讓我們看看 TCP 是如何工作的。

TCP(傳輸控制協議)是一種網絡協議,可確保在交換數據之前建立主機之間的連接。

這是一個非常可靠的協議,因為它每次發送另一個數據包時,都必須檢查是否收到了前一個數據包。

發送的數據包是有序的,如果某個數據包有問題(即接收方未確認數據包已經到達),則重新發送該數據包。結果,傳輸速率相對較低,因為需要更多時間進行嚴格監控和確保正確排序。

這就是它的“兄弟”UDP 協議的用武之地。與 TCP 不同,UDP 並不真正關心每個數據包的順序和狀態。它只是簡單地發送數據而無需發送確認。更重要的是,它不建立連接,也不以任何方式依賴於連接狀態。

它的目的只是將數據發送到一個地址。這導致了該協議的主要缺點,即可靠性低,因為它可能會丟失數據片段。此外,接收方必須為數據可能無序到達這一事實做好準備。也就是說,該協議也有一個優勢,即更高的傳輸速率,因為該協議僅限於發送數據。

數據本身的傳輸方式也存在差異。在 TCP 中,數據是流式傳輸的,這意味著數據沒有邊界。在 UDP 中,數據以數據報的形式傳輸並具有邊界,接收方檢查數據的完整性,但前提是消息被成功接收。

讓我們總結一下:

TCP 是一種可靠且準確的協議,可防止數據丟失。消息將始終以最準確的方式傳遞,或者根本不傳遞。接收者不需要訂購數據的邏輯,因為傳入的數據已經訂購。 UDP 不那麼可靠,但它是一種更快的數據傳輸協議。發送方和接收方需要一些額外的邏輯才能使用此協議。但讓我們以通過網絡玩電腦遊戲或手機遊戲為例,看看它是如何工作的。我們可能不再關心應該在 5 秒前到達的數據包,如果它們沒有按時到達,我們可以跳過幾個數據包——遊戲可能會延遲,但您仍然可以玩!

在 Java 中,為了處理通過 UDP 傳輸的數據報,我們使用DatagramSocketDatagramPacket類的對象。

為了交換數據,發送方和接收方創建數據報套接字,即DatagramSocket類的實例。該類有幾個構造函數。它們之間的區別在於創建的套接字將連接到哪裡:

數據報套接字 () 連接到本地機器上的任何可用端口
DatagramSocket(整數端口) 連接到本地機器上的指定端口
DatagramSocket(int port, InetAddress 地址) 連接到本地機器地址 (addr) 的指定端口

該類包含許多用於訪問和管理套接字參數的方法(稍後我們將查看它們),以及用於接收和發送數據報的方法:

發送(數據報包包) 發送打包成數據包的數據報
接收(數據報包包) 接收打包成數據包的數據報

DatagramPacket是一個表示數據報包的類。數據報包用於實現無連接的包傳送服務。每條消息僅根據該數據包中包含的信息從一台機器路由到另一台機器。從一台機器發送到另一台機器的多個數據包可能會有不同的路由,並且可能以任何順序到達。不保證數據包的交付。

構造函數:

DatagramPacket(byte[] buf, int length) 創建一個DatagramPacket以接受長度為 length 的數據
DatagramPacket(byte[] buf, int 長度, InetAddress 地址, int 端口) 創建一個數據報包,將長度為 length的數據包發送到指定主機上的指定端口號。
DatagramPacket(byte[] buf, int offset, int length) 創建一個DatagramPacket以接受長度為 length 的數據包指定緩衝區中的偏移量。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 創建一個數據報包,將長度為length ,偏移量為offset的數據包發送到指定主機上的指定端口號。
DatagramPacket(byte[] buf, int offset, int length, SocketAddress 地址) 創建一個數據報包,將長度為length ,偏移量為offset的數據包發送到指定主機上的指定端口號。
DatagramPacket(byte[] buf, int length, SocketAddress 地址) 創建一個數據報包,將長度為 length的數據包發送到指定主機上的指定端口號。

我們記得 UDP 方法不建立連接。這些數據包被發送出去,希望收件人期待它們。但是您可以使用DatagramSocket類的connect(InetAddress addr, int port)方法建立連接。

基於地址和端口與主機建立單向連接:發送或接收數據報。可以使用disconnect()方法終止連接。

讓我們嘗試編寫基於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。當它收到一條消息時,它將它打印到控制台。我們將傳輸單詞“Hello”,因此我們將緩衝區大小限制為五個字節。

現在我們將創建發件人類:

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方法中,我們創建一個DatagramPacketDatagramSocket,並發送我們的消息。請注意,close()方法用於在消息發送後關閉DatagramSocket 。

接收者的控制台每秒顯示發送者發送的傳入“Hello”消息。這意味著我們的通信都正常工作。