繁体   English   中英

UDP端口和DatagramSockets出现问题

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM