Android Server-Client-Verbindung in Android-App mit Sockets aufbauen

Michel95

Mitglied
Hallo,

ich möchte gerne eine Android-App in Java programmieren, die sich zunächst mit einem Server im WLAN-Netzwerk verbindet. Später sollen dann Daten zwischen Server und Client hin und her geschickt werden.

Auf dem Handy soll also die App als Client laufen und sich über einen
Code:
java.net.Socket
mit dem Server verbinden. Das ganze sieht auf dem Handy folgendermaßen aus:



Wenn der Connect-Button geklickt wird, soll von der App eine Verbindung zum Server hergestellt werden und auf einem Label (
Code:
android.widget.TextView
) ausgegeben werden, dass die Verbindung erfolgreich hergestellt werden konnte oder auch nicht hergestellt werden konnte.

Jetzt zu meinem Problem:
Ich darf ich in einer Android-App keine Netzwerkverbindung im Main-Thread herstellen, dann gibt es eine
Code:
android.os.NetworkOnMainThreadException
.
05-27 18:33:02.422 15805-15805/com.musicstream.app E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.musicstream.app, PID: 15805
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1156)
at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
at libcore.io.IoBridge.connectErrno(IoBridge.java:127)
at libcore.io.IoBridge.connect(IoBridge.java:112)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
at java.net.Socket.startupSocket(Socket.java:566)
at java.net.Socket.tryAllAddresses(Socket.java:128)
at java.net.Socket.<init>(Socket.java:178)
at java.net.Socket.<init>(Socket.java:150)
at com.musicstream.app.Verbindung.<init>(Verbindung.java:61)
at com.musicstream.app.MainActivity.btnConnectClick(MainActivity.java:115)
at com.musicstream.app.MainActivity.access$400(MainActivity.java:19)
at com.musicstream.app.MainActivity$2.onClick(MainActivity.java:65)
at android.view.View.performClick(View.java:4633)
at android.view.View$PerformClick.run(View.java:19330)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5356)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(Native Method)
Daher habe ich das Erstellen des neuen Sockets einen neuen Thread verlegt und starte diesen beim Aufruf der Button-Click-Methode, so wie ich es normalerweise in Java auch mache, damit die graphische Oberfläche nicht während der Laufzeit der Button-Click-Methode blockiert ist. Wenn ich aber dann von diesem Thread aus den Text eines Labels ändern will, bekomme ich auch eine Fehlermeldung, und zwar eine
Code:
android.view.ViewRootImpl$CalledFromWrongThreadException
, weil ich den Text eines Labels nur aus dem Thread ändern darf, in dem ich es auch ereugt habe, also nur vom MainThread.
05-27 18:59:00.973 19873-19962/com.musicstream.app E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-16367
Process: com.musicstream.app, PID: 19873
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6909)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1054)
at android.view.View.requestLayout(View.java:17321)
at android.view.View.requestLayout(View.java:17321)
at android.view.View.requestLayout(View.java:17321)
at android.view.View.requestLayout(View.java:17321)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.requestLayout(View.java:17321)
at android.widget.TextView.checkForRelayout(TextView.java:8003)
at android.widget.TextView.setText(TextView.java:4840)
at android.widget.TextView.setText(TextView.java:4672)
at android.widget.TextView.setText(TextView.java:4647)
at com.musicstream.app.MainActivity$3.run(MainActivity.java:155)
at java.lang.Thread.run(Thread.java:841)
Wie soll ich denn so eine Verbindung erstellen und danach auf einem Label ausgeben, ob die Verbindung erfolgreich war. Meine einzige Lösung wäre, eine Warteschliefe in der Button-Click-Methode zu erstellen, die wartet, bis die Verbindung erstellt wurde. Allerdings ist dann die Graphische Oberfläche die ganze Zeit blockiert. Kennt sich vielleicht jemand mit der Android-Programmierung aus und kann mir weiterhelfen? Gibt es da auch eine schönere Lösung?

PS: Der Server wird auf dem Computer gestartet und funktioniert bereits. Sobald sich ein Client verbindet, zeigt der Server dies an.
 

dzim

Top Contributor
Du bist dir also des Problems bewusst, warum du Sockets nicht auf dem Hauptthread öffnen soll, das ist schon mal gut. Du kannst dir (wie genau weiss ich nicht, da ich es unsauber finde) auch irgendwie die Berechtigung holen, doch auf dem Hauptthread zu arbeiten. Ist aber eher eine blöde Idee.
Entweder du verwendest nun die #post-Methode der View-Klasse
Java:
<yourViewToUpdate>.post(new Runnable() {
	@Override
	public void run() {
		// do something
		// e.g. <yourViewToUpdate>.setText("bla");
	}
});
dann kannst du auch von nicht-UI-Threads die Oberfläche aktualisieren.

Ich verwende aber meist nicht nur Threads, sondern eigene Implementierungen der Klasse AsyncTask. Dort kann man die Hauptaufgabe in der #doInBackground-Methode erledigen, aber die anderen #on<suchDirWasAus>-Methoden sind wieder im Hauptthread.
Bsp./Tutorials (und wieder mal der Lars Vogel): Android Background Processing with Handlers and AsyncTask and Loaders - Tutorial
 

Michel95

Mitglied
Hi,

danke für deine Antwort erstmal. Ich habe mir die Klasse AsyncTask jetzt mal angeschaut, allerdings finde ich das Programmieren damit sehr umständlich, da ich immer zwei Werte von der doInBackground()-Methode zurückgeben lassen müsste, einen String für die Toast-Nachricht und einen für das Statuslabel. Dafür bräuchte ich eine extra Hilfsklasse. Daher arbeite ich jetzt immer mit der post()-Methode, das ist zwar dann sehr viel Code, aber nicht so schlimm. Ich habe meinen Code jetzt soweit umgeändert.
Java:
package com.musicstream.app;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.util.logging.Handler;


public class MainActivity extends ActionBarActivity {

    private EditText txtHost;
    private EditText txtPort;
    private Button btnConnect;
    private TextView lblStatus;

    /*** Specifies whether the UI is enabled or not. */
    private boolean enabled = true;

    /** Specifies whether the client is connected to the server or not. */
    private boolean connected = false;

    /** Connection to the server */
    Verbindung verbindung;

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

        txtHost = (EditText) findViewById(R.id.txtHost);
        txtPort = (EditText) findViewById(R.id.txtPort);
        btnConnect = (Button) findViewById(R.id.btnConnect);
        lblStatus = (TextView) findViewById(R.id.lblStatus);

        txtPort.setText("2048");

        TextWatcher watcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
                btnConnect.setEnabled(txtHost.getText().toString().trim().length() > 0 &&
                                      txtPort.getText().toString().trim().length() > 0 &&
                                      enabled);
            }

            @Override
            public void afterTextChanged(Editable editable) { }
        };
        
        txtHost.addTextChangedListener(watcher);
        txtPort.addTextChangedListener(watcher);

        btnConnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                btnConnectClick();
            }
        });
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Is executed when the Connect-Button is clicked.
     */
    private void btnConnectClick() {

        Thread thread = new Thread(new Runnable() {
            public void run() {

                enableInterface(false);

                if (connected) {
                    disconnect();
                }
                else {
                    connect();
                }

                enableInterface(true);
            }
        });
        thread.start();
    }

    /**
     * Tries to connect the client to the server.
     */
    private void connect() {
        btnConnect.post(new Runnable() {
            @Override
            public void run() {
                btnConnect.setText(R.string.connecting);
            }
        });
        lblStatus.post(new Runnable() {
            @Override
            public void run() {
                lblStatus.setText(R.string.statusConnecting);
            }
        });


        try {

            final String host = txtHost.getText().toString().trim();
            final String portString = txtPort.getText().toString().trim();

            final int port = Integer.parseInt(portString);

            if (port < 1024 || port > 65535) {
                showMessage(R.string.toastPortInfo);
                lblStatus.post(new Runnable() {
                    @Override
                    public void run() {
                        lblStatus.setText("Invalid port: " + port + ". Port must be between 1024 and 65535");
                    }
                });
                print("Invalid port: " + port + ". Port must be between 1024 and 65535");
            }
            else {
                print("Connecting to host " + host + ":" + port);

                // Hier wird die Verbindung hergestellt und dabei der Socket erzeugt.
                verbindung = new Verbindung(host, port, new Verarbeitung() {
                    @Override
                    public void verarbeite(String nachricht) {

                    }

                    @Override
                    public void sendeFehler(Exception e) {

                    }

                    @Override
                    public void empfangFehler(Exception e) {

                    }
                });

                showMessage(R.string.toastConnected);
                btnConnect.post(new Runnable() {
                    @Override
                    public void run() {
                        btnConnect.setText(R.string.disconnect);
                    }
                });
                lblStatus.post(new Runnable() {
                    @Override
                    public void run() {
                        lblStatus.setText(R.string.statusConnected);
                    }
                });

            }
        }
        catch (NumberFormatException e) {
            printException(e, "Invalid Port");
            // showMessage(R.string.toastInvalidPort);
            btnConnect.post(new Runnable() {
                @Override
                public void run() {
                    btnConnect.setText(R.string.connect);
                }
            });
        }
        catch (ConnectException e) {
            printException(e, "Connection time ran out. Could not connect.");
            showMessage(R.string.toastConnectException);
            lblStatus.post(new Runnable() {
                @Override
                public void run() {
                    lblStatus.setText(R.string.statusConnectException);
                }
            });
            btnConnect.post(new Runnable() {
                @Override
                public void run() {
                    btnConnect.setText(R.string.connect);
                }
            });
        }
        catch (UnknownHostException e) {
            printException(e, "Unknown host. Server not found. Could not connect.");
            showMessage(R.string.toastUnknownHost);
            lblStatus.post(new Runnable() {
                @Override
                public void run() {
                    lblStatus.setText(R.string.statusUnknownHost);
                }
            });
            btnConnect.post(new Runnable() {
                @Override
                public void run() {
                    btnConnect.setText(R.string.connect);
                }
            });
        }
        catch (IOException e) {
            printException(e, "Connection problems. Could not connect.");
            showMessage(R.string.toastIOException);
            lblStatus.post(new Runnable() {
                @Override
                public void run() {
                    lblStatus.setText(R.string.statusIOException);
                }
            });
            btnConnect.post(new Runnable() {
                @Override
                public void run() {
                    btnConnect.setText(R.string.connect);
                }
            });

        }
        catch (final Exception e) {
            printException(e, "Unknown problem occurred.");
            showMessage(R.string.toastUnknownProblem);
            lblStatus.post(new Runnable() {
                @Override
                public void run() {
                    lblStatus.setText("Unknown Problem occurred: " + e);
                }
            });
            btnConnect.post(new Runnable() {
                @Override
                public void run() {
                    btnConnect.setText(R.string.connect);
                }
            });
        }
    }


    /**
     * Disconnects the client from the server.
     */
    private void disconnect() {
        if (verbindung != null) {
            try {
                verbindung.beende();
                showMessage(R.string.toastDisconnected);
                lblStatus.post(new Runnable() {
                    @Override
                    public void run() {
                        lblStatus.setText(R.string.statusDisconnected);
                    }
                });
                btnConnect.post(new Runnable() {
                    @Override
                    public void run() {
                        btnConnect.setText(R.string.connect);
                    }
                });
            }
            catch (IOException e) {
                printException(e, "Connection to server could not be closed correctly.");
            }
        }
    }

    /**
     * Prints the Exception message.
     * @param e
     */
    public static void printException(Exception e) {
        print("Exception caught: " + e);
    }

    /**
     * Prints the Exception message with some information.
     * @param e
     */
    public static void printException(Exception e, String info) {
        printException(e);
        print("=> " + info);
    }

    /**
     * Prints an Object into System.out.
     * @param object
     */
    public static void print(Object object) {
        System.out.println(object);
    }


    /**
     * Enables components of the user interface.
     * @param b
     */
    private void enableInterface(final boolean b) {
        enabled = b;
        txtHost.post(new Runnable() {
            @Override
            public void run() {
                txtHost.setEnabled(b);
            }
        });
        txtPort.post(new Runnable() {
            @Override
            public void run() {
                txtPort.setEnabled(b);
            }
        });
        btnConnect.post(new Runnable() {
            @Override
            public void run() {
                btnConnect.setEnabled(b);
            }
        });
    }


    /**
     * Shows a message with Toast.
     * @param message
     */
    private void showMessage(final int message) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * Shows a message with Toast.
     * @param message
     */
    private void showMessage(final String message) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
            }
        });
    }
}
Ich habe sogar die Internet Berechtigung
Code:
android.permission.INTERNET
zur AndroidManifest.xml hinzugefügt. Allerdings bin ich mir nicht sicher, ob die Berechtugung an der richtigen Stelle eigefügt habe.
[XML]<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.musicstream.app" >

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.musicstream.app.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

<uses-permission android:name="android.permission.INTERNET" />

</manifest>
[/XML]
Wenn die Manifest-Datei in Ordnung ist, müsste ja eigentlich alles funktionieren. beide Geräte, Computer und Handy, sind im WLAN-Netzwerk angemeldet und haben Zugriff aufs Internet. Ich starte den Server und trage die Server IP und den gleichen Port am Handy ein, dann versuche ich mich mit der App am Handy mit dem Server zu verbinden. Allerdings bekomme ich immer eine
Code:
java.net.ConnectException
mit folgendem Fehlercode:
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ java.net.ConnectException: failed to connect to /192.168.2.3 (port 2048): connect failed: ETIMEDOUT (Connection timed out)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at libcore.io.IoBridge.connect(IoBridge.java:114)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at java.net.Socket.startupSocket(Socket.java:566)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at java.net.Socket.tryAllAddresses(Socket.java:128)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at java.net.Socket.<init>(Socket.java:178)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at java.net.Socket.<init>(Socket.java:150)
05-29 13:59:42.843 20979-21236/com.musicstream.app W/System.err﹕ at com.musicstream.app.Verbindung.<init>(Verbindung.java:61)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at com.musicstream.app.MainActivity.connect(MainActivity.java:157)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at com.musicstream.app.MainActivity.access$800(MainActivity.java:20)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at com.musicstream.app.MainActivity$3.run(MainActivity.java:109)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at java.lang.Thread.run(Thread.java:841)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ Caused by: libcore.io.ErrnoException: connect failed: ETIMEDOUT (Connection timed out)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at libcore.io.Posix.connect(Native Method)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at libcore.io.BlockGuardOs.connect(BlockGuardOs.java:85)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at libcore.io.IoBridge.connectErrno(IoBridge.java:127)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ at libcore.io.IoBridge.connect(IoBridge.java:112)
05-29 13:59:42.853 20979-21236/com.musicstream.app W/System.err﹕ ... 11 more
Wenn das hilfreich ist, kann ich auch nochmal den Code der Klasse Verbindung.java posten, aber der Fehler passiert genau in der Zeile, in der
Java:
socket = new Socket(host, port);
steht.
Auf docs.oracle.com habe ich folgendes gefunden:
Signals that an error occurred while attempting to connect a socket to a remote address and port. Typically, the connection was refused remotely (e.g., no process is listening on the remote address/port).

Der Fehler müsste also daran liegen, dass kein Server auf der anderen Seite horcht, kann aber nicht sein, weil ich mit jedem anderen Client verbinden kann. Liegt der Fehler jetzt in der App oder beim Server? Kann mir da noch jemand weiterhelfen?
 

Michel95

Mitglied
OK ich hab die Antwort gefunden. Die Firewall meines PC's wars schuld. Erst als ich das Eclipse Projekt für den Server als jar-Datei exportiert habe, hat die Firewall nachgefragt, ob sie die Verbindung zulassen soll.

Vielen Dank nochmal für deine Antwort.
 

dzim

Top Contributor
1) Bei mir stehen die permissions eigentlich immer oberhalb des application-Tags.
2) Der Code der Verbindungs-Klasse (auch wenn's mir bei Denglischen Code immer schauert), wäre sicher nicht verkehrt.
 

Michel95

Mitglied
So ich hab die
Code:
AndroidManifest.xml
umgeändert, sodass die Berechtigungen nun oben stehen.
Tut mir leid wegen dem Denglisch, das hab ich früher mal programmiert und so übernommen.

Hier nochmal der Code von der Klasse
Code:
Verbindung.java
, falls das noch jemandem weiterhilft:

Java:
package com;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;

import util.PROTOKOLL;

/**
 * Die Verbindung ist eine reine Kommunikationsklasse, die nur Nachrichten hin und her schickt, allerdings nicht verarbeitet.
 * Sie kann sowohl vom Server als auch vom Client verwendet werden, da sie Informationen senden und empfangen kann.
 * In der Verbindung werden nach der Erzeugung alle bei der Kommunikation auftretenden Exceptions abgefangen, die Verarbeitung 
 * wird jedoch darueber informiert, dass ein Fehler aufgetreten ist.
 * 
 * @author Michel
 * @version 24.10.13
 */
public class Verbindung {
	
	/** Fortlaufende ID für alle Verbindungen. */
	private static int ID;
	
	private int id = ID++;
	
	/** Socket für den Datenverkehr */
	private Socket socket;
	
	/** Printstream fuer ausgehende Daten */
	private PrintStream printStream;
	
	/** BufferedReader fuer eingehende Daten */
	private BufferedReader bufferedReader;
	
	
	/** Gibt an, ob die Verbindung eingehende Daten liest */
	private boolean reading;
	
	
	/** Verarbeitung, die alle Nachrichten der Verbindung verarbeitet. 
	 *  Die Verbindung bekommt ihre zu sendenden Nachrichten von dieser Verarbeitung. */
	private Verarbeitung verarbeitung;
	

	/**
	 * Erzeugt eine neue Verbindung, die sich ueber einen Socket mit dem gegebenen Host-Server ueber den gegebenen Port verbindet.
	 * Hinweis: Damit die Verbindung eingehende Nachrichten liest, muss die Methode startReading() aufgerufen werden.
	 * 
	 * Die Verbindung teilt der gegebenen Verarbeitung automatisch mit, dass sie nun ueber diese Verbindung senden kann.
	 * Dann kann die Verarbeitung Nachrichten ueber die Verbindung versenden, empfangene Nachrichten werden automatisch
	 * an die Verbindung weitergeleitet.
	 * 
	 * @param host - Hostname bzw. IP-Adresse des Servers, an den sich die Verbindung anmelden soll
	 * @param port - Port, auf dem sich die Verbindung an den Server verbinden soll
	 * @param verarbeitung - Verarbeitung, an die die empfangenen Nachrichten weitergeleitet und dort verarbeitet werden 
	 * 		  sowie von der die zu sendenden Nachrichten stammen 
	 * 
	 * @throws IOException - Wenn ein I/O Error bei der Erzeugung des Sockets auftritt
	 * @throws UnknownHostException - Wenn der Host bzw. die IP-Adresse nicht identifiziert werden konnten
	 * @throws IllegalArgumentException - Wenn der Port ungueltig ist (kleiner als 0 oder groesser als 65535)
	 * 									- Wenn die gegebene Verbindung null ist
	 */
	public Verbindung(String host, int port, Verarbeitung verarbeitung) throws UnknownHostException, IOException, IllegalArgumentException {
		
		this(new Socket(host, port), verarbeitung);
	}
	
	
	/**
	 * 
	 * 
	 * @param socket
	 * @param verarbeitung
	 * 
	 * @throws IOException
	 * @throws IllegalArgumentException
	 */
	public Verbindung(Socket socket, Verarbeitung verarbeitung) throws IOException, IllegalArgumentException {
		
		// Pruefen, ob der Socket verbunden ist
		if ( ! socket.isConnected()) {
			throw new IllegalArgumentException("Der Socket der Verbindung ist nicht verbunden!");
		}
		
		// Socket in Variable speichern
		this.socket = socket;
	
		// Verzögerungszeit fuer Schliessen des Sockets auf 0 Sekunden setzen.
		socket.setSoLinger(true,0);
		
		// Verbindung initialisieren
		init(verarbeitung);
	}
	
	
	/**
	 * Initialisiert die Verbindung.
	 * 
	 * Zuerst wird ein Printstream fuer ausgehende Daten und ein BufferedReader fuer eingehende erstellt.
	 * Die Verbindung teilt der gegebenen Verarbeitung mit, dass sie nun ueber diese Verbindung senden kann.
	 * Dann kann die Verarbeitung Nachrichten ueber die Verbindung versenden, empfangene Nachrichten werden automatisch
	 * an die Verbindung weitergeleitet. Das Verarbeitungs-Objekt wird in einer Variable gespeichert.
	 * 
	 * @param verarbeitung - Verarbeitung, an die die empfangenen Nachrichten weitergeleitet und dort verarbeitet werden 
	 * 		  sowie von der die zu sendenden Nachrichten stammen 
	 * 
	 * @throws IOException - Wenn der Socket den Input- oder OutputStream nicht angeben kann
	 * @throws IllegalArgumentException - Wenn die gegebene Verbindung null ist
	 */
	private final void init(Verarbeitung verarbeitung) throws IOException, IllegalArgumentException {
		
		// PrintStream fuer ausgehende Daten erstellen
		printStream = new PrintStream(socket.getOutputStream());
						
		// BufferedReader fuer eingehende Daten erstellen
		bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
						
		if (verarbeitung != null) {
			// Verarbeitung speichern
			this.verarbeitung = verarbeitung;
			// Verbindung der Verarbeitung als diese Verbindung festlegen
			verarbeitung.setzeVerbindung(this);
		}
		else {
			throw new IllegalArgumentException("Die Verarbeitung, an die die Verbindung weiterleitet, darf nicht null sein!");
		}
	}
	
	
	/**
	 * Startet einen neuen Lesethread, wenn die Verbindung noch keinen am laufen hat.
	 */
	public void startReading() {
		
		// Nur ausfuehren, wenn nicht bereits ein Lesethread besteht.
		if ( ! reading) {
			
			final Verbindung verbindung = this;
			
			// Neuen Lesethread erzeugen
			Thread threadRead = new Thread(new Runnable() {

				public void run() {
					
					// reading auf true setzen
					reading = true;
					
					// Dauerschliefe, die den Reader abfragt, ob neue Nachrichten angekommen sind
					while (reading) {
						try {
							// Wenn der Reader eine nachricht Empfangen hat, wir diese gelesen und weitergegeben
							if (bufferedReader.ready()) {
								String nachricht = bufferedReader.readLine();
								empfange(nachricht);
							}
						} 
						catch (IOException e) {
							// Fehler beim Empfangen abfangen und an Verarbeitung weitergeben
							verarbeitung.empfangFehler(e, verbindung);
						}
					}
				}
			});
			
			// Lesethread starten
			threadRead.start();
		}
	}
	
	/**
	 * Stoppt den aktuellen Lesethread, falls einer besteht, indem die Dauerleseschleife beendet wird.
	 * Dadurch laeuft die run() Methode des Lesethreads zuende und der Thread wirt einen ThreadDeath und ist beendet.
	 */
	public void stopReading() {
		reading = false;
	}
	
	
	/**
	 * Sendet eine einfache Textnachricht an den Server.
	 * Wenn dabei eine Exception auftritt, wird die Verarbeitung darueber benachrichtgt.
	 */
	public void sende(String nachricht) {
		try {
			// Nachricht in PrintStream eingeben
			printStream.println(nachricht);
			// und abschicken
			printStream.flush();
		} 
		catch (Exception e) {
			verarbeitung.sendeFehler(e, this);
		}
	}
	
	
	/**
	 * Sendet ein einzelnes Byte an den Server.
	 * Wenn dabei eine Exception auftritt, wird die Verarbeitung darueber benachrichtgt.
	 */
	public void sende(int i) {
		try {
			// Nachricht in PrintStream eingeben
			printStream.print(i);
			
			// Hiernach sollte, wenn alle Daten gesendet sind, einmal flush() aufgerufen werden.
		} 
		catch (Exception e) {
			verarbeitung.sendeFehler(e, this);
		}
	}
	
	
	/**
	 * Sendet alle Daten, die sich noch im Buffer befinden.
	 */
	public void flush() {
		printStream.flush();
	}
	
	
	/**
	 * Wird beim Empfangen aufgerufen.
	 * Leitet die Nachricht an die Verarbeitung weiter, wo sie verarbeitet wird.
	 * Wenn dabei eine Exception auftritt, wird die Verarbeitung darueber auch benachrichtgt.
	 */
	private void empfange(String nachricht) {
		verarbeitung.verarbeite(nachricht, this);
	}
	
	
	/**
	 * Gibt zurueck, ob die Verbindung besteht.
	 */
	public boolean isConnected() {
		return socket.isConnected();
	}
	
	
	/**
	 * Beendet die Verbindung.
	 * @throws IOException - Wenn der Socket nicht beendet werden kann
	 */
	public void beende() throws IOException {
		sende(PROTOKOLL.EXIT);
		socket.close();
	}

	
	public String toString() {
		return "Verbindung " + id;
	}
}
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
N Java ME Server-Client Verbindung über Wifi Android & Cross-Platform Mobile Apps 6
F Server - Client Verbindung mit Java ME Android & Cross-Platform Mobile Apps 3
N Android Client-Server-Kommunikation: push oder poll? Android & Cross-Platform Mobile Apps 10
M Einfache Rechenoperation über TCP Server Android & Cross-Platform Mobile Apps 15
J Android Server-Login Daten in der App sicherer hinterlegen? Android & Cross-Platform Mobile Apps 7
M App Datenbank Server Android & Cross-Platform Mobile Apps 5
B Android TCP-Verbindung zum Server über welche Prozess auslagerung nutzen? Android & Cross-Platform Mobile Apps 1
K Android Zugriff auf FTP-Server Android & Cross-Platform Mobile Apps 1
M Android Nur erste Zeile wird vom Server empfangen Android & Cross-Platform Mobile Apps 0
L Zugriff auf entfernten mySQL-Server via Android Android & Cross-Platform Mobile Apps 4
K Android verbinden mit verschlüsselten Server Android & Cross-Platform Mobile Apps 2
M Inhalt eines Eingabefeldes an einen Server senden? Android & Cross-Platform Mobile Apps 9
T Android KontrollApp für Nitrado Server Android & Cross-Platform Mobile Apps 2
D gpx-Datei von Smartphone auf Server uploaden Android & Cross-Platform Mobile Apps 4
S Android binäre Daten zwischen Android und einem Java-Server Android & Cross-Platform Mobile Apps 5
K BT-Server EOFException abfangen Android & Cross-Platform Mobile Apps 2
M Android Von Smartphone auf Daten von Server zugreifen Android & Cross-Platform Mobile Apps 2
S Android Zugriff auf FTP Server Android & Cross-Platform Mobile Apps 7
A "HandyClient-Server-modell" Android & Cross-Platform Mobile Apps 7
D messages via xml zwischen server/clienthandy verschicken Android & Cross-Platform Mobile Apps 5
M Android TCP Client Android & Cross-Platform Mobile Apps 4
ruutaiokwu Android Selbst entwickelter SMTP-Client läuft auf PC, nicht aber auf Android Android & Cross-Platform Mobile Apps 9
M Android Simpler TLS/SSL Client Android & Cross-Platform Mobile Apps 11
R Android Mail Client öffnen Android & Cross-Platform Mobile Apps 4
Exdroid Android Broadcast client Android & Cross-Platform Mobile Apps 1
W Android HTTPS-Verbindung mit Client-Authentifizierung Android & Cross-Platform Mobile Apps 0
missy72 Kotlin SSH Verbindung mit JSch Android & Cross-Platform Mobile Apps 5
J Android VPN Verbindung herstellen? Android & Cross-Platform Mobile Apps 4
M Problem bei Werteübergabe, MSQL verbindung Android & Cross-Platform Mobile Apps 3
H WIFI, Bluetooth und NFC Verbindung überwachen Android & Cross-Platform Mobile Apps 1
H Android 3G TCP Socket Verbindung zum PC durch NAT Android & Cross-Platform Mobile Apps 8
T Android MSSQL Verbindung herstellen - Android Studio Android & Cross-Platform Mobile Apps 2
R Socket Verbindung AsycTask Android & Cross-Platform Mobile Apps 5
F Android USB Verbindung zu Windows Programm Android & Cross-Platform Mobile Apps 3
U Android Https-Verbindung Android & Cross-Platform Mobile Apps 2
K Java ME Bluetooth verbindung parameter Android & Cross-Platform Mobile Apps 3
A Problem mit HTTP- Verbindung Android & Cross-Platform Mobile Apps 4
N Handy -PC Verbindung Android & Cross-Platform Mobile Apps 2
N Blutooz-Verbindung ... ich schaffs nicht Android & Cross-Platform Mobile Apps 5
G Bluetooth Verbindung zwischen Handy und PC Android & Cross-Platform Mobile Apps 5
G Bluetooth Verbindung Android & Cross-Platform Mobile Apps 2
O Bluetooth Verbindung zwischen 2 Handys Android & Cross-Platform Mobile Apps 5
K HTTP-Verbindung mit J2ME.... Android & Cross-Platform Mobile Apps 2

Ähnliche Java Themen

Neue Themen


Oben