在Java中進行Socket的UDP通信,總是有兩個地方可以指定端口。
一個是在創建DatagramSocket的時候,一個是在創建DatagramPacket的時候。
這兩個端口到底有什麼不同?是不是都需要設置呢?可以設置成一樣的麼?
一番試驗,終於悟出了區別。
簡單的說,DatagramSocket創建時指定的端口是自己這一端的端口,而DatagramPacket創建時指定的端口是對方使用的端口。
例子如下:
class UdpSend {
public static void main(String args[]) throws Exception {
DatagramSocket ds = new DatagramSocket();
//發送端可以使用系統分配的端口,但是接收端回覆消息必須獲取實際端口。
byte[] data = "This is a Message send by UDP.--發送端".getBytes("utf-8");
DatagramPacket dp = new DatagramPacket(data, data.length,
InetAddress.getByName("127.0.0.1"), 2014);
ds.send(dp);
byte[] buf = new byte[1024];
DatagramPacket dpRecv = new DatagramPacket(buf, buf.length);
ds.receive(dpRecv);
InetAddress ip = dp.getAddress();
int port = dpRecv.getPort();
String recvData = new String(dpRecv.getData(),"UTF-8").trim();
System.out.println(ip.getHostAddress()+"::"+port+"::"+recvData);
ds.close();
}
}
/**
* 定義UDP接收端,通常會監聽一個端口,方便於明確哪些數據過來該應用程序可以處理。
* @author Wanglei
*
*/
class UdpRecv{
public static void main(String args[]) throws Exception{
DatagramSocket ds = new DatagramSocket(2014);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
InetAddress ip = dp.getAddress();
int port = dp.getPort();
String data = new String(dp.getData(), 0, dp.getLength(), "UTF-8");
System.out.println(ip.getHostAddress()+"::"+port+"::"+data);
byte[] sendData = "來自接收端的迴應。".getBytes("UTF-8");
DatagramPacket dpSend = new DatagramPacket(sendData, sendData.length, ip, port);
ds.send(dpSend);
ds.close();
}
}
先運行接收端再運行發送端,運行的結果是:
接收端:
E:\JavaStudy\NetDemo\bin>java UdpRecv
127.0.0.1::64904::This is a Message send by UDP.--發送端
發送端:E:\JavaStudy\NetDemo\bin>java UdpSend
127.0.0.1::2014::來自接收端的迴應。
這裏發送端沒有指定DatagramSocket的端口,系統自動分配了一個64904。發送端指定了DatagramPacket的端口是2014,那麼接收端必須指定自己的DatagramSocket的端口是2014,否則是收不到消息的。
下面稍微修改代碼,發送端指定DatagramSocket端口,比如8888,
DatagramSocket ds = new DatagramSocket(8888);
運行結果:
接收端:
E:\JavaStudy\NetDemo\bin>java UdpRecv
127.0.0.1::8888::This is a Message send by UDP.--發送端
發送端:E:\JavaStudy\NetDemo\bin>java UdpSend
127.0.0.1::2014::來自接收端的迴應。
可以看到發送端的端口變成了我們指定的端口號8888,接收端的端口號仍舊是2014。
下面試驗發送端接收端都使用同樣的端口,比如都用端口2014,運行結果,發送端報異常,端口號被佔用。
E:\JavaStudy\NetDemo\bin>java UdpSend
Exception in thread "main" java.net.BindException: Address already in use: Canno
t bind
at java.net.DualStackPlainDatagramSocketImpl.socketBind(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.bind0(DualStackPlainDatagra
mSocketImpl.java:65)
at java.net.AbstractPlainDatagramSocketImpl.bind(AbstractPlainDatagramSo
cketImpl.java:95)
at java.net.DatagramSocket.bind(DatagramSocket.java:376)
at java.net.DatagramSocket.<init>(DatagramSocket.java:231)
at java.net.DatagramSocket.<init>(DatagramSocket.java:284)
at java.net.DatagramSocket.<init>(DatagramSocket.java:256)
at UdpSend.main(NetDemo.java:8)
這個結果是不是表明了收發兩端不能使用同樣的端口嗎?
不是!!
這個運行結果是因爲我們測試代碼在同一臺機器上測試發送端和接收端導致的,那麼端口號當然被佔用啦,這個時候需要稍微修改下代碼,分別把發送端和接收端放到兩臺機器上運行就OK了。
發送端改動部分:假定接收端的IP地址是192.168.0.101,發送端的IP地址是192.168.0.102;
DatagramSocket ds = new DatagramSocket();
DatagramPacket dp = new DatagramPacket(data, data.length,
InetAddress.getByName("192.168.0.101"), 2014);
接收端不用改。
兩臺機器的運行結果:
接收端:
E:\JavaStudy\NetDemo\bin>java UdpRecv
192.168.0.102::2014::This is a Message send by UDP.--發送端
發送端:D:\NetDemo\bin>java UdpSend
192.168.0.101::2014::來自接收端的迴應。
OK了。只要記住,需要發送數據的時候,DatagramSocket指定自己的端口號,DatagramPacket指定對方的端口號。
需要接收數據的時候,必須指定和上面發送端的DatagramPacket一致的端口號,而用來接收數據的DatagramPacket不需要指定端口號。等待數據接收到了,數據自動把端口號填充好了。