使用DatagramSocket發送接收數據
DatagramSocket本身只是碼頭不維護狀態不能產生IO流它的唯一作用就是接收和發送數據報Java使用DatagramPacket來代表數據報DatagramSocket接收和發送的數據都是通過DatagramPacket對象完成的
先看一下DatagramSocket的構造器
DatagramSocket()創建一個DatagramSocket實例並將該對象綁定到本機默認IP地址本機所有可用端口中隨機選擇的某個端口
DatagramSocket(int prot)創建一個DatagramSocket實例並將該對象綁定到本機默認IP地址指定端口
DatagramSocket(int port InetAddress laddr)創建一個DatagramSocket實例並將該對象綁定到指定IP地址指定端口
通過上面三個構造器中任意一個構造器即可創建一個DatagramSocket實例通常在創建服務器時我們創建指定端口的DatagramSocket實例這樣保證其他客戶端可以將數據發送到該服務器一旦得到了DatagramSocket實例之後就可以通過如下兩個方法來接收和發送數據
receive(DatagramPacket p)從該DatagramSocket中接收數據報
send(DatagramPacket p)以該DatagramSocket對象向外發送數據報
從上面兩個方法可以看出使用DatagramSocket發送數據報時DatagramSocket並不知道將該數據報發送到哪裡而是由DatagramPacket自身決定數據報的目的就像碼頭並不知道每個集裝箱的目的地碼頭只是將這些集裝箱發送出去而集裝箱本身包含了該集裝箱的目的地
當Client/Server程序使用UDP協議時實際上並沒有明顯的服務器和客戶端因為兩方都需要先建立一個DatagramSocket對象用來接收或發送數據報然後使用DatagramPacket對象作為傳輸數據的載體通常固定IP固定端口的DatagramSocket對象所在的程序被稱為服務器因為該DatagramSocket可以主動接收客戶端數據
下面看一下DatagramPacket的構造器
DatagramPacket(byte buf[]int length)以一個空數組來創建DatagramPacket對象該對象的作用是接收DatagramSocket中的數據
DatagramPacket(byte buf[] int length InetAddress addr int port)以一個包含數據的數組來創建DatagramPacket對象創建該DatagramPacket時還指定了IP地址和端口這就決定了該數據報的目的
DatagramPacket(byte[] buf int offset int length)以一個空數組來創建DatagramPacket對象並指定接收到的數據放入buf數組中時從offset開始最多放length個字節
DatagramPacket(byte[] buf int offset int length InetAddress address int port)創建一個用於發送的DatagramPacket對象也多指定了一個offset參數
在接收數據前應該采用上面的第一個或第三個構造器生成一個DatagramPacket對象給出接收數據的字節數組及其長度然後調用DatagramSocket 的方法receive()等待數據報的到來receive()將一直等待(也就是說會阻塞調用該方法的線程)直到收到一個數據報為止如下代碼所示
//創建接受數據的DatagramPacket對象
DatagramPacket packet=new DatagramPacket(buf )
//接收數據
socketreceive(packet)
發送數據之前調用第二個或第四個構造器創建DatagramPacket對象此時的字節數組裡存放了想發送的數據除此之外還要給出完整的目的地址包括IP地址和端口號發送數據是通過DatagramSocket的方法send()實現的send()根據數據報的目的地址來尋徑以傳遞數據報如下代碼所示
//創建一個發送數據的DatagramPacket對象
DatagramPacket packet = new DatagramPacket(buf length address port)
//發送數據報
socketsend(packet)
當我們使用DatagramPacket來接收數據時會感覺DatagramPacket設計得過於煩瑣對於開發者而言只關心該DatagramPacket能放多少數據而DatagramPacket是否采用字節數組來存儲數據完全不想關心但Java要求創建接收數據用的DatagramPacket時必須傳入一個空的字節數組該數組的長度決定了該DatagramPacket能放多少數據這實際上暴露了DatagramPacket的實現細節接著DatagramPacket又提供了一個getData()方法該方法又可以返回DatagramPacket對象裡封裝的字節數組該方法更顯得有些多余如果程序需要獲取DatagramPacket裡封裝的字節數組直接訪問傳給 DatagramPacket構造器的字節數組實參即可無須調用該方法
當服務器(也可以客戶端)接收到一個DatagramPacket對象後如果想向該數據報的發送者反饋一些信息但由於UDP是面向非連接的所以接收者並不知道每個數據報由誰發送過來但程序可以調用DatagramPacket的如下三個方法來獲取發送者的IP和端口
InetAddress getAddress()返回某台機器的 IP 地址當程序准備發送次數據報時該方法返回此數據報的目標機器的IP地址當程序剛剛接收到一個數據報時該方法返回該數據報的發送主機的IP地址
int getPort()返回某台機器的端口當程序准備發送此數據報時該方法返回此數據報的目標機器的端口當程序剛剛接收到一個數據報時該方法返回該數據報的發送主機的端口
SocketAddress getSocketAddress()返回完整SocketAddress通常由IP地址和端口組成當程序准備發送此數據報時該方法返回此數據報的目標SocketAddress;當程序剛剛接收到一個數據報時該方法返回該數據報是源SocketAddress
上面getSocketAddress方法的返回值是一個SocketAddress對象該對象實際上就是一個IP地址和一個端口號也就是說SocketAddress對象封裝了一個InetAddress對象和一個代表端口的整數所以使用SocketAddress對象可以同時代表IP地址和端口
下面程序使用DatagramSocket實現Server/Client結構的網絡通信程序本程序的服務器端使用循環次來讀取DatagramSocket中的數據報每當讀到內容之後便向該數據報的發送者送回一條信息服務器端代碼如下
程序清單codes///UdpServerjava
public class UdpServer
{
public static final int PORT = ;
//定義每個數據報的最大大小為K
private static final int DATA_LEN = ;
//定義該服務器使用的DatagramSocket
private DatagramSocket socket = null;
//定義接收網絡數據的字節數組
byte[] inBuff = new byte[DATA_LEN];
//以指定字節數組創建准備接受數據的DatagramPacket對象
private DatagramPacket inPacket =
new DatagramPacket(inBuff inBufflength)
//定義一個用於發送的DatagramPacket對象
private DatagramPacket outPacket;
//定義一個字符串數組服務器發送該數組的的元素
String[] books = new String[]
{
輕量級JEE企業應用實戰
基於JEE的Ajax寶典
Struts權威指南
ROR敏捷開發最佳實踐
};
public void init()throws IOException
{
try
{
//創建DatagramSocket對象
socket = new DatagramSocket(PORT)
//采用循環接受數據
for (int i = ; i < ; i++ )
{
//讀取Socket中的數據讀到的數據放在inPacket所封裝的字節數組裡
socketreceive(inPacket)
//判斷inPacketgetData()和inBuff是否是同一個數組
Systemoutprintln(inBuff == inPacketgetData())
//將接收到的內容轉成字符串後輸出
Systemoutprintln(new String(inBuff
inPacketgetLength()))
//從字符串數組中取出一個元素作為發送的數據
byte[] sendData = books[i % ]getBytes()
//以指定字節數組作為發送數據以剛接受到的DatagramPacket的
//源SocketAddress作為目標SocketAddress創建DatagramPacket
outPacket = new DatagramPacket(sendData
sendDatalength inPacketgetSocketAddress())
//發送數據
socketsend(outPacket)
}
}
//使用finally塊保證關閉資源
finally
{
if (socket != null)
{
socketclose()
}
}
}
public static void main(String[] args)
throws IOException
{
new UdpServer()init()
}
}
上面程序中粗體字代碼就是使用DatagramSocket發送接收DatagramPacket的關鍵代碼該程序可以接受個客戶端發送過來的數據
[] []
From:http://tw.wingwit.com/Article/program/Java/hx/201311/27259.html