[英]Trouble with UDP ports and DatagramSockets
我正在开发一个项目,该项目应该使用DatagramPackets和DatagramSockets将文件从一台计算机发送到另一台计算机。 假定实现是模仿TCP协议的。 因此,一旦接收方收到数据包,它将向发送方发回ACK,以确认数据包已交付。 到目前为止,我的程序尚未对ACK进行任何检查。 我在执行ACK消息时遇到问题。 在我的接收器程序上,它表明正在发送ACK,但发送器应用程序未接收到它们。
我不断从创建套接字中得到一个错误。 “ java.net.BindException:地址已在使用中:无法绑定”。 我很困惑,因为发送方应用程序中的其他任何地方都没有指定端口。 我只使用DatagramSocket socket = new DatagramSocket();
但我确实使用DatagramPacket packet = new DatagramPacket(packetData, packetData.length, internetAddress, 49000); socket.send(packet);
DatagramPacket packet = new DatagramPacket(packetData, packetData.length, internetAddress, 49000); socket.send(packet);
发送数据包时。
我尝试在我的waitForAck()方法中删除数据报声明,并使用了与发送数据包相同的datagramSocket。 但是socket.receive(packet);
会挂起并且永远不会收到任何东西,因为它尚未分配端口进行监听。
这是我侦听ACK的方法:
public void waitForACK(){
//listen for ack for a period of time
//if ACK received, then break send next packet
//if ACK not received or time out, send last packet
//TODO: implement a timeout
System.out.println("### Sender waiting for ACK");
try {
DatagramSocket receivingSocket = new DatagramSocket(49000);
while (!ACKreceived) {
byte[] buf = new byte[1500]; // Actual Ethernet packet size is 1500 bytes
// receive request
DatagramPacket packet = new DatagramPacket(buf, buf.length);
receivingSocket.receive(packet); //socket.receive(packet); <--
byte[] packetData = Arrays.copyOf(packet.getData(), packet.getLength());
ACKreceived = checkACK(packetData);//check the recieved packet contains an ACK message
}
System.out.println("### Sender recieved ACK");
} catch (Exception e) {
System.out.println("### never got ACK");
System.out.println(e);
}
}
我也尝试过此方法,但该壁挂程序会挂起并且永远不会收到任何东西。 即使接收文件的应用程序成功报告发送了ACK。 我猜是因为它不知道在端口49000上收到ACK。
public void waitForACK(){
//listen for ack for a period of time
//if ACK received, then break send next packet
//if ACK not received or time out, send last packet
//TODO: implement a timeout
System.out.println("### Sender waiting for ACK");
try {
while (!ACKreceived) {
byte[] buf = new byte[1500]; // Actual Ethernet packet size is 1500 bytes
// receive request
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet); //<--- HANGS RIGHT HERE
byte[] packetData = Arrays.copyOf(packet.getData(), packet.getLength());
ACKreceived = checkACK(packetData);//check the recieved packet contains an ACK message
}
System.out.println("### Sender recieved ACK");
} catch (Exception e) {
System.out.println("### never got ACK");
System.out.println(e);
}
}
您正在泄漏套接字。
不要仅为了等待ACK而创建新的套接字。 在应用程序的生命周期中,应该只打开一个DatagramSocket
。
尝试使用netstat命令检查端口上是否有其他程序(甚至您的程序)处于活动状态。 在su上,su将显示在unix netstat -lp上,而在Windows上netstat也存在,但命令行选项不同
在我们讨论您的代码问题之前:为什么客户端试图在端口49000上侦听?
如果您尚未意识到这一点:本地端口和对等端口不必相同,通常就不必相同。 调用DatagramSocket()
,将获得操作系统分配的任意本地端口。 您发送到49000的事实不会更改您的本地端口。 而且,如果连接的另一端只是发送回元组,它从中接收到一个数据包,该数据包不会到达49000,它将到达您的本地端口。
如果这是您的问题,则解决方法是使用第二个版本(只需使用现有的套接字来侦听和发送),然后修复另一端(您尚未向我们显示代码)将ACK发送至数据包发送方的完整地址元组,而不是发送方主机上的端口49000。
如果您意识到这一点,但由于某种原因认为双方都需要拥有本地端口49000,那么,他们可能没有。 通常,协议需要一侧(“服务器”)具有众所周知的端口进行连接,而另一侧(“客户端”)则不需要。 这就是为什么您可以在客户端上使用DatagramSocket()
而不是DatagramSocket(49000)
且工作正常的原因。
再次,相同的修复程序。
在极少数情况下,双方实际上都确实需要一个众所周知的端口(例如,因此您可以在公司的内部防火墙中显式打开它),您几乎可以肯定希望发送也发生在该端口上。
因此,而不是创建一个DatagramSocket()
用于发送和DatagramSocket(48000)
上侦听,只需创建一个DatagramSocket(48000)
摆在首位,并用其来进行。
但是,请注意,此解决方案与使用固定端口的任何解决方案一样,还有两个其他问题:
首先,如果客户端和服务器都希望绑定端口48000,则它们都不能在同一台计算机上运行。 您可以将其中之一重新编号为48001,也可以接受。
其次,如果您希望频繁启动和停止客户端,则通常会尝试绑定端口49000,而OS仍具有处于TIME_WAIT
状态的该端口的套接字,因此您将遇到绑定错误。 这就是SO_REUSEADDR
目的; 用它。
如果您确实确实想在客户端上使用任意端口发送器,但使用固定端口侦听器怎么办? 在某些情况下,这是有道理的,但是除非您能解释为什么真正需要它,否则您将没有理由。
如果这样做,那么,只有那样,您才能使用类似您的第一个版本的东西。 但是您仍然可能想只创建一次侦听器套接字,而不是每次都侦听ACK。 它应该是与发送套接字不同的属性。 (当然,您仍然需要处理与上一节相同的内容。)
而且,如果您确实确实想为每个ACK创建一个新的侦听器套接字,则必须确保立即关闭它,而不是等待Java GC和OS共同为您关闭它,或者在下一次关闭它。当您等待ACK时,您很可能会遇到绑定错误,因为旧的侦听器套接字仍绑定到该错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.