Jack
Jack

Reputation: 9242

Android socket programming ... what gives?

So I've scoured the net (mostly SO, google) to find good examples of socket programming with Android. I've done lots of Android dev (just not with sockets). I dont understand why my readLine() ALWAYS RETURNS NULL. And please ignore the hideous code, this is a quick and dirty prototype for a friend. My overall goal is to establish a connection to a server, send header data (GET /MOUNTPOINT Content-Type: ... etc...), and receive a response, based on the response I need to either continue the stream or close it. Here is my most recent attempts code:

                    String userPass = new     String(Base64.encodeToString("user:password".getBytes(),     Base64.DEFAULT));
            boolean connected = false;
            String requestmsg = "GET /MOUNTPOINT" + " HTTP/1.0\r\n";
            requestmsg += "User-Agent: MYCUSTOMAGENT\r\n";
            requestmsg += "Accept: */*\r\n";
            requestmsg += "Connection: close\r\n";

            requestmsg += "Authorization: Basic " + userPass;

            requestmsg += "\r\n";
            DataOutputStream dos =  null;
            DataInputStream dis = null;
            try {

                Log.d("ClientActivity", "Connecting...");
                Socket socket = new Socket("1.2.3.4", 9000);
                connected = true;
                String data = "";

                while (connected) {
                    try {
                        Log.d("ClientActivity", "C: Sending command.");

                        dos = new DataOutputStream(socket.getOutputStream());
                        dis = new DataInputStream(socket.getInputStream());

                        dos.write(requestmsg.getBytes());
                        Log.i("ClientActivity", "RequestMsg Sent");

                        StringBuilder sb = new StringBuilder();

                        while ((data = dis.readLine()) != null)
                            {
                                sb.append(data);
                            }
                            Log.i("ClientActivity", "C: Sent.");
                    } catch (Exception e) {
                        Log.e("ClientActivity", "S: Error", e);

                    }
                }
                socket.close();
                Log.d("ClientActivity", "C: Closed.");
            } catch (Exception e) {
                Log.e("ClientActivity", "C: Error", e);
                connected = false;
            }

I've tried using the HttpClient API but it inserts a "GET /" message in the header and I need to specify "GET /MOUNTPOINT". Plus I don't know if HttpClient allows streaming. Ive also tried URLConnection, but also it inserts either GET or POST by default. Thanks for any help!

Also I have tried the following:

SocketAddress sockaddr = new InetSocketAddress("1.2.3.4", 9000);
nsocket = new Socket();

nsocket.connect(sockaddr, 10 * 1000); // 10 second connection timeout
    if (nsocket.isConnected()) {
        //nsocket.setSoTimeout(20 * 1000); // 20 second timeout once data is flowing
        nis = nsocket.getInputStream();
        nos = nsocket.getOutputStream();
        Log.i(NTAG, "Socket created, streams assigned");
        // Build request message
        requestmsg = "GET /MOUNTPOINT" + " HTTP/1.0\r\n";
        requestmsg += "User-Agent: MYANDROIDAGENT\r\n";
        requestmsg += "Accept: */*\r\n";
        requestmsg += "Connection: close\r\n";
        requestmsg += "Authorization: Basic " + userPass;
        requestmsg += "\r\n";

        nos.write(requestmsg.getBytes());


        nos.write(genGPGGA().getBytes());
        Log.i(NTAG, "Waiting for inital data...");
        byte[] buffer = new byte[4096];
        int read = nis.read(buffer); // This is blocking
        //Log.i("ReadNTRIP", tempdata.toString());
        String test = "TESTING";

        while (read != -1) {
            tempdata = new byte[read];
            System.arraycopy(buffer, 0, tempdata, 0, read);
            read = nis.read(buffer, 0, 4096); // This is blocking

        }
    }
} catch (SocketTimeoutException ex) {
    ex.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    try {
        nis.close();
        nos.close();
        nsocket.close();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    }
    Log.i(NTAG, "Finished");

}

But read always returns -1. Im ready to pull my hair out.

EDIT: I added a bounty (worth very little) - I don't have much rep to give, but this problem has become a show stopper for me. To get the bounty, write a simple android program to connect to thor.lsu.edu, port 9000, send a simple GET / HTTP/1.0 command and actually get back data (using sockets - cannot be with URLConnection or HttpClient). Post the program and a screenshot of the results. I've tried it so many different ways I don't know what else to do (using InputStreams, BufferedInputStreams, InputStreamReaders, etc...). I've even used an example Android program (THAT WORKS), yet I still don't get back data in my implementation. I don't know if its a timing issue or what.

Thanks

Edit: I've noticed that I cannot even browse to http://thor.lsu.edu:9000 in Androids browser. It gives a data connectivity problem. Any idea why this port may be blocked?

Upvotes: 3

Views: 5409

Answers (2)

Justin Breitfeller
Justin Breitfeller

Reputation: 13801

All I did was a few modifications to your code. I think the major change was just not using the DataInputStream since the documentation explicitly says to not use readLine() anymore on this data structure. http://download.oracle.com/javase/1.4.2/docs/api/java/io/DataInputStream.html#readLine%28%29

I also remove the authorization stuff since it wasn't required. If you run this code somewhere, you will notice the log printing out the HTTP response.

String requestmsg = "GET /MOUNTPOINT" + " HTTP/1.0\r\n";
    requestmsg += "User-Agent: MYCUSTOMAGENT\r\n";
    requestmsg += "Accept: */*\r\n";
    requestmsg += "Connection: close\r\n";

    // requestmsg += "Authorization: Basic " + userPass;

    requestmsg += "\r\n";
    DataOutputStream dos = null;
    BufferedReader dis = null;
    try {

        Log.d("ClientActivity", "Connecting...");
        Socket socket = new Socket("thor.lsu.edu", 9000);
        String data = "";

        try {
            Log.d("ClientActivity", "C: Sending command.");

            dos = new DataOutputStream(socket.getOutputStream());
            dis = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            dos.write(requestmsg.getBytes());
            Log.i("ClientActivity", "RequestMsg Sent");

            StringBuilder sb = new StringBuilder();

            while ((data = dis.readLine()) != null) {
                sb.append(data);
            }
            Log.i("ClientActivity", "C: Sent.");
            Log.i("ClientActivity", "C: Received " + sb.toString());
        } catch (Exception e) {
            Log.e("ClientActivity", "S: Error", e);

        }

        socket.close();
        Log.d("ClientActivity", "C: Closed.");
    } catch (Exception e) {
        Log.e("ClientActivity", "C: Error", e);
    }

Upvotes: 9

Femi
Femi

Reputation: 64700

Odd. I'd really suggest just using URLConnection with a URL http://1.2.3.4:9000/MOUNTPOINT. That should do what you want. The null readline is probably because your handcrafted http request is not being understood by the other side.

HttpClient allows streaming, you just have to implement a fairly substantial chunk of code to handle it.

EDIT: Interesting. I look at it in wget (my go-to HTTP debugging tool) and see this:

C:\Documents and Settings\Olufemi Omojola>wget -O - http://130.39.148.171:9000/
--21:31:55--  http://130.39.148.171:9000/
           => `-'
Connecting to 130.39.148.171:9000... connected.
HTTP request sent, awaiting response... 200 No headers, assuming HTTP/0.9
Length: unspecified

    [<=>                                                                          ] 0             --.--K/s             S
OURCETABLE 200 OK
Server: NTRIP Trimble NTRIP Caster
Content-Type: text/plain
Content-Length: 631
Date: 10/Jul/2011:01:31:37 UTC

STR;CMRp_All;CMRp_All;CMR+;1(1),3(10),18(1),19(1);2;GPS;GULFnet;USA;30.84;268.81;1;1;Trimble GPSNet;None;B;N;0;;
STR;RTCM2_3All;RTCM2_3All;RTCM 2.3;1(1),3(10),18(1),19(1);2;GPS;GULFnet;USA;30.84;268.81;1;1;Trimble GPSNet;None;B;N;0;;

STR;GLNnorthLA;GLNnorthLA;RTCM 3;1004(1),1005/1007(5),PBS(10);2;GPS+GLONASS;GULFNet;USA;0;0;1;1;Trimble GPSNet;None;B;N;
0;;
STR;GLNnoLA_CMRp;GLNnoLA_CMRp;CMR+;1004(1),1005/1007(5),PBS(10);2;GPS+GLONASS;GULFNet;USA;0;0;1;1;Trimble GPSNet;None;B;
N;0;;
STR;GLNseLA_CMRp;GLNseLA_CMRp;CMR+;1004(1),1005/1007(5),PBS(10);2;GPS+GLONASS;GULFNet;USA;0;0;1;1;Trimble GPSNet;None;B;
N;0;;
ENDSOURCETABLE
    [ <=>                                                                         ] 768           --.--K/s

21:31:55 (30.13 MB/s) - `-' saved [768]

C:\Documents and Settings\Olufemi Omojola>wget -O - http://130.39.148.171:9000/CMRp_All --http-user=user --http-password
=pass
--21:31:31--  http://130.39.148.171:9000/CMRp_All
           => `-'
Connecting to 130.39.148.171:9000... connected.
HTTP request sent, awaiting response... 401 Unauthorized
Authorization failed.

The lack of response headers may be the reason URLConnection fails. If you continue to get Unauthorized responses then the Authorization header you're sending may be wrong: for Basic auth, I do this.

httpost.addHeader("Authorization", "Basic " + Base64.encode(user+":"+pass).trim());

This is the Base64 class I use:

public class Base64 {
        private static final String base64code = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "+/";

        private static final int splitLinesAt = 76;

        public static byte[] zeroPad(int length, byte[] bytes) {
                byte[] padded = new byte[length]; // initialized to zero by JVM
                System.arraycopy(bytes, 0, padded, 0, bytes.length);
                return padded;
        }

        public static String encode(String string) {

                String encoded = "";
                byte[] stringArray;
                try {
                        stringArray = string.getBytes("UTF-8");  // use appropriate encoding string!
                } catch (Exception ignored) {
                        stringArray = string.getBytes();  // use locale default rather than croak
                }
                // determine how many padding bytes to add to the output
                int paddingCount = (3 - (stringArray.length % 3)) % 3;
                // add any necessary padding to the input
                stringArray = zeroPad(stringArray.length + paddingCount, stringArray);
                // process 3 bytes at a time, churning out 4 output bytes
                // worry about CRLF insertions later
                for (int i = 0; i < stringArray.length; i += 3) {
                        int j = ((stringArray[i] & 0xff) << 16) +
                        ((stringArray[i + 1] & 0xff) << 8) +
                        (stringArray[i + 2] & 0xff);
                        encoded = encoded + base64code.charAt((j >> 18) & 0x3f) +
                        base64code.charAt((j >> 12) & 0x3f) +
                        base64code.charAt((j >> 6) & 0x3f) +
                        base64code.charAt(j & 0x3f);
                }
                // replace encoded padding nulls with "="
                return splitLines(encoded.substring(0, encoded.length() -
                                paddingCount) + "==".substring(0, paddingCount));

        }
        public static String splitLines(String string) {

                String lines = "";
                for (int i = 0; i < string.length(); i += splitLinesAt) {

                        lines += string.substring(i, Math.min(string.length(), i + splitLinesAt));
                        lines += "\r\n";

                }
                return lines;

        }
}

Upvotes: 2

Related Questions