在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不需要指定端口号。等待数据接收到了,数据自动把端口号填充好了。