Java UDP通訊:Java DatagramSocket類和DatagramPacket類

2020-07-16 10:04:40
在 TCP/IP 協定的傳輸層除了一個 TCP 協定之外,還有一個 UDP 協定。UDP 協定是使用者資料包協定的簡稱,也用於網路資料的傳輸。雖然 UDP 協定是一種不太可靠的協定,但有時在需要較快地接收資料並且可以忍受較小錯誤的情況下,UDP 就會表現出更大的優勢。

下面是在 Java 中使用 UDP 協定傳送資料的步驟。
  1. 使用 DatagramSocket() 建立一個封包通訊端。
  2. 使用 DatagramPacket() 建立要傳送的封包。
  3. 使用 DatagramSocket 類的 send() 方法傳送封包。

接收 UDP 封包的步驟如下:
  • 使用 DatagramSocket 建立封包通訊端,並將其系結到指定的埠。
  • 使用 DatagramPacket 建立位元組陣列來接收封包。
  • 使用 DatagramPacket 類的 receive() 方法接收 UDP 包。

DatagramPacket 類

java.net 包中的 DatagramPacket 類用來表示資料包包,資料包包用來實現無連線包投遞服務。每條報文僅根據該包中包含的資訊從一台機器路由到另一台機器。從一台機器傳送到另一台機器的多個包可能選擇不同的路由,也可能按不同的順序到達。表 1 和表 2 簡單介紹了 DatagramPacket 的構造方法和常用方法。

表1 DatagramPacket的構造方法
構造方法 說明
DatagramPacket(byte[] buf,int length) 構造 DatagramPacket,用來接收長度為 length 的封包。
DatagramPacket(byte[] buf,int offset,
int length)
構造 DatagramPacket,用來接收長度為 length 的包,在緩
衝區中指定了偏移量。
DatagramPacket(byte[] buf,int length,
InetAddress address,int port)
構造 DatagramPacket,用來將長度為 length 的包傳送到指
定主機上的指定埠。
DatagramPacket(byte[] buf,int length,
SocketAddress address)
構造資料包包,用來將長度為 length 的包傳送到指定主機上
的指定埠。
DatagramPacket(byte[] buf,int offset,
int length,InetAddress address,int port)
構造 DatagramPacket,用來將長度為 length 偏移量為 offset
的包傳送到指定主機上的指定埠。
DatagramPacket(byte[] buf,int offset,
int length,SocketAddress address)
構造資料包包,用來將長度為 length、偏移量為 offset 的包發
送到指定主機上的指定埠。

表2 DatagramPacket的常用方法
方法 說明
InetAddress getAddress() 返回某台機器的 IP 地址,此資料包將要發往該機器或者
從該機器接收。
byte[] getData() 返回資料緩衝區。
int getLength() 返回將要傳送或者接收的資料的長度。
int getOffset() 返回將要傳送或者接收的資料的偏移量。
int getPort() 返回某台遠端主機的埠號,此資料包將要發往該主機或
者從該主機接收。
getSocketAddress() 獲取要將此包傳送或者發出此資料包的遠端主機的
SocketAddress(通常為 IP地址+埠號)。
void setAddress(InetAddress addr) 設定要將此資料包發往的目的機器的IP地址。
void setData(byte[] buf) 為此包設定資料緩衝區。
void setData(byte[] buf,int offset,
int length)
為此包設定資料緩衝區。
void setLength(int length) 為此包設定長度。
void setPort(int port) 設定要將此資料包發往的遠端主機的埠號。
void setSocketAddress(SocketAddress
address)
設定要將此資料包發往的遠端主機的
SocketAddress(通常為 IP地址+埠號)。

DatagramSocket 類

DatagramSocket 類用於表示傳送和接收資料包包的通訊端。資料包包通訊端是包投遞服務的傳送或接收點。每個在資料包包通訊端上傳送或接收的包都是單獨編址和路由的。從一台機器傳送到另一台機器的多個包可能選擇不同的路由,也可能按不同的順序到達。

DatagramSocket 類的常用構造方法如表 3 所示。

表3 DatagramSocket 的構造方法
構造方法 說明
DatagramSocket() 構造資料包包通訊端並將其系結到本地主機上任何可用的埠。
DatagramSocket(int port) 建立資料包包通訊端並將其系結到本地主機上的指定埠。
DatagramSocket(int portJnetAddress addr) 建立資料包包通訊端,將其系結到指定的本地地址。
DatagramSocket(SocketAddress bindaddr) 建立資料包包通訊端,將其系結到指定的本地通訊端地址。

DatagramSocket 類的常用方法如表 4 所示。

表4 DatagramSocket 的常用方法
方法 說明
void bind(SocketAddress addr) 將此 DatagramSocket 係結到特定的地址和埠。
void close() 關閉此資料包包通訊端。
void connect(InetAddress address,int port) 將通訊端連線到此通訊端的遠端地址。
void connect(SocketAddress addr) 將此套接子連線到遠端套接子地址(IP地址+埠號)。
void disconnect() 斷開通訊端的連線。
InetAddress getInetAddress() 返回此通訊端連線的地址。
InetAddress getLocalAddress() 獲取通訊端系結的本地地址。
int getLocalPort() 返回此通訊端系結的本地主機上的埠號。
int getPort() 返回此通訊端的埠。

例 1

編寫 UDP 程式,要求用戶端程式可以向伺服器端傳送多條資料,伺服器端程式可以接收用戶端傳送的多條資料並將其資訊輸出在控制台,主要步驟如下所示。

(1) 建立一個類作為用戶端,在 main() 方法定義一個 DatagramSocket 物件和一個 DatagramPacket 物件並初始化為 null。然後再定義一個 InetAddress 物件和一個埠號並分別進行初始化,程式碼如下所示。
public static void main(String[] args)
{
    DatagramSocket ds=null;
    DatagramPacket dpSend=null;
    InetAddress ia=InetAddress.getByName("127.0.0.1");
    int port=3021;
}

(2) 使用 DatagramSocket 的 send(DatagramPacket p) 方法向伺服器端傳送資料包包,使用迴圈的方式完成 5 次資料的傳送,每傳送 1 次傳輸線程休眠 1000 毫秒,資料傳送完畢呼叫 close() 方法,關閉 DatagramSocket 物件,程式碼如下。
try
{
    ds=new DatagramSocket();
    for(int i=0;i<5;i++)
    {
        byte[] data=("我是 UDP 用戶端"+i).getBytes();
        dpSend=new DatagramPacket(data,data.length,ia,port);
        ds.send(dpSend);
        Thread.sleep(1000);
    }
    ds.close();
}
catch(IOException | InterruptedException e)
{
    // TODO 自動生成的 catch 塊
    e.printStackTrace();
}   

(3) 建立一個類作為伺服器端,在 main() 方法中定義一個 DatagramSocket 物件和一個 DatagramPacket 物件並初始化為 null,再定義一個埠號,程式碼如下所示。
public static void main(String[] args)
{
    DatagramSocket ds=null;
    DatagramPacket dpReceive=null;
    int port=3021;
}

(4) 如果成功連線到 UDP 伺服器則輸出“UDP 伺服器已啟動。。。”。迴圈接收用戶端傳送的資料,並將其傳送的內容以及IP地址等資訊輸出到控制台,程式碼如下所示。
try
{
    ds=new DatagramSocket(port);
    System.out.println("UDP伺服器已啟動。。。");
    byte[] b=new byte[1024];
    while(ds.isClosed()==false)
    {
        dpReceive=new DatagramPacket(b, b.length);
        try
        {
            ds.receive(dpReceive);
            byte[] Data=dpReceive.getData();
            int len=Data.length;
            System.out.println("UDP用戶端傳送的內容是:" + new String(Data, 0, len).trim());
            System.out.println("UDP用戶端IP:" + dpReceive.getAddress());
            System.out.println("UDP用戶端埠:" + dpReceive.getPort());
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }
    }
    catch(SocketException e1)
    {
        // TODO 自動生成的 catch 塊
        e1.printStackTrace();
    }
}

(5) 最後關閉 DatagramSocket 物件。執行伺服器端程式,執行結果如下所示。
UDP伺服器已啟動。。。

(6) 再執行用戶端程式,控制台的輸出結果如下所示。
UDP用戶端傳送的內容是:我是UDP用戶端0
UDP用戶端IP:/127.0.0.1
UDP用戶端埠:53472
UDP用戶端傳送的內容是:我是UDP用戶端1
UDP用戶端IP:/127.0.0.1
UDP用戶端埠:53472
UDP用戶端傳送的內容是:我是UDP用戶端2
UDP用戶端IP:/127.0.0.1
UDP用戶端埠:53472
UDP用戶端傳送的內容是:我是UDP用戶端3
UDP用戶端IP:/127.0.0.1
UDP用戶端埠:53472
UDP用戶端傳送的內容是:我是UDP用戶端4
UDP用戶端IP:/127.0.0.1
UDP用戶端埠:53472