在Java中在协议层提取数据的最难痛的方法是什么?

时间:2013-03-31 20:06:42

标签: java network-programming byte

我正在尝试实现一个Android应用程序来连接到开源软件Motion。目标是能够检查应用程序的状态并获取最后拍摄的图像。

我不是用Java编程,我的背景主要是C和Python。我对理解Android的UI部分没有任何实际问题,但我发现使用任何类型的字节缓冲区都非常痛苦。 Motion软件具有非常简单的HTTP API。在Java中打开URL连接很容易。默认页面的响应如下所示

Motion 3.2.12 Running [4] Threads
0
1
2
3

就我的目的而言,应用程序需要做的第一件事是解析线程数。在某些时候,我也可以从第一行检索版本号,但这在目前并不重要。

这是我的代码

package com.hydrogen18.motionsurveillanceviewer;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import java.util.List;

public class MotionHttpApi {
    String host;
    int port = 80;
    boolean secure = false;

    int numberOfThreads = -1;

    String getBaseUrl()
    {
        StringBuilder sb = new StringBuilder();

        sb.append(secure ? "https://" : "http://");
        sb.append(host);
        sb.append(':');
        sb.append(port);

        return sb.toString();
    }

    public int getNumberOfCameras() throws IOException
    {
        if(numberOfThreads == -1)
        {
            retrieveSplash();
        }

        if(numberOfThreads == 1)
        {
            return 1;
        }
        return numberOfThreads - 1;
    }
    void retrieveSplash () throws IOException
    {
        URL url = new URL(getBaseUrl());

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();

        if(conn.getResponseCode()!=HttpURLConnection.HTTP_OK)
        {
            throw new IOException("Got response code" + conn.getResponseCode());
        }

        try{
            Byte[] buffer = new Byte[512];
            byte[] sbuf = new byte[128];
            int offset = 0;
            InputStream in = new BufferedInputStream(conn.getInputStream());

            boolean foundInfoString= false;
            while( ! foundInfoString)
            {
                //Check to make sure we have not run out of space
                if(offset == buffer.length)
                {
                    throw new IOException("Response too large");
                }

                //Read into the smaller buffer since InputStream
                //can't write to a Byte[]
                final int result = in.read(sbuf,0,sbuf.length);

                //Copy the data into the larger buffer
                for(int i = 0; i < result;++i)
                {
                    buffer[offset+i] = sbuf[i];
                }
                //Add to the offset
                offset+=result;

                //Wrap the array as a list
                List<Byte> list = java.util.Arrays.asList(buffer);  

                //Find newline character
                final int index = list.indexOf((byte) '\n');

                //If the newline is present, extract the number of threads
                if (index != -1)
                {
                    //Find the number of threads
                    //Thread number is in the first lin like "[X]"
                    final int start = list.indexOf((byte)'[');
                    final int end = list.indexOf((byte)']');

                    //Sanity check the bounds
                    if(! (end > start))
                    {
                        throw new IOException("Couldn't locate number of threads");
                    }

                    //Create a string from the Byte[] array subset
                    StringBuilder sb = new StringBuilder();
                    for(int i = start+1; i != end; ++i)
                    {                   
                        final char c = (char) buffer[i].byteValue();
                        sb.append(c);
                    }
                    String numThreadsStr = sb.toString();

                    //Try and parse the string into a number
                    try
                    {
                        this.numberOfThreads = Integer.valueOf(numThreadsStr);
                    }catch(NumberFormatException e)
                    {
                        throw new IOException("Number of threads is NaN",e);
                    }

                    //No more values to extract
                    foundInfoString = true;
                }

                //If the InputStream got EOF and the into string has not been found
                //Then an error has occurred. 
                if(result == -1 && ! foundInfoString )
                {
                    throw new IOException("Never got info string");
                }
            }

        }finally
        {
            //Close the connection
            conn.disconnect();
        }

    }

    public MotionHttpApi(String host,int port)
    {
        this.host = host;
        this.port = port;
    }
}

当您致电getNumberOfCameras()时,代码工作正常。但我认为我不能真正理解java的东西,因为retrieveSplash方法太复杂了。我可以在10行左右的C行或1行Python中做同样的事情。当然,必须有一种更健全的方法来操纵java中的字节?

我认为有一些样式问题,比如每当整数无法解析时我可能不应该抛出IOException。但这是一个单独的问题。

2 个答案:

答案 0 :(得分:1)

按照Gautam Tandon的建议阅读第一行,然后使用正则表达式。 然后,您可以检查正则表达式是否匹配,甚至可以轻松提取数字。

正则表达式'可以在http://txt2re.com创建。我已经为你做过了。 该页面甚至可以创建Java,Pyhton,C等文件供您使用。

// URL that generated this code:
// http://txt2re.com/index-java.php3?s=Motion%203.2.12%20Running%20[4]%20Threads&-7&-19&-5&-20&-1&2&-22&-21&-62&-63&15 

import java.util.regex.*;

class Main
{
  public static void main(String[] args)
  {
    String txt="Motion 3.2.12 Running [4] Threads";

    String re1="(Motion)";  // Word 1
    String re2="( )";   // White Space 1
    String re3="(3\\.2\\.12)";  // MMDDYY 1
    String re4="( )";   // White Space 2
    String re5="(Running)"; // Word 2
    String re6="( )";   // White Space 3
    String re7="(\\[)"; // Any Single Character 1
    String re8="(\\d+)";    // Integer Number 1
    String re9="(\\])"; // Any Single Character 2
    String re10="( )";  // White Space 4
    String re11="((?:[a-z][a-z]+))";    // Word 3

    Pattern p = Pattern.compile(re1+re2+re3+re4+re5+re6+re7+re8+re9+re10+re11,Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
    Matcher m = p.matcher(txt);
    if (m.find())
    {
        String word1=m.group(1);
        String ws1=m.group(2);
        String mmddyy1=m.group(3);
        String ws2=m.group(4);
        String word2=m.group(5);
        String ws3=m.group(6);
        String c1=m.group(7);
        String int1=m.group(8);
        String c2=m.group(9);
        String ws4=m.group(10);
        String word3=m.group(11);
        System.out.print("("+word1.toString()+")"+"("+ws1.toString()+")"+"("+mmddyy1.toString()+")"+"("+ws2.toString()+")"+"("+word2.toString()+")"+"("+ws3.toString()+")"+"("+c1.toString()+")"+"("+int1.toString()+")"+"("+c2.toString()+")"+"("+ws4.toString()+")"+"("+word3.toString()+")"+"\n");
    }
  }
}

//-----
// This code is for use with Sun's Java VM - see http://java.sun.com/ for downloads. 
//
// Paste the code into a new java application or a file called 'Main.java'
//
// Compile and run in Unix using:
// # javac Main.java 
// # java Main 
//

String int1=m.group(8);为您提供所需的整数。当然,您可以简化上面的代码。这是现在详细说明的方式。

答案 1 :(得分:0)

您可以使用BufferedReader大大简化retrieveSplash方法。这是一个更简单的函数版本:

void retrieveSplash_simpler() throws IOException {
    URL url = new URL(getBaseUrl());
    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
    // open the connection
    conn.connect();
    // create a buffered reader to read the input stream line by line
    BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

    // find number of threads
    String firstLine = reader.readLine();
    int x = firstLine.indexOf("[");
    int y = firstLine.indexOf("]");
    if (x > 0 && y > 0 && x < y) {
        try {
            numberOfThreads = Integer.parseInt(firstLine.substring(x+1, y));
        } catch (NumberFormatException nfe) {
            // disconnect and throw exception
            conn.disconnect();
            throw new IOException("Couldn't locate number of threads");
        }
    } else {
        // disconnect and throw exception
        conn.disconnect();
        throw new IOException("Couldn't locate number of threads");
    }

    // disconnect
    conn.disconnect();
}

我通过在适当的位置使用try / catch / finally块来进一步清理上面的方法,这样我就不必复制“conn.disconnect()”了。但是我没有这样做,以保持简单(尝试/捕获/最终有时变得棘手......)。