【问题标题】:Java DatagramChannel hangsJava DatagramChannel 挂起
【发布时间】:2014-11-01 15:17:28
【问题描述】:

我正在开始一些 Java NIO 编程,并且正在构建两个独立的 Java 程序。一种是创建一些随机数据通过 UDP 发送,另一种是接收这些数据并可能对其进行一些处理。

在每个类中,我定义了要发送/接收的数据的大小,以及一次发送或接收的字节数(每个数据包)为 8 个字节。当我的 SIZE 为 1024 字节时,我可以打开接收器(默认情况下等待某些数据包到达),然后打开发送数据包的发送器,接收器正确接收这些数据包并在所有数据包结束时终止数据包被接收。然后我尝试将 SIZE 设置为 10 兆字节。我启动接收器,然后启动发送器。发件人似乎工作正常,几秒钟后终止。接收器开始工作并继续读取一些数据包(我在代码周围放置了一些 printlns)。然而,在这个过程的某个地方,接收器似乎挂起并停止接收更多的数据包。我不知道挂起发生在哪里,除了它发生在缓冲区被清除以进行新读取之后。

也许我错误地使用了库。非常感谢任何帮助。

发送者类称为UDPSender,如下所示:

    public class UDPSender {

    public static void main(String[] args) {
        try {
            // Generate some data to send
            int SIZE=1024*1024*10;
            int bufferBytes = 8;

            byte[] b = new byte[SIZE];
            new Random().nextBytes(b);
            String value = new String(b);
            //System.out.println(value);

            // Create Datagram channel for sending UDP packets
            DatagramChannel sendingChannel = DatagramChannel.open();
            // Bind the channel to a specific sending socket address and port
            InetSocketAddress sendSocket = new InetSocketAddress("127.0.0.1", 9999);
            sendingChannel.socket().bind(sendSocket);

            //Target socket address
            InetSocketAddress targetSocket = new InetSocketAddress("127.0.0.1", 8888);
            //loop to send bufferBytes bytes at a time
            int location = 0;
            int bytesSent = 0;
            long startTime=0; long endTime=0; long elapsedTime=0;  // for speed benchmarking
            startTime = System.nanoTime();
            for (int i = 1; i <= SIZE / bufferBytes; i++) {
                //System.out.println(i);
                ByteBuffer buf = ByteBuffer.allocate(bufferBytes);
                buf.clear();
                buf.put(Arrays.copyOfRange(b, location, location + bufferBytes));
                location = location + bufferBytes;
                buf.flip();
                bytesSent = bytesSent + sendingChannel.send(buf, targetSocket);
            }
            endTime = System.nanoTime();
            elapsedTime = endTime - startTime;
            double elapsedTimeMs = elapsedTime / 1000000.0;
            System.out.println("Elapsed:" + elapsedTimeMs + " msec");
        } catch (IOException ex) {
            Logger.getLogger(UDPSender.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

第二个类叫做UDPReceiver,如下图:

    public class UDPReceiver {
    public static void main(String[] args) {
        try {
            int SIZE=1024*1024*10;
            int bufferBytes = 8;
            int packet_max = SIZE/bufferBytes;

            // Create Datagram channel for receiving UDP packets
            DatagramChannel receivingChannel = DatagramChannel.open();
            // Bind the channel to a specific receiving socket address and port
            InetSocketAddress receiveSocket = new InetSocketAddress("127.0.0.1",8888);
            receivingChannel.socket().bind(receiveSocket);
            // Set up address of send
            InetSocketAddress sendSocket = new InetSocketAddress("127.0.0.1",9999);
            // Connect our receiving socket to their sending socket address
            receivingChannel.connect(sendSocket);

            long startTime=0; long endTime=0; long elapsedTime=0;  // for speed benchmarking
            byte[] b = new byte[SIZE]; //receiving storage
            int packets=0; int location=0; int bytesCount; //byte[] indexes
            boolean clockStart=false;


            //try indirect ByteBuffer
            System.out.println("Indirect ByteBuffer()");
            ByteBuffer buf = ByteBuffer.allocate(bufferBytes);
            packets=0; location=0; bytesCount = 0; clockStart=false;
            while ((bytesCount = receivingChannel.read(buf)) > 0) { // Read data from file into ByteBuffer
                System.out.println(".");
                if(clockStart==false){
                    startTime = System.nanoTime();
                    clockStart=true;
                }
                // flip the buffer which set the limit to current position, and position to 0.
                buf.flip(); System.out.println("..");
                System.arraycopy(buf.array(), 0, b, location, buf.capacity()); System.out.println("..."); // Write data from ByteBuffer to bytearray
                location=location+bufferBytes; System.out.println("....");
                buf.clear();  System.out.println(".....");    // For the next read
                packets = packets+1; System.out.println("......");
                if(packets==packet_max){
                    System.out.println(".......");
                    System.out.println(packets);
                    break;
                }
            }
            endTime = System.nanoTime();
            elapsedTime = endTime - startTime;
            System.out.println("ByteBuffer() Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");

        } catch (IOException ex) {
            System.out.println(ex.toString());
            //Logger.getLogger(UDPReceiver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

【问题讨论】:

  • 为什么是 8 个字节? 8 字节的有效负载和 28 字节的 UDP 标头开销没有多大意义。
  • 嗨@EJP - 感谢您的阅读。我对 8 个字节没有任何特别的理由。这只是我尝试了解 UDP、NIO 等的一个示例。即使使用更大的有效负载,例如64 字节。
  • 调试后的一些额外细节:我通过插入 Thread.sleep(1); 减慢了 UDPSender 速率;在每个发送的数据包之后,现在接收器能够完成整个循环。似乎是一次性可以接受的UDP数据包突发的限制。

标签: java udp nio channel bytebuffer


【解决方案1】:

它不会“挂起”,它阻塞, 这样做是因为您试图读取特定数量的数据包,而不考虑 UDP 不可靠的事实。当您丢失数据包时,您的发送者和接收者都不知道,因此您的发送者不会重新发送,并且您的接收者会继续尝试读取全部数量的数据包,而这些数据包永远不会到达。

【讨论】:

  • 这很有意义,谢谢!当我放慢传输速度时,正在接收所有数据包,并且随着传输速度加快,一些数据包丢失或未及时收到,因此正如您所说,该过程一直在等待更多(确切数量)数据包自从他们迷路以来,那永远不会到达。附带问题 - 您认为将 Java 套接字用于高数据速率是否现实? 10Gb/s 传入数据包?或者,直接在以太网接口上使用某种形式的原始数据包嗅探也许是个好主意。 Java可以做到这一点吗?
  • 它与 Java 无关。问题是这样使用UDP是否合适,答案是否定的。如果您需要接收特定数量的数据包,请使用 TCP。如果你能承受丢失数据包的负担,并且你可以重写你的代码而不是期望它们的数量是固定的,那么一定要使用 UDP。
  • 在上面的这个例子中,我只是测试数据包是否丢失以及我可以达到的传输速度。我正在考虑一个应用程序,我可以承受丢失一些数据包而不会产生任何后果,但同时想要更快的传输速度,据我所知,由于 UDP 实现要简单得多,因此如果你'愿意接受诸如丢包之类的限制。另一个问题是 Java 是否可以用于读取以太网接口上的原始帧(绕过 TCP/IP 堆栈)。我正在阅读一个名为 Jpcap 的库
  • 如果你问Jpcap在读取原始数据包时是否会丢包,答案是肯定的。
  • 毫无疑问,它们仍然是UDP数据包,所以会有损失。通过绕过整个网络堆栈并访问原始数据包来提高速度怎么样?
猜你喜欢
  • 1970-01-01
  • 2012-05-11
  • 2012-08-23
  • 1970-01-01
  • 2017-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-31
相关资源
最近更新 更多