android asynchrone sockets (android app als server, die auf verbindungen von clients wartet)

  • Antworten:1
kent211
  • Forum-Beiträge: 15

15.05.2012, 17:22:41 via Website

Hi,

ich arbeite derzeit an einem projekt für die uni und könnte eure hilfe gebrauchen.
Auf stackoverflow hab ich meine Frage eben auch schon gepostet, ich hoffe mal das ist kein Problem?
Ich denke es macht so mehr Sinn, da nicht alle Leute dort vorbeischauen, die hier aktiv sind.
Somit würde es sicher auch anderen Entwicklern hier helfen :-).

Im Projekt haben wir vor eine tcp socket verbindung zu nutzen, um eine android app und einen client auf windows kommunizieren zu lassen.
Der client soll nachher ein Spiel sein, welches durch die App gesteuert werden kann. Durch die Sensoren unter Android kann man sich da eine schöne Steuerung basteln :-). (Kurzfassung ohne Code, befindet sich unten :-D.)

Die MainActivity startet einen Service, welcher sich um den Aufbau eines ServerSockets kümmern soll und allgemein um die gesamte Verbindung. Meine App soll in dem Zusammenhang der Server sein, welcher Verbindungen entgegennimmt.

Ich werde nun im folgenden die wichtigsten Codeausschnitte posten und hoffe das jemand den "Fehler" findet :-/, bzw. mir einfach helfen kann.

Service Klasse:
1public class ServerService extends Service {
2
3 ConnectionHandler conHandler;
4
5 @Override
6 public void onCreate() {
7 startListener();
8 }
9
10 private void startListener() {
11 conHandler = new ConnectionHandler(this);
12 conHandler.execute();
13 }
14
15 private void sendMessage(String s)
16 {
17 conHandler.write(s);
18 }
19
20 public void messageNotify(String s) {
21 //Log.d("receivedMessage", s);
22 }
23 }

Die ConnectionHandler Klasse ist wie folgt implementiert:
1public class ConnectionHandler extends AsyncTask<Void, Void, Void>{
2
3 public static int serverport = 11111;
4 ServerSocket s;
5 Socket c;
6 ConnectionListening conListening;
7 ConnectionWriting conWriting;
8 DataOutputStream dos;
9 DataInputStream dis;
10 ServerService server;
11
12 public ConnectionHandler(ServerService server)
13 {
14 this.server = server;
15 }
16
17 @Override
18 protected Void doInBackground(Void... params) {
19
20 try {
21 Log.i("AsyncTank", "doInBackgoung: Creating Socket");
22 s = new ServerSocket(serverport);
23 } catch (Exception e) {
24 Log.i("AsyncTank", "doInBackgoung: Cannot create Socket");
25 }
26 try {
27 //this is blocking until client connects
28 c = s.accept();
29 Log.d("ConnectionHandler", "client connected");
30 dis = new DataInputStream(c.getInputStream());
31 dos = new DataOutputStream(c.getOutputStream());
32 } catch (IOException e1) {
33 // TODO Auto-generated catch block
34 e1.printStackTrace();
35 }
36 conWriting = new ConnectionWriting(this.c, this.dos);
37 conWriting.execute();
38 conListening = new ConnectionListening(this.c, this.dis, this.server);
39 if(this.c != null)
40 {
41 Timer timer = new Timer();
42 timer.schedule(conListening, 0, 10);
43 }
44
45 Log.i("AsyncTank", "doInBackgoung: Socket created, Streams assigned");
46 return null;
47 }
48
49 public void write(String s)
50 {
51 conWriting.writeToStream(s);
52 }
53
54 public void messageNotify(String s) {
55 // TODO method stub
56 }
57
58 }

Der ConnectionHandler und die ConnectionWriting-Klasse sind als AsyncTasks implementiert, sodass die blockenden Funktionen von TCP nicht meine gesamte Anwendung blockieren und beeinflussen.

Wichtig zu wissen ist noch, dass der Client auch Nachrichten an den Server schicken kann. Natürlich weiss man nicht, wann eine solche Nachricht geschickt wurde, sodass ich irgendwie den Stream dauerhaft beobachten muss. Ich habe das jetzt hier als TimerTask realisiert, welcher alle 10ms ausgeführt wird und guckt ob Nachrichten vorhanden sind.

Diese Klasse sieht wie folgt aus:
1And the ConnectionListening class:
2 public class ConnectionListening extends TimerTask{
3
4 public DataInputStream dis;
5 Socket c;
6 ServerService server;
7
8 public ConnectionListening(Socket c, DataInputStream dis, ServerService server)
9 {
10 this.c = c;
11 this.dis = dis;
12 this.server = server;
13 }
14
15 @Override
16 public void run() {
17 String message = "";
18 try {
19 if (c != null) {
20 //Log.i("AsynkTask", "readFromStream : Reading message");
21 message = dis.readLine();
22 Log.i("AsynkTask", "read: " + message);
23 } else {
24 Log.i("AsynkTask", "readFromStream : Cannot Read, Socket is closed");
25 }
26 } catch (Exception e) {
27 Log.i("AsynkTask", "readFromStream : Writing failed");
28 }
29
30 if(message != null)
31 {
32 this.server.messageNotify(message);
33 }
34
35 }
36
37
38 }

Und die Klasse die zum Schreiben von Nachrichten an den Client benutzt wird, sieht wie folgt aus:

1public class ConnectionWriting extends AsyncTask<Context, Void, Boolean>{
2
3 public DataOutputStream dos;
4 Socket c;
5
6 public ConnectionWriting(Socket c, DataOutputStream dos) {
7 this.dos = dos;
8 this.c = c;
9 }
10
11 @Override
12 protected Boolean doInBackground(Context... params) {
13 return true;
14 }
15
16 public void writeToStream(String s) {
17 try {
18 if (c != null){
19 //Log.i("AsynkTask", "writeToStream");
20 dos.writeBytes(s+"\n");
21 dos.flush();
22 Log.i("AsynkTask", "write: " +s);
23 } else {
24 Log.i("AsynkTask", "writeToStream : Cannot write to stream, Socket is closed");
25 }
26 } catch (Exception e) {
27 Log.i("AsynkTask", "writeToStream : Writing failed");
28 }
29 }
30
31 }

Diesen komplexen, asynchronen Ansatz habe ich eben deshalb gewählt, da der Server (die app) durchgängig Daten an den Client sendet, es dazwischen allerdings Fälle geben kann, in denen der Client auch dem Server etwas senden will, was ich unbedingt möglichst zeitnah erhalten muss.

Die eigentlich funktionsweise von tcp sockets sieht ja so aus, dass das lesen und schreiben gegenseitig blockierend sind. Das würde eben dazu führen, dass ich die Nachrichten des clients nie erhalten würde, zumindest nicht so lange wie ich (der server) daten an den client sendet.

Kurzfassung:
Mein Ansatz scheint nicht wirklich asynchron abzulaufen. Ich habe zum Test mal direkt nach erfolgreicher Verbindung 100 Nachrichten an den Client gesendet, die ich mit einem Echo direkt beantworte. Im Log sieht man, dass der Server zuerst alle seine Nachrichten abschickt und dann erst die vom Client gesendeten liest :-/. Also genau das was ich nicht haben wollte.

Eventuell kann mir einer helfen dieses Problem zu lösen?
Ich habe so viele verschiedene Ansätze und Vorschläge im Internet gelesen, allerdings sind mir bei vielen noch mehr Fragen aufgekommen, sodass ich mich eben für eine eigene Lösung entschieden habe.

Wichtig ist: Die Kommunikation muss asynchron sein! Ebenso muss das Lesen automatisch immer wieder stattfinden, also quasi das Lauschen auf ein Read muss irgendwie implementiert werden können.

Ein interesseanter Ansatz, von dem ich gelesen habe, geht in die Richtung, dass man für das Lesen und Schreiben jeweils einen Thread nimmt. Das Problem hierbei ist aber, dass ich nicht weiss wie ich dann aus meinen Activities die Threads nutzen kann um was zu schreiben und wie ich aus den Threads Funktionen in meinen Activities aufrufen kann.

Für jeden Hinweis wäre ich sehr dankbar.

LG

Antworten
kent211
  • Forum-Beiträge: 15

17.05.2012, 13:11:51 via Website

Push!

Kann keiner behilflich sein? :-/.

LG

Antworten