有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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);
    }
}

共 (2) 个答案

  1. # 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. # 2 楼答案

    尝试使用netstat命令检查端口上是否有其他程序(甚至您的程序)处于活动状态。在unix netstat-lp上,正如su将向您展示的那样,在windows netstat上也存在不同的命令行选项