1. 程式人生 > >UDP 理論詳解

UDP 理論詳解

UDP 簡 介

  • UDP 是一種高速,無連線的資料交換方式UDP 傳送資料前並不與對方建立連線,對接收到的資料也不傳送確認訊號,傳送端不知道資料是否會正確接收,當然也不用重發,所以說 UDP 是無連線的、不可靠的一種資料傳輸協議
  • UDP( User Datagram Protocol ) 使用者資料報協議,是一種無連線的協議,每個資料報都是一個獨立的資訊,包括完整的源或目的地址,它在網路上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
  • 因為不必進行收發資料的確認,所以 UDP 的實時性更好,傳輸速率更高。有時候速度比資料完整性重要,在比如視訊會議、電話通訊等,丟失幾幀畫面、聲音是可以接受的,但在需要資料安全接受的環境下,就宜採用TCP
  • 在網路質量令人不十分滿意的環境下,UDP 協議資料包丟失會比較嚴重。但是由於它不屬於連線型協議,因而具有資源消耗小,處理速度快的優點,所以通常音訊、視訊和普通資料在傳送時使用 UDP 較多,因為它們即使偶爾丟失一兩個資料包,也不會對接收結果產生太大影響。
  • Java 中使用 UDP 程式設計主要使用 java.net 包下的 DatagramSocket 和 DatagramPacket 類
  • 可以參考《TCP 理論詳解

通訊示例

·接收端·

  • 使用 DatagramSocket(int port) 建立socket(套間字)服務,必須指定監視的 port
  • 定義資料報包(DatagramPacket),用於儲存接收到的資料
  • 通過 DatagramSocket 的 receive 方法將接收的資料存入上面定義的包中
  • 使用 DatagramPacket 的 getData 方法,提取資料
  • DatagramPacket 關閉資源
package com.lct.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.Charset;
import java.util.Date;

/**
 * Created by Administrator on 2018/10/14 0014.
 * UDP 接收端
 */
public class UdpServer {
    public static void main(String[] args) {
        udpReceive();
    }

    /**
     * Udp 監聽埠資料
     */
    public static void udpReceive() {
        DatagramSocket datagramSocket = null;
        /** 資料接收大小設定為1024位元組,超出部分是接收不到的
         */
        byte[] buffer = new byte[1024];
        DatagramPacket datagramPacket;
        try {
            /**
             * DatagramSocket(int port, InetAddress laddr):建立一個DatagramSocket例項,並將該物件繫結到指定IP地址、指定埠。
             * DatagramSocket(int port):建立一個DatagramSocket例項,並將該物件繫結到本機預設IP地址、指定埠。
             * */
            datagramSocket = new DatagramSocket(8080);
            datagramPacket = new DatagramPacket(buffer, buffer.length);

            /**
             * setSoTimeout(int timeout):設定 DatagramSocket 的 receive(DatagramPacket p) 方法
             * 阻塞監聽埠時的超時時間,單位毫秒,
             * 當 receive 方法阻塞監聽埠超過指定的時間時,丟擲異常:java.net.SocketTimeoutException: Receive timed out
             */
            /*datagramSocket.setSoTimeout(60 * 1000);*/

            /** 接收端迴圈監聽*/
            while (true) {
                System.out.println(new Date() + " :開始監聽埠" + Thread.currentThread().getName());
                /**阻塞監聽埠*/
                datagramSocket.receive(datagramPacket);
                /**讀取資料*/
                String message = new String(datagramPacket.getData(), 0, datagramPacket.getLength(), Charset.forName("UTF-8"));
                System.out.println("監聽到資料:" + message);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            /**如果傳送異常,關閉操作*/
            if (datagramSocket != null) {
                if (!datagramSocket.isConnected()) {
                    datagramSocket.disconnect();
                }
                if (!datagramSocket.isClosed()) {
                    datagramSocket.close();
                }
            }
        }
    }
}

·傳送端·

  • 使用 DatagramSocket 建立 socket(套間字)服務,可以指定埠,也可讓它自動分配
  • 將資料打包到 DatagramPacket 中
  • 通過 socket 服務傳送打包好的 DatagramPacket(send() 方法
  • socket 服務關閉資源
package com.lct.udp;

import java.io.IOException;
import java.net.*;

/**
 * Created by Administrator on 2018/10/14 0014.
 * Udp 傳送端
 */
public class UcpClient {
    public static void main(String[] args) {
        /**
         * 使用 3 個執行緒傳送訊息,模擬 多個客戶端操作
         */
        for (int i = 0; i < 3; i++) {
            new Thread() {
                @Override
                public void run() {
                    sendUdpMessage();
                }
            }.start();
        }
    }

    /**
     * 傳送 Udp 訊息
     */
    public static void sendUdpMessage() {
        DatagramSocket datagramSocket = null;
        try {
            /** 例項化資料報套接字*/
            datagramSocket = new DatagramSocket();

            /**將被髮送的資料轉為位元組陣列,同時指定編碼*/
            String msg = "修長城的民族!" + Thread.currentThread().getName();
            byte[] msgBytes = msg.getBytes("UTF-8");

            /** InetAddress:指定 UDP 接收端的 IP 地址*/
            InetAddress inetAddress = InetAddress.getByName("192.168.1.20");

            /**
             * DatagramPacket(byte buf[], int length,InetAddress address, int port)
             *      buf[]:被髮送的資料
             *      length:指定 buf[] 中 [0,length]的資料進行傳送
             *      address:udp 接收端的地址
             *      port:udp 接收端監聽的埠
             */
            DatagramPacket datagramPacket = new DatagramPacket(msgBytes, msgBytes.length, inetAddress, 8080);
            /**傳送資料*/
            datagramSocket.send(datagramPacket);
            System.out.println("訊息傳送結束..............." + Thread.currentThread().getName());
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            /**操作結束,釋放 datagramSocket,並關閉*/
            if (datagramSocket != null) {
                if (!datagramSocket.isConnected()) {
                    datagramSocket.disconnect();
                }
                if (!datagramSocket.isClosed()) {
                    datagramSocket.close();
                }
            }
        }
    }
}

udp 接收端輸出如下:

Sun Oct 14 11:43:40 CST 2018 :開始監聽埠main
監聽到資料:修長城的民族!Thread-0
Sun Oct 14 11:43:42 CST 2018 :開始監聽埠main
監聽到資料:修長城的民族!Thread-1
Sun Oct 14 11:43:42 CST 2018 :開始監聽埠main
監聽到資料:修長城的民族!Thread-2
Sun Oct 14 11:43:42 CST 2018 :開始監聽埠main

udp 傳送端輸出如下:

訊息傳送結束...............Thread-1
訊息傳送結束...............Thread-0
訊息傳送結束...............Thread-2

網路程式設計

  • 技術日新月異,目前網路程式設計最為流行的當屬 Netty,Java 網路程式設計發展歷程:

JDK 1.4 以前:java.net + java.io——即平時所使用的簡單的 TCP 、UDP 程式設計

JDK 1.4 及以後:java.nio

當下流行:JBoos 的 Netty 庫、Apache 的  Mina 等