收到大约1000个UDP DatagramPackets后,Java程序冻结10秒钟,然后再次恢复

时间:2017-01-11 14:24:41

标签: java sockets udp

我有一台运行Unity C#应用程序的PC,每隔几毫秒就通过网络向其他机器发送一个UDP数据包(嵌入Windows 7并运行相同JAVA程序的2台KUKA机器人,它有一个i5英特尔处理器,所以功能非常强大)。 Java程序应该接收这些数据包,解析它们的内容(机器人位置,编码为由'#'分隔的7个值的数组),再次移动和读取。问题是,当PC以每1 0.02秒的速率发送数据包时(这不会发生在0.03或更高,这是硬件限制?!),java程序会冻结大约1000个收到的数据包(有时是955或986等)8-10秒,然后再次恢复。它到达2000年和3000时也是如此 该程序冻结在:

serverSocket.receive(receivedPacket); // receives the array of Bytes

我怀疑网络切换,因此将PC直接连接到机器人,但没有任何改变。奇怪的是,它发生在两个机器人的同时,这让我怀疑PC。但是,当我的同事启动一个实时显示C#程序发送数据包的控制台时,它在java程序被冻结时没有冻结,看起来这些数据包丢失了。
我在SO上寻找类似的问题,很多可疑缓冲区,所以我正在考虑创建一个侦听UDP端口的线程并将数据包存储在内存中的队列中,然后我的主java程序从该线程读取。它看起来像一条可行的轨道吗? 欢迎任何建议。

P.S。这是代码:

package readers;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyProgram {
    // Network variables 
    public static DatagramSocket serverSocket;
    private static DatagramPacket receivedPacket; 

    // Received data variables
    private static byte[] aReceivedData = new byte[1024];
    private static String sReceivedData;
    private static String sAxesInformationReceived;
    private static Double[] dAxesInformationReceived = new Double[7]; 

    // ******** MAIN ***************************************
    public static void main(String[] args) throws  Exception {  
        int mFramecount =0;
        int mPort = 30004; //default value
        int mTimeout = 20*1000; //default value
        int mFramelimit = (15 * 1000); //default value

        // Create UDP server socket
        try {
            serverSocket = new DatagramSocket(mPort);
            serverSocket.setReuseAddress(true);
            serverSocket.setSoTimeout(mTimeout);
        } catch (SocketException e) 
        {   System.err.println("socket bind fail"); closeSocket();e.printStackTrace(); return; }

        // Receive the UDP packet   
        try {
            receivedPacket = new DatagramPacket(aReceivedData, aReceivedData.length);
            serverSocket.receive(receivedPacket); // receive the array of Bytes
        } catch (Exception e) { closeSocket();  return; }  
        //Clear Buffer  
        for (int i = 0; i < 7; i++) {
            if(dAxesInformationReceived[i] == null)
            {
                dAxesInformationReceived[i] = 0.0;
            }
        }


        // <<<<<<<<<<<WHILE <<<<<<<<<<<<<<<<<<<< 
        while (true) {          

            //Clear Buffer
            for(int i=0; i < aReceivedData.length; i++)
            {
                aReceivedData[i]=0;
            }


            // Decoding and Parsing received values
            try {

                receivedPacket = new DatagramPacket(aReceivedData, aReceivedData.length); 
                serverSocket.receive(receivedPacket); // receive the array of Bytes

                byte[] byteData = new byte[receivedPacket.getLength()];
                System.arraycopy(receivedPacket.getData(), receivedPacket.getOffset(), byteData, 0,  receivedPacket.getLength()); 
                sReceivedData = new String(byteData, "UTF-8");   
                Pattern pattern = Pattern.compile("@(.*?)@"); // RegEx
                Matcher matcher = pattern.matcher(sReceivedData); 
                System.out.println("Data: '" + sReceivedData + "', || length: " + byteData.length + "|| Frame count="+ mFramecount ++);

                /*
                 * mFramecount++;
                        if (mFramecount %100 == 0) {
                            System.out.println("Data: '" + sReceivedData + "', || length: " + byteData.length + "|| Frame count="+ mFramecount );
                        }
                 */

                if (matcher.find()) {
                    sAxesInformationReceived = matcher.group(1);
                    String[] sAxesValuesInStringArray = sAxesInformationReceived.split("#");
                    if (sAxesValuesInStringArray.length != 7) {
                        System.err.println("[UnityControl] invalide number of axis");
                        break;
                    }
                    for (int i = 0; i < 7; i++) {
                        dAxesInformationReceived[i] = Double.parseDouble(sAxesValuesInStringArray[i]);
                    }
                } else {
                    System.err.println("[UnityControl] invalid format");
                    break;
                }
            } catch (Exception e) {
                System.err.println("[UnityControl] socket exception");
                e.printStackTrace();
                break;
            }



            /* THIS PART IS USING THE ROBOT's API */
            // Change destination according to the received position
            JointPosition framePos = new JointPosition(
                    Math.toRadians(dAxesInformationReceived[0]),
                    Math.toRadians(dAxesInformationReceived[1]),
                    Math.toRadians(dAxesInformationReceived[2]),
                    Math.toRadians(dAxesInformationReceived[3]),
                    Math.toRadians(dAxesInformationReceived[4]),
                    Math.toRadians(dAxesInformationReceived[5]),
                    Math.toRadians(dAxesInformationReceived[6]));

            try {
                if(runtime.setDestination(framePos)<0) 
                    break; // break when error planning robot motion
            }
            catch(Exception e)
            {
                System.err.println("Runtime exeption");
                break;
            }

            if(mFramecount >= mFramelimit) break;


        }
        // LOOP BACK  
    }

    //**********************************************************************
    static void closeSocket() {
        if (serverSocket != null) {
            serverSocket.disconnect();
            serverSocket.close();
            System.out.println("[UnityControl] socket closed");

        }
    }

}

screenshot

我做了@EJP在他的回答中提出的建议,为了更好地跟踪问题,我在最后添加了数据包的数量,看起来两台机器上的UDP数据包都丢失了(PC的日志)说它并没有停止发送)。这是来自运行相同代码的两台机器的日志:

Log two machines

2 个答案:

答案 0 :(得分:0)

很有可能问题是你正在制作叫做停止世界的东西的GC(垃圾收集)。停止世界冻结应用程序并清除未使用对象的内存:)。您可以获取程序PID,然后连接jconsole以查看您的内存发生了什么。

设置更多内存也有可能有所帮助。 java -Xms1024m -Xmx1024m -Xms设置初始Java堆大小 -Xmx设置最大Java堆大小

如果您使用多个线程,那么创建线程可能会占用大量内存并且非常耗时 - 那么您可以使用线程池。

不幸的是,没有任何代码,我无法帮助你。

答案 1 :(得分:0)

你创造的垃圾多于必要的垃圾。您根本不需要byteArraySystem.arraycopy()

sReceivedData = new String(receivedPacket.getData(), 0, receivedPacket.getLength(), "UTF-8");

您也不需要将同一正则表达式重新编译为Pattern

在接收之前,您也不需要将字节数组归零。

NB 构建setReuseAddress()之后调用DatagramSocket (因此绑定它)完全是浪费时间。