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