在Java中操縱UDP
使用位于JDK中Java.net包下的DatagramSocket和DatagramPacket類,可以非常方便地控制用戶數據報文?
在描述它們之前,必須了解位于同一個位置的InetAddress類?InetAddress實現了Java.io. Serializable接口,不允許繼承?它用于描述和包裝一個Internet IP地址,通過三個方法返回InetAddress實例:
getLocalhost():返回封裝本地地址的實例?
getAllByName(String host):返回封裝Host地址的InetAddress實例數組?
getByName(String host):返回一個封裝Host地址的實例?其中,Host可以是域名或者是一個合法的IP地址?
DatagramSocket類用于創建接收和發送UDP協議的Socket實例?和Socket類依賴SocketImpl類一樣,DatagramSocket類的實現也依靠專門為它設計的DatagramScoketImplFactory類?DatagramSocket類有3個構建器:
DatagramSocket():創建實例?這是個比較特殊的用法,通常用于客戶端編程,它并沒有特定監聽的端口,僅僅使用一個臨時的?
DatagramSocket(int port):創建實例,并固定監聽Port端口的報文?
DatagramSocket(int port, InetAddress localAddr):這是個非常有用的構建器,當一臺機器擁有多于一個IP地址的時候,由它創建的實例僅僅接收來自LocalAddr的報文?
值得注意的是,在創建DatagramSocket類實例時,如果端口已經被使用,會產生一個SocketException的異常拋出,并導致程序非法終止,這個異常應該注意捕獲?DatagramSocket類最主要的方法有4個:
Receive(DatagramPacket d):接收數據報文到d中?receive方法產生一個“阻塞“?
Send(DatagramPacket d):發送報文d到目的地?
SetSoTimeout(int timeout):設置超時時間,單位為毫秒?
Close():關閉DatagramSocket?在應用程序退出的時候,通常會主動釋放資源,關閉Socket,但是由于異常地退出可能造成資源無法回收?所以,應該在程序完成時,主動使用此方法關閉Socket,或在捕獲到異常拋出后關閉Socket?
“阻塞”是一個專業名詞,它會產生一個內部循環,使程序暫停在這個地方,直到一個條件觸發?
DatagramPacket類用于處理報文,它將Byte數組?目標地址?目標端口等數據包裝成報文或者將報文拆卸成Byte數組?應用程序在產生數據包是應該注意,TCP/IP規定數據報文大小最多包含65507個,通常主機接收548個字節,但大多數平臺能夠支持8192字節大小的報文?DatagramPacket類的構建器共有4個:
DatagramPacket(byte[] buf, int length, InetAddress addr, int port):從Buf數組中,取出Length長的數據創建數據包對象,目標是Addr地址,Port端口?
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port):從Buf數組中,取出Offset開始的?Length長的數據創建數據包對象,目標是Addr地址,Port端口?
DatagramPacket(byte[] buf, int offset, int length):將數據包中從Offset開始?Length長的數據裝進Buf數組?
DatagramPacket(byte[] buf, int length):將數據包中Length長的數據裝進Buf數組?
DatagramPacket類最重要的方法就是getData()了,它從實例中取得報文的Byte數組編碼?
下面程序使用DatagramSocket實現了Server/Client結構的網絡通信。本程序的服務器端使用循環1000次來讀取DatagramSocket中的數據報,每當讀取到內容之后便向該數據報的發送者送回一條信息。
? ? ? ? 服務器端程序代碼如下。
UdpServer.java
public class UdpServer
{
public static final int PORT = 30000;
// 定義每個數據報的最大大小為4KB
private static final int DATA_LEN = 4096;
// 定義接收網絡數據的字節數組
byte[] inBuff = new byte[DATA_LEN];
// 以指定字節數組創建準備接收數據的DatagramPacket對象
private DatagramPacket inPacket =
new DatagramPacket(inBuff , inBuff.length);
// 定義一個用于發送的DatagramPacket對象
private DatagramPacket outPacket;
// 定義一個字符串數組,服務器端發送該數組的元素
String[] books = new String[]
{
“瘋狂Java講義”,
“輕量級Java EE企業應用實戰”,
“瘋狂Android講義”,
“瘋狂Ajax講義”
};
public void init()throws IOException
{
try(
// 創建DatagramSocket對象
DatagramSocket socket = new DatagramSocket(PORT))
{
// 采用循環接收數據
for (int i = 0; i 《 1000 ; i++ )
{
// 讀取Socket中的數據,讀到的數據放入inPacket封裝的數組里
socket.receive(inPacket);
// 判斷inPacket.getData()和inBuff是否是同一個數組
System.out.println(inBuff == inPacket.getData());
// 將接收到的內容轉換成字符串后輸出
System.out.println(new String(inBuff
, 0 , inPacket.getLength()));
// 從字符串數組中取出一個元素作為發送數據
byte[] sendData = books[i % 4].getBytes();
// 以指定的字節數組作為發送數據,以剛接收到的DatagramPacket的
// 源SocketAddress作為目標SocketAddress創建DatagramPacket
outPacket = new DatagramPacket(sendData
, sendData.length , inPacket.getSocketAddress());
// 發送數據
socket.send(outPacket);
}
}
}
public static void main(String[] args)
throws IOException
{
new UdpServer().init();
}
}
客戶端程序代碼也與此類似,客戶端采用循環不斷地讀取用戶鍵盤輸入,每當讀取到用戶輸入的內容后就將該內容封裝成DatagramPacket數據報,再將該數據報發送出去;接著把DatagramSocket中的數據讀入接收用的DatagramPacket中(實際上是讀入該DatagramPacket所封裝的字節數組中)。
評論
查看更多