Search code examples
javaandroidtelnet

Reading Lines Correctly Using Telnet


I have an Android app, which should receive and read NMEA-sentences

e.g. $GPGLL,3751.65,S,14507.36,E*77

from a remote TCP-Server using Telnet protocol. I am using the org.apache.commons.net.telnet.TelnetClient library.

What works:

  • Connecting to Server

  • Reading some sentences without any error

Problem: There are more than half of the sentences missing. I guess that it is a timing problem, perhaps this has also to do with the connection which gets restarted in every iteration.

Here is my MainActivity.java:

package com.example.clienttel;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import org.apache.commons.net.telnet.TelnetClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Go to ClientThread, where NMEA-sentences will be received
        int i;
        for (i = 0; i < 10000; i++)     // 10000 for testing
            new Thread(new ClientThread()).start();
    }

    class ClientThread implements Runnable {
        //@Override
        public void run() {
            TelnetClient telnet = new TelnetClient();

            // Variables
            String ADDRESS = "194.66.82.11";
            int PORT = 51000;
            String NMEA = null;
            final String TAG = "TestApp";

            // Connect To Server
            try {
                telnet.connect(ADDRESS, PORT);
            } catch (IOException e) {
                e.printStackTrace();
            }

            // Process NMEA-sentences
            InputStream inStream = telnet.getInputStream();
            BufferedReader r = new BufferedReader(new InputStreamReader(inStream));

            try {
                NMEA = r.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }

            // Ignore "...busy" sentences
            if (NMEA != null) {
                if(!(NMEA.equals("*** Serial port is busy ***"))) {
                    Log.i(TAG, NMEA);
                }
            }

            // Disconnect From Server
            try {
                telnet.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Any help would be appreciated!

EDIT:

My code now looks like this:

package com.example.clienttel;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;

import org.apache.commons.net.telnet.TelnetClient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class MainActivity extends ActionBarActivity {
    // Variables
    public String ADDRESS = "194.66.82.11";
    public int PORT = 50100;
    public String NMEA = null;
    public final String TAG = "TestApp";
    public boolean first = true;


    // Handler in mainthread
    Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            String dataString = "";
            Bundle bundle = msg.getData();

            Log.d("handleMessage", bundle.toString());
            if (bundle.containsKey("outgoingString")) {
                dataString = bundle.getString("outgoingString");
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ClientThread ct;
        ct = new ClientThread();
        ct.mhandler=handler;

        int i;
        for (i = 0; i < 10000; i++) {     // 10000 for testing
            // Go to ClientThread, where NMEA-sentences will be received
            //new Thread(new ClientThread()).start();
            ct.start();
        }
    }

    class ClientThread implements Runnable {
        public Handler mhandler = null;
        @Override
        public void run() {
            TelnetClient telnet = new TelnetClient();

            if (first) {
                // Connect To Server in 1st Iteration
                try {
                    telnet.connect(ADDRESS, PORT);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                first = false;
            }

            // Process NMEA-sentences
            InputStream inStream = telnet.getInputStream();
            BufferedReader r = new BufferedReader(new InputStreamReader(inStream));

            try {
                NMEA = r.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }

            // Handler in ClientThread to send back
            Bundle b = new Bundle();

            b.putString("outgoingString", NMEA);

            Message m = mhandler.obtainMessage();
            m.setData(b);
            mhandler.sendMessage(m);
        }
    }
}

but I am not sure about the placement of the handlers


Solution

  • As @EJP said, opening an closing the connection for each line will kill the app...

    A better approach is have the thread handle the communication(s) and wait for the next line, passing the data back to the main thread as a bundle message.

    There are plenty of examples out there, but essentially you create a handler in your main thread:

    Handler handler = new Handler()
    {
        public void handleMessage(Message msg)
        {
    
            String dataString = "";
    
            Bundle bundle = msg.getData();
    
            Log.d("handleMessage", bundle.toString());
            if (bundle.containsKey("outgoingString"))
            {
                dataString = bundle.getString("outgoingString");
    
            }
    
            // you can handle other message types here....
       }
    

    and pass it (the handler) to your ClientThread (here, as mhandler) where you can send the messages back:

    Bundle b = new Bundle();
    
    b.putString("outgoingString", outgoingText);
    Message m = mhandler.obtainMessage();
    m.setData(b);
    mhandler.sendMessage(m);