# Wie spreche ich einen Thread an?



## Paul279 (20. Sep 2012)

Hallo liebe Community,

jetzt steh ich vor einem Problem, dass ich ohne euch nicht lösen kann.

Ich habe ein Programm geschrieben, das Auswertungen durchführt.
Funktioniert tadellos.

Habe eine Klasse erzeugt(serverThread), die nur auf Anfragen von Clients reagiert. Wenn ein Client sich auf einen Socket verbindet, wird die Klasse serverThread2 gestartet. Somit habe ich für jeden Client einen eigenen serverThread2.

serverThread:

```
package myserver;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Asus Schlappi
 */
public class serverThread extends Thread{
     
     private ServerSocket serverSocket = null;

    public serverThread(ServerSocket serverSocket) {
    this.serverSocket = serverSocket;
    }
    

    public void run() {
            
        boolean listening = true;
        try {
            while (listening)     
            new serverThread2(serverSocket.accept()).start();
        
            serverSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(serverThread.class.getName()).log(Level.SEVERE, null, ex);
        }                        
    }    
}
```

ServerThread2:

```
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package myserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Paul
 */
public class serverThread2 extends Thread{

    private Socket socket = null;
    String vomClient;
    String zumClient; 
    PrintWriter out;
    BufferedReader in;
    
    serverThread2(Socket accept) {
        super("serverThread");
	this.socket = socket;
        System.out.println("Vor beim Server");
    }
 
    public void run() {
    	try {
	    out = new PrintWriter(socket.getOutputStream(), true);
	    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            zumClient = "START";
            out.println(zumClient);
	    while ((vomClient = in.readLine()) != null) {
		
                //out == damit wird alles zum Client geschickt
                out.println(zumClient);
                
		
                
                //Clientverbindung schließen
                if (zumClient.equals("Bye"))
		    break;
	    }
	    out.close();
	    in.close();
	    socket.close();

	} catch (IOException e) {
	} 
    }
    
}
```

Gut, die Verbindung steht, man könnte kommunizieren.

Das Problem:
Im serverThread steht:

```
new serverThread2(serverSocket.accept()).start();
```

somit kann ich den Thread nicht wie ein normales Objekt ansprechen. Also logisch für mich hätte es so funktionieren müssen:
serverThread2 meinServerThread2 = new serverThread2(serverSocket.accept()).start();

Leider funktioniert das nicht so.
Ich möchte aber gerne den Thread irgendwie ansprechen können, denn wenn in meinem Auswertungssystem ein Button "Senden" gedrückt wird, soll auch eine Message an einen speziellen Client (serverThread2) gelangen.

Also kurz gesagt:
Wie spreche ich meinen Thread an, übergebe ihm Werte und bekomme auch Rückmeldungen?

Ich hoffe, dass alles verständlich ist.
Mfg
Paul


----------



## parabool (20. Sep 2012)

```
serverThread2 meinServerThread2 = new serverThread2(serverSocket.accept());
meinServerThread2.start();
```


----------



## Paul279 (20. Sep 2012)

danke sehr, aber es hilft mir nicht ganz weiter

Hier mein Code:

```
public class serverThread extends Thread{
     
    private ServerSocket serverSocket = null;

    serverThread2[] meineServerThreads;
    int i = 0;
    
    public serverThread(ServerSocket serverSocket) {
    
        this.serverSocket = serverSocket;
        this.meineServerThreads = new serverThread2[25];
    }
    

    public void run() {
            
        boolean listening = true;
        try {
            while (listening)     
            i++;
            //new serverThread2(serverSocket.accept()).start();
            meineServerThreads[i] = new serverThread2(serverSocket.accept());
            meineServerThreads[i].start();
            
            serverSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(serverThread.class.getName()).log(Level.SEVERE, null, ex);
        }                        
    }    
}
```

Warum geht er nicht in meinServerThreads[1].start ? 
er geht in "meineServerThreads[1] = new serverThread2(serverSocket.accept());" hinein, aber macht dann nicht weiter ???


----------



## Refti (20. Sep 2012)

nach while() noch Klammern setzen {}


----------



## Paul279 (20. Sep 2012)

Ahhh so ein dummer Fehler von mir.

Danke sehr, scheint zu funktionieren 

Gibt es noch eine Möglichkeit, die "meineServerThreads" dynamisch zu machen.
Also dass ich diese nicht auf 25 beispielsweise begrenze.
Jetzt habe ich ja begrenzt:

this.meineServerThreads = new serverThread2[25];

Einfach 100 reinschreiben möchte ich nicht, da es sehr hässlich ist und auch nicht performant.

Danke sehr


----------



## Michael... (20. Sep 2012)

Paul279 hat gesagt.:


> Jetzt habe ich ja begrenzt:
> 
> this.meineServerThreads = new serverThread2[25];
> 
> Einfach 100 reinschreiben möchte ich nicht, da es sehr hässlich ist und auch nicht performant.


Es gibt auch Lists z.B. ArrayList. Die sind dynamisch erweiterbar. Allerdings macht es wenig Sinn Referenzen auf einen Thread zu halten. Zur Kommunikation musst Du Referenzen auf die In- und OutputStreams der Clients halten und nur den InputStream in einem separaten Thread auslesen.


----------



## Paul279 (20. Sep 2012)

uh danke sehr. Klingt alles schon ziemlich "hoch".
Aber ich glaube, dass ich weiß was du meinst.

Werde ich ausprobieren.
Danke sehr


----------



## jamesv (20. Sep 2012)

Michael, könntest du das eventuell etwas erläutern? HAst du einen Link dazu, dass interessiert mich jetzt aber, wie man so etwas realisiert. Habe noch nie etwas mit Netzwerk/Threads gemacht xD

Thx.
--
Edit:

Bzw. warum man die In/Output streams referenzieren muss.


----------



## Paul279 (20. Sep 2012)

Entschuldige ich brauche wieder Hilfe bei diesem Thema.

Ich schaffe es, Dateien hin und her zu senden, aber immer nur Strings.

Wie kann ich ein ganzes Object oder ArrayList senden aber auch empfangen?

Danke sehr


----------



## Spacerat (21. Sep 2012)

> Hoch veehrter Herr Thread
> Ich, als Ihr Schöpfer fände es angenehm, wenn Sie auch mal andere Dinge im Netz hin und her bewegen würden, als immer nur Strings.


 :lol:

@TO: Kennst du den Serializable-Mechanismus? In diesem Fall, eignet er sich zur Abwechslung auch mal. Die meisten User wollen damit aber immer blos Dateien laden und speichern und dann rät man davon ab. Für deinen Fall aber ist er hervorragend geeignet.


----------



## Paul279 (21. Sep 2012)

So es ist 2 Uhr früh und ich muss ins Bett, morgen wird wieder ein schei* Tag in der Arbeit :-(

@Spacerat: Nein sagt mir leider gar nichts, aber ich hoffe es geht irgendwie so, wie ich es jetzt gemacht habe.

Nun habe ich alles neu gemacht:
Mein Server:

```
package myserver;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;

/**
 *
 * @author Asus Schlappi
 */
public class serverThread extends Thread{
    
    ArrayList<ObjectOutputStream> clientstreams = new ArrayList<>();    
    private ServerSocket mySocket;

 
    public serverThread(ServerSocket serverSocket) {
        this.mySocket = serverSocket;
    }
    
    public void run() {
        while(true){
            try {
                System.out.println("Ready to receive");
                Socket client = mySocket.accept();
                clientstreams.add(new ObjectOutputStream(client.getOutputStream()));
                System.out.println(client.getInetAddress().getHostAddress()+" connected to the Server");
                Thread t = new Thread(new ClientHandler(client));
                t.start();
            } catch (IOException e) {
                // TODO Auto-generated catch block
            }
        }
    }
    
    public void shareToAll(Object objectToSchare){
        for(ObjectOutputStream stream:clientstreams){
            try {
                stream.writeObject(objectToSchare);
                stream.flush();
            } catch (IOException e) {
                // TOD  O Auto-generated catch block
            }
        }
    } 
        
    
    private class ClientHandler implements Runnable{
        Socket clientSocket;

        public ClientHandler(Socket clientSocket){
            this.clientSocket = clientSocket;
        }
        
        @Override
        public void run() {
            try {
                ObjectInputStream ois = new ObjectInputStream(clientSocket.getInputStream());
                while(true){
                    try {
                        ois.readObject();

                    } catch (ClassNotFoundException | IOException e) {
                        // TODO Auto-generated catch block
                    }
                }
            }catch(SocketException e){
                System.out.println(clientSocket.getInetAddress().getHostAddress()+" disconnected from the Server");
                clientstreams.remove(clientSocket);
            }catch (IOException e) {
                // TODO Auto-generated catch block
            }
        }
    }   
 
}
```

MeinClient:

```
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package client;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 *
 * @author Paul
 */
public class clientThread {
 
    ObjectOutputStream  out;
    ObjectInputStream  in;

    public clientThread(String ip, int port) throws UnknownHostException, IOException {
        connect(ip, port);
    }
    
    public void connect(String ip, int port) throws UnknownHostException, IOException {
        Socket socket = new Socket(ip, port);
        out = new ObjectOutputStream(socket.getOutputStream());
        in = new ObjectInputStream(socket.getInputStream());
    } 
    
    public void send(Object message) throws IOException {
        if(out == null) throw new IOException();
        out.writeObject(message);
        out.flush();
    }
 
    public Object receive() throws IOException{
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException();
        }
    }
    
}
```

Dieser Nachbau ist von diesem gefundenen Forumsbeitrag (Klick mich. Habe gemeint, dass ich es verstehe, aber leider schaltet mein Kopf bei 

```
ois.readObject();
```
 komplett ab.

Also was funktioniert/wo ist der Fehler:
Server startet. Der Client verbindet sich zum Server.

Ich rufe die Methode "shareToAll" beim Server auf und sende einfach "Hallelulja" zum Beispiel.

Beim Client tut sich rein gar nichts.
Der Server geht in die Klasse ClientHandler hinein und geht bei "ois.readObject();" nicht weiter.

Was muss ich tun? Muss der Client das irgendwie "abholen" oder was passiert beim ois.readObject(); ?

Hoffe dass mir ein Java Guru weiterhelfen kann. Geh jetzt etwas verzweifelt ins Bett :bahnhof:
Wünsche eine gute Nacht


----------



## Spacerat (21. Sep 2012)

So einfach geht's natürlich nicht. Ein ObjectStream muss zwischenzeitlich auch mal geschlossen werden, weil sonst laufen die HandleTables über und vieles mehr. Will man also, dass die Socket-Streams geöffnet bleiben, so muss man die Objekte z.B. in ein ByteArrayStream kapseln und dessen ByteArray senden. Andernfalls steht da in deinem Link auch etwas davon, dass man die ObjectOutputStreams gleich nach ihrer Initialisierung flushen sollte, damit er einen initialen Header sendet und die Empfänger nicht ewig auf ein Objekt warten, was niemals kommt.


----------



## Paul279 (21. Sep 2012)

Könntest du mir bitte zeigen wie ich so etwas realisiere, mit dem flushen, dass er nur einen initialen Header sendet und ich das dann Empfangen kann.

Steh dermaßen auf der Leitung


----------



## Spacerat (21. Sep 2012)

Also bisher schreibst du:

```
clientstreams.add(new ObjectOutputStream(...))
```
daraus machst du:

```
ObjectOutputStream oos = new ObjectOutputStream(...);
clientstreams.add(oos);
oos.flush();
```
Wenn ich das richtig gelesen hab'.


----------



## kaetzacoatl (21. Sep 2012)

ois.readObject() wartet auf ein Object.
Flushen nützt da wenig.

edit:
Genaugenommen wartet der SocketStream


----------



## Spacerat (21. Sep 2012)

kaetzacoatl hat gesagt.:


> ois.readObject() wartet auf ein Object.
> Flushen nützt da wenig.


Gut erkannt, dass ist genau der Punkt. Damit es das nicht tut, muss der OutputStream geflushed werden, bevor der InputStream geöffnet wird.


> the ObjectInputStream constructor reads data from the given InputStream. in order for this to work, you must flush the ObjectOutputStream immediately after constuction (to write the initial header) before you attempt to open the ObjectInputStream. also, if you want to send more than one object per connection, you must open the ObjectOutputStream once and use it for the lifetime of the socket (e.g. your shareToAll method).


----------



## kaetzacoatl (21. Sep 2012)

Nein.
Der ObjectOutputStream muss
NICHT geflusht werden.


----------



## Paul279 (21. Sep 2012)

Okay, auch wenn es nicht geflusht werden muss.
Habe es mal umgeschrieben auf das:

```
ObjectOutputStream oos = new ObjectOutputStream(...);
clientstreams.add(oos);
oos.flush();
```

Und wie kann der Client jetzt etwas empfangen? Client reagiert überhaupt nicht ?


----------



## Spacerat (21. Sep 2012)

Na sowas. Was muss man dann machen, damit readObject() nicht bis zum Ende aller Tage auf Objekte wartet, die auf der anderen Seite bereits durch den Aufruf von shareToAll() geschrieben wurden?
[EDIT] Überschneidung. Wenn's jetzt tatsächlich nicht am flush() liegt, gebe ich mal vertrauensvoll an kaetzacoatl ab.[/EDIT]


----------



## kaetzacoatl (21. Sep 2012)

this.socket = socket;

Daran liegts!
Der client hat keine Verbindung
zum server!

Tipp: benutz eine IDE!
         dann hättest du
         Fehler sofort
         gefunden.


----------



## Paul279 (22. Sep 2012)

So liebe Leute
gestern habe ich mal schlafen müssen, war fertig von der Woche und der Nacht davor :rtfm:

Nein es liegt nicht am socket = socket, da ich ja oben geschrieben habe, dass ich alles neu gecodet habe.J etzt habe ich, bevor ich alles erkläre, einfach mein Programm (server + client) gepackt und zum Download bereitgestellt. Zum Download

PS: Das Programm bleibt jetzt bei ois.readObject() stehen und macht keine Endlosschleife. 

Fragt mich aber nicht was es genau war, habe eigentlich nur gelöscht. Aber seht mal selbst

Das Thema endet hier leider nicht.
Diese Sockets machen mich fertig.

*Problem:*
Ich habe keine Ahnung, wie ich nun das Object beim Client speichern kann.
Es gibt zwar eine Funktion beim Client:

```
public Object receive() throws IOException{
        try {
            return in.readObject();
        } catch (ClassNotFoundException e) {
            throw new IOException();
        }
    }
```

Aber woher soll man jetzt beim Client wissen, dass er was bekommen hat? 
In meinem Kopf ist das so:
Server sendet, client müsste in einer while sein und sobald er was empfängt macht er was.

Jetzt steht nur "return in.readObject();" ja wohin returnt er das?

Tut mir leid, wenns jetzt schon sehr viel ist, aber leider verstehe ich das nicht (Mein Kopf ???:L )

PS: Das Projekt ist in Netbeans programmiert. Beim Server kann man die Klasse serverThread2 einfach lassen, die ist für nichts gut. Da hab ich nur probiert. Eigentlich braucht man die Datenbank nicht für mein Programm, müsste aber trotzdem gehn, da die Datenbank mit im Paket dabei ist.
(Server/DatenbankFiles)


----------

