Tillbaka till lektionslistan

Mobila applikationer med Android: Lektion 10

Idag:
  • Grunderna om nätverkskommunikation med Android
  • Programmet Bouncer (en server som tar emot "PING" från klienten och svarar med "PONG")
  • Apparna OnePinger och Pinger (klienter som skickar "PING"-meddelanden till servern)
  • Appen AndroidBouncer (servern Bouncer men för Android)
  • TCP och UDP

Klicka på startknappen i den lilla mediaspelaren ovan för att lyssna på lektionen. (Man kan behöva vänta en stund på att ljudfilen laddas ner.) Om mediaspelaren inte syns, eller om det inte fungerar av något annat skäl, kan man klicka här för att ladda ner mp3-filen (ca 44 minuter, ca 20 megabyte). Beroende på hur webbläsaren är konfigurerad kan det kräva ett separat mp3-spelarprogram av något slag.

"Bild" 1: Grunderna om nätverkskommunikation med Android

Nätverkshantering i Android använder (i stort sett) samma klasser och metoder som nätverkshantering i vanliga skrivbords- och server-Java (Java SE).

Några viktiga saker som skiljer:

  • Man måste ge appen tillåtelse att använda nätet, genom att ange android.permission.INTERNET i AndroidManifest.xml.
  • I Android behöver man tänka lite mer på trådar och det grafiska användargränssnittet.
  • Nätet kan fungera sämre än på en skrivbordsdator eller server:
    • lägre bandbredd
    • längre latens (fördröjning)
    • korta och långa avbrott
    • begränsningar
    • brandväggar
    • kostar pengar
    • drar batteri

"Bild" 2: Sex olika exempelprogram

TCP/IP ("socketar"):
  • Bouncer är inte en Android-app, utan ett program för skrivbords-Java (Java SE). Den fungerar som en server. Klienter kan koppla upp sig mot Bouncer och skicka PING, och då svarar Bouncer med PONG. Bouncer kan hantera flera olika klienter samtidigt.
  • OnePinger är en Android-app som kopplar upp sig mot Bouncer, skickar ett PING, väntar på Bouncers PONG, och sen är den klar och kopplar ner sig.
  • Pinger är en Android-app som kopplar upp sig mot Bouncer, men här kan man skicka mer än ett PING. Appen har tre knappar: Connect, Ping och Disconnect. De gör vad man kan förvänta sig.
  • AndroidBouncer är servern Bouncer, men nu i form av en Android-app, så att man kan köra den på en Android-telefon.
UDP (datagram):
  • UDPSender är en Android-app som skickar en text till en mottagare, men den här appen arbetar med datagram i stället för uppkopplade socketar. Alla de fyra programmen ovan använder TCP (Transmission Control Protocol), som betyder att man upprättar en uppkoppling, en så kallad socket, och sen ser systemet till att allt som skickas kommer fram, och i rätt ordning. Om något behöver sändas om, sköts det automatiskt. Med UDP (User Datagram Protocol) skickar man datapaket, så kallade datagram, och det finns inga garantier för att de kommer fram, vare sig i rätt ordning eller överhuvudtaget.
  • UDPReceiver är en Android-app som tar emot meddelanden, men med datapaket i stället för uppkopplade socketar.

"Bild" 3: Bouncer

Bouncer är inte en Android-app, utan ett program för skrivbords-Java (Java SE). Den fungerar som en server. Klienter kan koppla upp sig mot Bouncer och skicka PING, och då svarar Bouncer med PONG. Bouncer kan hantera flera olika klienter samtidigt.

Ladda ner: Bouncer.zip

Körexempel:

Vi börjar med att starta servern Bouncer, antingen inuti Eclipse eller motsvarande, eller bara i ett kommandofönster med kommandot java:
Bouncer 2013-11-05 11:12:16.657 +0100: Öppnar server-socketen...
Bouncer 2013-11-05 11:12:16.661 +0100: Server-socketen: ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=2003]
Bouncer 2013-11-05 11:12:16.661 +0100: Servern lyssnar...
Bouncer 2013-11-05 11:12:16.661 +0100: Väntar på uppkoppling från klient...
Nu står Bouncer och väntar på uppkopplingar. Här kommer det en:
Bouncer 2013-11-05 11:12:35.261 +0100: Uppkoppling från klient accepterad.
Bouncer 2013-11-05 11:12:35.261 +0100: Den nya socketen: Socket[addr=/127.0.0.1,port=42196,localport=2003]
Bouncer 2013-11-05 11:12:35.265 +0100: Väntar på uppkoppling från klient...
Nu är en klient uppkopplad. Än en gång väntar servern på uppkopplingar från klienter, men den väntar dessutom på meddelanden från den uppkopplade klienten. Efter ett tag skickar klienten ett PING:
ClientTalkerThread 1 2013-11-05 11:13:44.940 +0100: Fick ett PING från klienten
Strax därefter är der en annan klient som kopplar upp sig:
Bouncer 2013-11-05 11:14:08.897 +0100: Uppkoppling från klient accepterad.
Bouncer 2013-11-05 11:14:08.897 +0100: Den nya socketen: Socket[addr=/127.0.0.1,port=42231,localport=2003]
Bouncer 2013-11-05 11:14:08.898 +0100: Väntar på uppkoppling från klient...
Sen kommer några olika PING, omväxlande från de nu uppkopplade två klienterna:
ClientTalkerThread 1 2013-11-05 11:14:55.115 +0100: Fick ett PING från klienten
ClientTalkerThread 2 2013-11-05 11:14:59.204 +0100: Fick ett PING från klienten
ClientTalkerThread 1 2013-11-05 11:15:00.699 +0100: Fick ett PING från klienten
ClientTalkerThread 2 2013-11-05 11:15:02.251 +0100: Fick ett PING från klienten
ClientTalkerThread 2 2013-11-05 11:15:03.268 +0100: Fick ett PING från klienten
ClientTalkerThread 1 2013-11-05 11:15:04.219 +0100: Fick ett PING från klienten

Bild 4: Telnet som PING-klient

Om man inte har någon riktigt klient-app än, kan man använda programmet telnet. Här på Linux, men det fungerar även på Windows och Mac OS X. (På en del modernare versioner av Windows, till exempel Windows 7, finns programmet telnet inte med som default, men det är enkelt att installera: http://technet.microsoft.com/en-us/library/cc771275%28v=ws.10%29.aspx)

Telnet-PING-klient

Vi har kopplat upp oss mot en Bouncer-server som kör på samma dator (localhost), och sen skrivit PING, bludder och BYE. Vi ser också hur protokollet fungerar:

"Bild" 5: PING-protokollet

Klienten skickar Servern svarar Exempeldialog
PING PONG Klienten: PING
Servern: PONG
BYE BYE Klienten: BYE
Servern: BYE
Förbindelsen kopplas ned.
något annat ERROR Klienten: hej hej
Servern: ERROR

Bild 6: OnePinger

Ladda ner: BAD-OnePinger.zip

OnePinger

Bild 7: Layout-editorn i Eclipse

OnePinger-layouten i Layout-editorn i Eclipse

"Bild" 8: Klassen MainActivity i OnePinger

BAD-MainActivity.java

    1   package se.nekotronic.onepinger;
    2   
    3   import java.io.BufferedReader;
   ....
   20   
   21   public class MainActivity extends Activity implements OnClickListener {
   22       private TextView output_area;
   23       private EditText address_field;
   24       private Button go_button;
   25       public static final String DEFAULT_HOST = "130.243.124.198";
   26       public String host;
   27       public static final int PORT = 2003;
   28       
   29       @Override
   30       protected void onCreate(Bundle savedInstanceState) {
   31           super.onCreate(savedInstanceState);
   32           setContentView(R.layout.activity_main);
   33           output_area = (TextView)findViewById(R.id.output_area);
   34           address_field = (EditText)findViewById(R.id.address_field);
   35           address_field.setText(DEFAULT_HOST);
   36           go_button = (Button)findViewById(R.id.go_button);
   37           go_button.setOnClickListener(this);
   38   
   39           output_area.setText("");
   40           print("AndroidPinger: Appen startar.");
   41       }
   ....

"Bild" 9: Ett första försök att koppla upp

BAD-MainActivity.java

   43       @Override
   44       public void onClick(View view) {
   45           Log.d("*** OnePinger", "Button clicked: view = " + view.toString() + "\n");
   46           if (view == go_button) {
   47               try {
   48                   host = address_field.getText().toString();
   49                   print("Connecting to " + host + "...");
   50                   Socket socket = new Socket(host, PORT);
   51                   print("Connected.");
   52                   BufferedReader from_server = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   53                   PrintWriter to_server = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
   54                   // true: PrintWriter is line buffered
   55                   print("Sending PING to server...");
   56                   to_server.println("PING");
   57                   Log.d("*** OnePinger", "Waiting for a line from the server...");
   58                   String line_from_server = from_server.readLine();
   59                   if (line_from_server == null) {
   60                       print("Nothing to read from server.");
   61                   }
   62                   else {
   63                       print("Got line from server: '" + line_from_server + "'");
   64                   }
   65                   to_server.close();
   66                   from_server.close();
   67                   socket.close();
   68               } catch (UnknownHostException e) {
   69                   warning("Unknown host: " + e.getMessage());
   70               } catch (IOException e) {
   71                   warning("I/O Exception: " + e.getMessage());
   72               }
   73           }
   74       } // onClick

Bild 10: Permission denied

Permission denied

"Bild" 11: android.permission.INTERNET

AndroidManifest.xml

    1   <?xml version="1.0" encoding="utf-8"?>
    2   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3       package="se.nekotronic.onepinger"
    4       android:versionCode="1"
    5       android:versionName="1.0" >
    6   
    7       <uses-sdk
    8           android:minSdkVersion="8"
    9           android:targetSdkVersion="18" />
   10   
   11       <uses-permission android:name="android.permission.INTERNET" />
   12       
   13       <application
   14           android:allowBackup="true"
   15           android:icon="@drawable/ic_launcher"
   16           android:label="@string/app_name"
   17           android:theme="@style/AppTheme" >
   18           <activity
   19               android:name="se.nekotronic.onepinger.MainActivity"
   20               android:label="@string/app_name" >
   21               <intent-filter>
   22                   <action android:name="android.intent.action.MAIN" />
   23   
   24                   <category android:name="android.intent.category.LAUNCHER" />
   25               </intent-filter>
   26           </activity>
   27       </application>
   28   
   29   </manifest>

Bild 12: OnePinger has stopped

OnePinger has stopped

Bild 13: Connection refused

Connection refused

Bild 14: OnePinger i gammal Android

OnePinger - men vad är fel?

Bild 15: OnePinger isn't responding

OnePinger not responding

Bild 16: OnePinger i nyare Android

OnePinger has stopped

"Bild" 17: LogCat

W/dalvikvm(18424): threadid=1: thread exiting with uncaught exception (group=0x40a71930)
E/AndroidRuntime(18424): FATAL EXCEPTION: main
E/AndroidRuntime(18424): android.os.NetworkOnMainThreadException
E/AndroidRuntime(18424):        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1117)
E/AndroidRuntime(18424):        at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
E/AndroidRuntime(18424):        at libcore.io.IoBridge.connectErrno(IoBridge.java:127)
E/AndroidRuntime(18424):        at libcore.io.IoBridge.connect(IoBridge.java:112)
E/AndroidRuntime(18424):        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
E/AndroidRuntime(18424):        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
E/AndroidRuntime(18424):        at java.net.Socket.startupSocket(Socket.java:566)
E/AndroidRuntime(18424):        at java.net.Socket.tryAllAddresses(Socket.java:127)
E/AndroidRuntime(18424):        at java.net.Socket.(Socket.java:177)
E/AndroidRuntime(18424):        at java.net.Socket.(Socket.java:149)
E/AndroidRuntime(18424):        at se.nekotronic.onepinger.MainActivity.onClick(MainActivity.java:50)
E/AndroidRuntime(18424):        at android.view.View.performClick(View.java:4204)
E/AndroidRuntime(18424):        at android.view.View$PerformClick.run(View.java:17355)
E/AndroidRuntime(18424):        at android.os.Handler.handleCallback(Handler.java:725)
E/AndroidRuntime(18424):        at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime(18424):        at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(18424):        at android.app.ActivityThread.main(ActivityThread.java:5041)
E/AndroidRuntime(18424):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(18424):        at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(18424):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime(18424):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
E/AndroidRuntime(18424):        at dalvik.system.NativeStart.main(Native Method)
W/ActivityManager(  277):   Force finishing activity se.nekotronic.onepinger/.MainActivity

"Bild" 18: Den nya MainActivity

MainActivity.java

   35       @Override
   36       public void onClick(View view) {
   37           Log.d("*** OnePinger", "Button clicked: view = " + view.toString() + "\n");
   38           if (view == go_button) {
   39               host = address_field.getText().toString();
   40               OnePingThread t = new OnePingThread(this, host, PORT);
   41               t.start();
   42           }
   43       } // onClick

"Bild" 19: Klassen OnePingThread

OnePingThread.java

    1   package se.nekotronic.onepinger;
    2   
    3   import java.io.BufferedReader;
    4   import java.io.BufferedWriter;
    5   import java.io.IOException;
    6   import java.io.InputStreamReader;
    7   import java.io.OutputStreamWriter;
    8   import java.io.PrintWriter;
    9   import java.net.Socket;
   10   import java.net.UnknownHostException;
   11   import android.util.Log;
   12   
   13   public class OnePingThread extends Thread {
   14       private MainActivity parent;
   15       private String host;
   16       private int port;
   17       Socket socket;
   18       BufferedReader from_server;
   19       PrintWriter to_server;
   20       
   21       public OnePingThread(MainActivity parent, String host, int port) {
   22           this.parent = parent;
   23           this.host = host;
   24           this.port = port;
   25       }
   26   
   27       public void run() {
   28           try {
   29               parent.print("Connecting to " + host + "...");
   30               Socket socket = new Socket(host, port);
   31               parent.print("Connected.");
   32               BufferedReader from_server = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   33               PrintWriter to_server = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
   34               // true: PrintWriter is line buffered
   35               parent.print("Sending PING to server...");
   36               to_server.println("PING");
   37               Log.d("*** OnePinger", "Waiting for a line from the server...");
   38               String line_from_server = from_server.readLine();
   39               if (line_from_server == null) {
   40                   parent.print("Nothing to read from server.");
   41               }
   42               else {
   43                   parent.print("Got line from server: '" + line_from_server + "'");
   44               }
   45               to_server.close();
   46               from_server.close();
   47               socket.close();
   48           } catch (UnknownHostException e) {
   49               parent.warning("Unknown host: " + e.getMessage());
   50           } catch (IOException e) {
   51               parent.warning("I/O Exception: " + e.getMessage());
   52           }
   53       } // run
   54   } // OnePingThread

"Bild" 20: Den nya MainActivity

MainActivity.java

   45       public void print(String message) {
   46           Log.d("*** OnePinger", "message: " + message);
   47           Time now = new Time();
   48           now.setToNow();
   49           String timeString = now.format("%H:%M:%S");
   50           final String line = timeString + ": " + message + "\n";
   51           output_area.post(new Runnable() {
   52               @Override
   53               public void run() {
   54                   // output_area.setText(line + output_area.getText());
   55                   output_area.setText(output_area.getText() + line);
   56                   output_area.invalidate();
   57               }
   58           });
   59       } // print

Bild 21: OnePinger är "responsive"

Ladda ner: OnePinger.zip

Nu fungerar det som det ska!

"Bild" 22: AsyncTask

AsyncTask är ett alternativ till att skapa en tråd med extends Thread. Lästips:

"Bild" 23: OnePingTask

OnePingTask.java

    1	package se.nekotronic.onepinger;
    2	
    3	import java.io.BufferedReader;
    4	import java.io.IOException;
    5	import java.io.InputStreamReader;
    6	import java.io.OutputStreamWriter;
    7	import java.io.PrintWriter;
    8	import java.net.Socket;
    9	import java.io.BufferedWriter;
   10	import java.net.UnknownHostException;
   11	
   12	import android.os.AsyncTask;
   13	import android.util.Log;
   14	
   15	public class OnePingTask extends AsyncTask {
   16	    @Override
   17	    protected Void doInBackground(Object... params) {
   18	        MainActivity parent = (MainActivity) params[0];
   19	        String host = (String) params[1];
   20	        int port = (Integer) params[2];
   21	        try {
   22	            parent.print("Connecting to " + host + "...");
   23	            Socket socket = new Socket(host, port);
   24	            parent.print("Connected.");
   25	            BufferedReader from_server = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   26	            PrintWriter to_server = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
   27	            // true: PrintWriter is line buffered
   28	            parent.print("Sending PING to server...");
   29	            to_server.println("PING");
   30	            Log.d("*** OnePinger", "Waiting for a line from the server...");
   31	            String line_from_server = from_server.readLine();
   32	            if (line_from_server == null) {
   33	                parent.print("Nothing to read from server.");
   34	            }
   35	            else {
   36	                parent.print("Got line from server: '" + line_from_server + "'");
   37	            }
   38	            to_server.close();
   39	            from_server.close();
   40	            socket.close();
   41	        } catch (UnknownHostException e) {
   42	            parent.warning("Unknown host: " + e.getMessage());
   43	        } catch (IOException e) {
   44	            parent.warning("I/O Exception: " + e.getMessage());
   45	        }
   46	        return null;
   47	    }
   48	} // OnePingTask

"Bild" 24: MainActivity med en AsyncTask

   35	    @Override
   36	    public void onClick(View view) {
   37	        Log.d("*** OnePinger", "Button clicked: view = " + view.toString() + "\n");
   38	        if (view == go_button) {
   39	            host = address_field.getText().toString();
   40	            OnePingTask t = new OnePingTask();
   41	            t.execute(this, host, PORT);
   42	        }
   43	    } // onClick

Bild 25: Pinger

Ladda ner: Pinger.zip

Pinger

"Bild" 26: PingActivity

PingActivity.java

    1   package se.nekotronic.pinger;
    2   
    3   import se.nekotronic.pinger.R;
    4   import android.os.Bundle;
    5   import android.app.Activity;
    6   import android.text.format.Time;
    7   import android.util.Log;
    8   import android.view.View;
    9   import android.view.View.OnClickListener;
   10   import android.widget.Button;
   11   import android.widget.EditText;
   12   import android.widget.TextView;
   13   
   14   public class PingActivity extends Activity implements OnClickListener {
   15       private TextView output_area;
   16       private EditText address_field;
   17       private Button connect_button;
   18       private Button ping_button;
   19       private Button disconnect_button;
   20       private PingThread ping_thread = null;
   21       public static final String DEFAULT_HOST = "130.243.124.198";
   22       public String host;
   23       public static final int PORT = 2003;
   24   
   25       @Override
   26       public void onCreate(Bundle savedInstanceState) {
   27           super.onCreate(savedInstanceState);
   28           setContentView(R.layout.activity_ping);
   29           output_area = (TextView)findViewById(R.id.output_area);
   30           address_field = (EditText)findViewById(R.id.address_field);
   31           address_field.setText(DEFAULT_HOST);
   32           connect_button = (Button)findViewById(R.id.connect_button);
   33           connect_button.setOnClickListener(this);
   34           ping_button = (Button)findViewById(R.id.ping_button);
   35           ping_button.setOnClickListener(this);
   36           disconnect_button = (Button)findViewById(R.id.disconnect_button);
   37           disconnect_button.setOnClickListener(this);
   38   
   39           output_area.setText("");
   40           print("AndroidPinger: Appen startar.");
   41       }
   42       
   43       @Override
   44       protected void onPause() {
   45           super.onPause();
   46           if (ping_thread != null) {
   47               ping_thread.please_stop();
   48               ping_thread = null;
   49           }
   50       }
   51   
   52       @Override
   53       public void onClick(View view) {
   54           Log.d("*** ReadyPinger", "Button clicked: view = " + view.toString() + "\n");
   55           if (view == connect_button) {
   56               if (ping_thread != null) {
   57                   warning("Already connected.");
   58               }
   59               else {
   60                   host = address_field.getText().toString();
   61                   ping_thread = new PingThread(this, host, PORT);
   62                   ping_thread.start();
   63               }
   64           }
   65           else if (view == ping_button) {
   66                           if (ping_thread == null)
   67                   warning("Not connected.");
   68               else
   69                   ping_thread.send("PING");
   70           }
   71           else if (view == disconnect_button) {
   72               if (ping_thread == null)
   73                   warning("Not connected.");
   74               else
   75                   ping_thread.please_stop();
   76               ping_thread = null;
   77           }
   78       }
   79   
   80       public void done() {
   81           ping_thread = null;
   82       }
   83   
   84       public void print(String message) {
   85           Log.d("*** ReadyPinger", "message: " + message);
   86           Time now = new Time();
   87           now.setToNow();
   88           String timeString = now.format("%H:%M:%S");
   89           final String line = timeString + ": " + message + "\n";
   90           output_area.post(new Runnable() {
   91               @Override
   92               public void run() {
   93                   // output_area.setText(line + output_area.getText());
   94                   output_area.setText(output_area.getText() + line);
   95                   output_area.invalidate();
   96               }
   97           });
   98       } // print
   99   
  100       public void warning(String message) {
  101           print("Warning! " + message);
  102       }
  103   } // PingActivity

"Bild" 27: PingThread

PingThread.java

    1   package se.nekotronic.pinger;
    2   
    3   import java.io.BufferedReader;
    4   import java.io.BufferedWriter;
    5   import java.io.IOException;
    6   import java.io.InputStreamReader;
    7   import java.io.OutputStreamWriter;
    8   import java.io.PrintWriter;
    9   import java.net.Socket;
   10   import java.net.UnknownHostException;
   11   
   12   import android.util.Log;
   13   
   14   public class PingThread extends Thread {
   15       private PingActivity parent;
   16       private boolean quit = false;
   17       private String host;
   18       private int port;
   19       Socket socket;
   20       BufferedReader from_server;
   21       PrintWriter to_server;
   22       
   23       public PingThread(PingActivity parent, String host, int port) {
   24           this.parent = parent;
   25           this.host = host;
   26           this.port = port;
   27       }
   28   
   29       public void please_stop() {
   30           quit = true;
   31           try {
   32               to_server.flush();
   33               socket.shutdownInput();
   34           } catch (IOException e) {
   35               // Couldn't close. Not much we can do.
   36               parent.print("Problem closing connection");
   37           }
   38       }
   39   
   40       public void run() {
   41           try {
   42               parent.print("Connecting to " + host + "...");
   43               socket = new Socket(host, port);
   44               parent.print("Connected.");
   45               if (quit)
   46                   return;
   47               from_server = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   48               to_server = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
   49               // true: PrintWriter is line buffered
   50   
   51               while (!quit) {
   52                   Log.d("*** ReadyPinger", "Waiting for a line from the server...");
   53                   String line_from_server = from_server.readLine();
   54                   if (line_from_server == null) {
   55                       parent.print("Nothing more to read from server.");
   56                       quit = true;
   57                   }
   58                   else {
   59                       parent.print("Got line from server: '" + line_from_server + "'");
   60                   }
   61               }
   62               
   63               to_server.close();
   64               from_server.close();
   65               socket.close();
   66               // parent.done();
   67           }
   68           catch (UnknownHostException e) {
   69               parent.warning("Unknown host: " + e.getMessage());
   70           } catch (IOException e) {
   71               parent.warning("I/O Exception: " + e.getMessage());
   72           }
   73           parent.done();
   74       }
   75       
   76       void send(String line) {
   77           if (to_server == null) {
   78               parent.warning("Not connected yet.");
   79           }
   80           else {
   81               parent.print("Sending " + line);
   82               to_server.println(line);
   83           }
   84       }
   85   } // PingThread

Bild 28: AndroidBouncer

Ladda ner: AndroidBouncer.zip

Pinger

Bild 29: Emulatorn är brandväggad

Emulatorns brandvägg

Bild 30: Nu kan man ansluta till en server som körs i emulatorn

Uppkoppling mot servern i emulatorn

Bild 31: Brandväggar och NAT

TELE2

Bild 32: Brandväggar och NAT

Eduroam

Bild 33: Brandväggar och NAT

Eduroam

Bild 34: UDPSender

Ladda ner: UDPSender.zip

UDPSender

Bild 35: UDPReceiver

Ladda ner: UDPReceiver.zip

UDPReceiver

"Bild" 36: UDPSendThread.java

Ladda ner: UDPSendThread.java

    1	package se.nekotronic.udpsender;
    2	import java.io.IOException;
    3	import java.net.DatagramPacket;
    4	import java.net.DatagramSocket;
    5	import java.net.InetAddress;
    6	import java.net.SocketException;
    7	import java.net.UnknownHostException;
    8	
    9	public class UDPSendThread extends Thread {
   10	    private UDPSendActivity parent;
   11	    private String host;
   12	    private int port;
   13	    private String message;
   14	
   15	    public UDPSendThread(UDPSendActivity parent, String host, int port, String message) {
   16	        this.parent = parent;
   17	        this.host = host;
   18	        this.port = port;
   19	        this.message = message;
   20	    }
   21	    
   22	    public void run() {
   23	        try {
   24	            InetAddress receiver_address = InetAddress.getByName(host);
   25	            DatagramSocket socket = new DatagramSocket();
   26	            byte[] buffer = message.getBytes();
   27	            DatagramPacket packet = new DatagramPacket(buffer, buffer.length, receiver_address, port);
   28	            parent.print("Sending '" + message + "'...");
   29	            socket.send(packet);
   30	            parent.print("Sent '" + message + "'");
   31	            socket.close();
   32	        } catch (UnknownHostException e) {
   33	            parent.warning("UnknownHostException e = " + e);
   34	        } catch (SocketException e) {
   35	            parent.warning("SocketException e = " + e);
   36	        } catch (IOException e) {
   37	            parent.warning("IOException e = " + e);
   38	        }
   39	    } // run
   40	} // UDPSendThread

"Bild" 37: UDPServerThread.java

Ladda ner: UDPServerThread.java

    1	package se.nekotronic.udpreceiver;
    2	
    3	import java.io.IOException;
    4	import java.net.DatagramPacket;
    5	import java.net.DatagramSocket;
    6	import java.net.InetAddress;
    7	import java.net.NetworkInterface;
    8	import java.net.SocketException;
    9	import java.util.Enumeration;
   10	
   11	public class UDPServerThread extends Thread {
   12	    private UDPReceiveActivity parent;
   13	    private int port;
   14	    private boolean quit = false;
   15	    private DatagramSocket socket;
   16	
   17	    public UDPServerThread(UDPReceiveActivity parent, int port) {
   18	        this.parent = parent;
   19	        this.port = port;
   20	    }
   21	
   22	    public void run() {
   23	        try {
   24	            parent.print("Nätverksstatus...");
   25	            showNetworkStatus();
   26	
   27	            socket = new DatagramSocket(port);
   28	            byte[] buf = new byte[100];
   29	            while (!quit) {
   30	                DatagramPacket packet = new DatagramPacket(buf, buf.length);
   31	                parent.print("Waiting for packet...");
   32	                socket.receive(packet);
   33	                String message = new String(packet.getData());
   34	                parent.print("Received packet: '" + message + "'");
   35	            }
   36	        } catch (SocketException e) {
   37	            parent.print("SocketException: " + e);
   38	        } catch (IOException e) {
   39	            parent.print("IOExceptione: " + e);
   40	        }            
   41	    } // run
   42	
   43	    public void please_stop() {
   44	        quit = true;
   45	        socket.close();
   46	    }
   47	    
   48	    private void showNetworkStatus() {
   49	        try {
   50	                for (Enumeration is = NetworkInterface.getNetworkInterfaces(); is.hasMoreElements();) {
   51	                        NetworkInterface i = is.nextElement();
   52	                        // parent.print("Interface: " + i.getName() + ", " + (i.isUp() ? "up" : "down")); -- Requires API 9
   53	                        parent.print("Interface: " + i.getName());
   54	                        for (Enumeration ip = i.getInetAddresses(); ip.hasMoreElements();) {
   55	                                InetAddress a = ip.nextElement();
   56	                                parent.print("  Address: " + a.toString());
   57	                        }
   58	                }
   59	        }
   60	        catch (SocketException e) {
   61	                parent.print("Caught exception: " + e);
   62	        }
   63	    } // showNetworkStatus
   64	} // UDPServerThread

Kom ihåg

Läs

Om nätverkshantering i vanliga skrivbords-Java kan man läsa i många olika Java-böcker och på många ställen på webben, till exempel i lektionen All About Sockets i The Java Tutorials. Dessa grunder om Java-nätverkshantering behövs om man ska göra mer avancerade saker i Android, till exempel skriva en Android-app som fungerar som en server och väntar på uppkopplingar.

På Android-utvecklarwebbplatsen developer.android.com kan man läsa Performing Network Operations:

Om man har tillgång till boken Android Application Development Cookbook så är kapitel 4, Network Programming, bra.

Ska man använda UDP eller TCP, till exempel i ett spel? Allmänt är TCP enklast, eftersom man har en garanti för att datat kommer fram, och kommer fram i rätt ordning, men ibland ger UDP bättre prestanda. Lästips:

Tillbaka till lektionslistan


Thomas Padron-McCarthy (thomas.padron-mccarthy@oru.se), 9 november 2013