首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 套接字程式設計(下):UDP 與多點傳送

     編輯:Java 相關知識

資料報套接字

因使用流套接字每個連接均需花費一定時間,為減少此開銷,網路 API 提供第二種套接字:資料報套接字。資料報使用 UDP 發送定址資訊(從客戶程式到服務程式或反之),不同處在於可透過資料報套接字發送多 IP 資訊包,資料報資訊包含在資料報包中,此外資料報包又包含在 IP 包內,將定址資訊長度限制在 60,000 位元組內。圖 2 顯示位於 IP 包內的資料報包的資料報資訊。

與 TCP 保證資訊到達目的地方式不同,UDP 提供另一種方法:若資料報包未到達目的地,UDP 不會請求發送者重新發送。因 UDP 在每個資料報包含錯誤檢測資訊,每個包到達目的地後 UDP 僅進行簡單錯誤檢查,檢測失敗則丟棄包,不從發送者重新請求替代者。這類似透過郵局寄信:寄信人寄信前無需與收信人建立連接,同樣無法保證信件能到達。

資料報套接字工作包含三個類:DatagramPacket、DatagramSocket、MulticastSocket。DatagramPacket 描述資料報包地址資訊,DatagramSocket 表示客戶與服務程式資料報套接字,MulticastSocket 描述能進行多點傳送的資料報套接字,三類均位於 java.net 套件。

DatagramPacket 類

使用資料報包前需熟悉 DatagramPacket 類,地址資訊與資料報包以位元組陣列方式同時壓縮入此類建立物件中。

DatagramPacket 有數個建構函數,雖形式不同但通常有兩共同參數:byte[] buffer、int length。buffer 參數包含保存資料報資料包資訊位元組陣列參考,length 表示陣列長度。

最簡單建構函數 DatagramPacket(byte[] buffer, int length) 確定資料報資料包陣列與長度,但無地址與埠資訊,可稍後透過 setAddress(InetAddress addr)、setPort(int port) 添加。下列代碼示範:

byte[] buffer = new byte[100];
DatagramPacket dgp = new DatagramPacket(buffer, buffer.length);
InetAddress ia = InetAddress.getByName("www.disney.com");
dgp.setAddress(ia);
dgp.setPort(6000); // Send datagram packet to port 6000.

若偏好調用建構函數時同時包含地址與埠號,可用 DatagramPacket(byte[] buffer, int length, InetAddress addr, int port):

byte[] buffer = new byte[100];
InetAddress ia = InetAddress.getByName("www.disney.com");
DatagramPacket dgp = new DatagramPacket(buffer, buffer.length, ia, 6000);

有時建立 DatagramPacket 後想改變位元組陣列與長度,可透過 setData(byte[] buffer)、setLength(int length) 實現。隨時可透過 getData() 取得陣列參考、getLength() 取得長度:

byte[] buffer2 = new byte[256];
dgp.setData(buffer2);
dgp.setLength(buffer2.length);

DatagramSocket 類

DatagramSocket 類在客戶端建立資料報套接字與服務器通訊連接,發送與接收資料報套接字。雖有多建構函數,但發現建立客戶端資料報套接字最方便選擇是 DatagramSocket(),服務器端則是 DatagramSocket(int port)。若無法建立套接字或綁定本地埠,兩函數都拋出 SocketException。一旦建立 DatagramSocket 物件,程式分別調用 send(DatagramPacket dgp)、receive(DatagramPacket dgp) 發送接收資料報包。

清單 4 顯示 DGSClient 原始碼,示範建立資料報套接字並透過套接字處理發送接收資訊:

// DGSClient.java
import java.io.*; import java.net.*;
class DGSClient { public static void main(String[] args) {
String host = "localhost";
if (args.length == 1) host = args[0];
DatagramSocket s = null;
try {
s = new DatagramSocket();
byte[] buffer = new String("Send me a datagram").getBytes();
InetAddress ia = InetAddress.getByName(host);
DatagramPacket dgp = new DatagramPacket(buffer, buffer.length, ia, 10000);
s.send(dgp);
byte[] buffer2 = new byte[100];
dgp = new DatagramPacket(buffer2, buffer.length, ia, 10000);
s.receive(dgp);
System.out.println(new String(dgp.getData()));
} catch (IOException e) { System.out.println(e.toString()); }
finally { if (s != null) s.close(); }
} }

DGSClient 由建立綁定任意本地埠 DatagramSocket 開始,裝入帶文本資訊陣列 buffer 與描述服務器主機 IP 位址 InetAddress 子類物件參考,建立 DatagramPacket 含緩衝區參考、InetAddress 參考、服務埠 10000,send() 發送給服務器。接著建立新 DatagramPacket 接收回應,receive() 取得回應包,getData() 回傳包參考,最後關閉 DatagramSocket。

DGSServer 服務程式補充 DGSClient,清單 5 為原始碼:

// DGSServer.java
import java.io.*; import java.net.*;
class DGSServer { public static void main(String[] args) throws IOException {
System.out.println("Server starting ...\n");
DatagramSocket s = new DatagramSocket(10000);
byte[] data = new byte[100];
DatagramPacket dgp = new DatagramPacket(data, data.length);
while (true) {
s.receive(dgp);
System.out.println(new String(data));
s.send(dgp);
}
} }

DGSServer 建立綁定埠 10000 資料報套接字,建立位元組陣列容納資料,建立資料報包(不初始化地址,因從客戶程式取得),進入無限迴圈接收包、顯示內容、回應客戶端。套接字不關閉因迴圈無限。

編譯後,輸入 java DGSServer 執行服務器,同主機輸入 java DGSClient 執行客戶端。若不同主機,命令列加服務器主機名或 IP,如 java DGSClient www.yesky.com。

多點傳送與 MulticastSocket 類

前例顯示服務器程式執行緒發送單一訊息(透過流或資料報套接字)給唯一客戶端,稱單點傳送。多數情況不適合單點傳送,如搖滾歌手開演唱會透過網路播放,畫面聲音品質仰賴傳輸速度,服務器傳送約 10 億位元組給客戶端。用單點傳送,每客戶程式都要複製一份資料,若 1 萬客戶端觀看,服務器須傳 1 萬 GB 資料,必然導致網路阻塞、降低速度。

若服務器要將同一資訊發送給多客戶端,可利用多點傳送通訊。多點傳送即服務程式對專用多點傳送組 IP 位址與埠發送一系列資料報包。透過加入操作 IP 位址被多點傳送 Socket 註冊,客戶程式可接收發送給組的資料報包(同樣客戶程式也可給組發送包)。客戶程式讀完所有包後,可透過離開組操作離開多點傳送組。

注意:IP 位址 224.0.0.1 至 239.255.255.255 為保留多點傳送組地址。

網路 API 透過 MulticastSocket 類與輔助類(如 NetworkInterface)支援多點傳送。客戶程式要加入多點傳送組時,建立 MulticastSocket 物件。MulticastSocket(int port) 允許指定埠接收資料報包,埠必須與服務程式埠號匹配。加入組調用兩個 joinGroup() 之一,離開組調用兩個 leaveGroup() 之一。因 MulticastSocket 擴展 DatagramSocket,MulticastSocket 物件有權訪問 DatagramSocket 方法。

清單 6 為 MCClient 原始碼,示範客戶端加入多點傳送組:

// MCClient.java
import java.io.*; import java.net.*;
class MCClient { public static void main(String[] args) throws IOException {
MulticastSocket s = new MulticastSocket(10000);
InetAddress group = InetAddress.getByName("231.0.0.1");
s.joinGroup(group);
for (int i = 0; i < 10; i++) {
byte[] buffer = new byte[256];
DatagramPacket dgp = new DatagramPacket(buffer, buffer.length);
s.receive(dgp);
byte[] buffer2 = new byte[dgp.getLength()];
System.arraycopy(dgp.getData(), 0, buffer2, 0, dgp.getLength());
System.out.println(new String(buffer2));
}
s.leaveGroup(group);
s.close();
} }

MCClient 建立綁定埠 10000 MulticastSocket,取得含多點傳送組 IP 231.0.0.1 InetAddress,joinGroup 加入組,接收 10 個資料報包並輸出內容,leaveGroup 離開組,關閉套接字。

或許對用兩陣列 buffer、buffer2 感奇怪。接收資料報包後 getData() 回傳參考,包長度 256 位元組,若輸出所有資料會有許多空格,不合理,故建立小陣列 buffer2,長度為資料實際長度,透過 getLength() 取得。從 buffer 複製 getLength() 長度到 buffer2 快速方法是 System.arraycopy()。

清單 7 MCServer 原始碼顯示服務程式運作:

// MCServer.java
import java.io.*; import java.net.*;
class MCServer { public static void main(String[] args) throws IOException {
System.out.println("Server starting...\n");
MulticastSocket s = new MulticastSocket();
InetAddress group = InetAddress.getByName("231.0.0.1");
byte[] dummy = new byte[0];
DatagramPacket dgp = new DatagramPacket(dummy, 0, group, 10000);
for (int i = 0; i < 30000; i++) {
byte[] buffer = ("Video line " + i).getBytes();
dgp.setData(buffer);
dgp.setLength(buffer.length);
s.send(dgp);
}
s.close();
} }

MCServer 建立 MulticastSocket(不綁定埠,因屬 DatagramPacket 一部分),DatagramPacket 有多點傳送組 IP 231.0.0.1,建立包後進入迴圈發送 3 萬行文本,每行建立位元組陣列,參考存入 DatagramPacket,send() 發送給所有組成員。

編譯後,java MCServer 執行服務器,再執行一或多個 MCClient。

結論

本文透過研究套接字揭示 Java 網路 API 應用方法,介紹套接字概念與組成、流套接字與資料報套接字、以及 InetAddress、Socket、ServerSocket、DatagramPacket、DatagramSocket、MulticastSocket 類使用。完成本文後可編寫基本底層通訊程式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved