UDP端口和DatagramSockets的java问题
我正在从事一个项目,该项目假设使用DatagramPackets和DatagramSockets将文件从一台机器发送到另一台机器。该实现被假定为模仿TCP协议。因此,一旦接收方收到一个数据包,它就会向发送方发回一个ACK,确认该数据包已送达。到目前为止,我的程序没有进行任何确认。我在实现ACK消息时遇到问题。在我的receiver程序中,它显示正在发送ACK,但发送方应用程序没有收到它们
我一直在创建socket时出错。“java.net.BindException:地址已在使用中:无法绑定”。我很困惑,因为发送方应用程序中没有其他地方指定了端口。我只是使用DatagramSocket socket = new DatagramSocket();
但我确实使用
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);
}
}
# 1 楼答案
在我们讨论代码问题之前:为什么客户端试图监听端口49000
如果您还没有意识到这一点:本地端口和对等端口不必相同,通常也不必相同。调用
DatagramSocket()
时,操作系统会分配一个任意的本地端口。您发送到49000的事实不会改变您的本地端口。如果连接的另一端只是将它接收到的数据包发送回元组,它不会到达49000,而是会到达您的本地端口如果这是您的问题,修复方法是使用第二个版本(只需使用现有套接字进行监听和发送),然后修复另一侧(您尚未向我们显示代码),将ACK发送到数据包发送方的完整地址元组,而不是发送方主机上的端口49000
如果你意识到了这一点,但是出于某种原因,你认为双方都需要本地端口49000……嗯,他们可能不需要。一般来说,协议需要一方(“服务器”)有一个众所周知的端口来连接,但另一方(“客户机”)不需要。这就是为什么在客户机上可以使用
DatagramSocket()
而不是DatagramSocket(49000)
的原因,而且一切正常同样的修正
在极少数情况下,双方确实需要一个众所周知的端口(例如,这样你就可以在公司的内部防火墙中显式地打开它),你几乎肯定希望发送到的操作也发生在该端口上
因此,与其创建一个
DatagramSocket()
来发送,或者创建一个DatagramSocket(48000)
来监听,不如首先创建一个DatagramSocket(48000)
并将其用于两者但是,请注意,与任何使用固定端口的解决方案一样,此解决方案还有两个问题:
首先,如果客户端和服务器都想绑定端口48000,那么它们不能同时在同一台机器上运行。你可以将其中一个重新编号为48001,或者直接接受
第二,如果您希望频繁启动和停止客户端,它通常会尝试绑定端口49000,而操作系统仍然有一个端口处于} 的目的;使用它
TIME_WAIT
状态的套接字,因此您将得到一个绑定错误。这就是^{如果您真的想在客户端上使用任意端口发送器,而不是固定端口侦听器,该怎么办?在某些情况下,这是有道理的,但除非你能解释为什么你真的需要这个,否则你没有
如果你做到了,那么,也只有到那时,你才能使用像你的第一个版本一样的东西。但是你仍然想创建一次监听器套接字,而不是每次你监听ACK;它应该是与发送套接字不同的属性。(当然,你仍然需要处理与上一节相同的事情。)
如果你真的想为每个ACK创建一个新的侦听器套接字,那么你必须确保立即关闭它,而不是等待Java GC和操作系统一起为你关闭它,或者下次你等待ACK时,你可能会得到一个绑定错误,因为旧的侦听器套接字仍然绑定到它
# 2 楼答案
尝试使用netstat命令检查端口上是否有其他程序(甚至您的程序)处于活动状态。在unix netstat-lp上,正如su将向您展示的那样,在windows netstat上也存在不同的命令行选项