# Probleme bei mehreren Clients uns Senden von Messages



## dansmo (10. Dez 2009)

Hallo,

ich stehe vor einem Problem, das ich seit einiger Zeit nicht lösen kann. Aufgrund des Problems an sich würde es wahrscheinlich auch ins Anfängerforum passen, aber im Programm geht es um Client/Serverconnections.
Ich möchte aus einem Hauptprogramm mehrere Clients mit Messages versorgen.

Es geht um vier hauptsächliche Klassen und meine Unvermögen, diese richtig zu verbinden:

- class Main starte im Prinzip nur alles, so auch MainWindow
- class MainWindow   beinhaltet alles rund ums GUI und den Programmablauf
- class MultiServerMonitor    ist der Server, der die Clientanbindungen annehmen und handlen soll
- class MultiServerThread     soll sich um die Messages zwischen Server und Clients kümmern.

An sich klappt die Verbindung zu den Clients wunderbar. Meine Problem ist aber, dass ich die Messages
aus besitmmten Methoden der Klasse MainWindow senden will. Und ich kriege es einfach nicht hin, in
MainWindow die entsprechende Methode der Klasse MultiServerThread aufzurufen. 

Hier mal die wichtigsten Code- Ausschnitte:
Klasse MainWindow:


```
public class MainWindow extends JFrame{
 //Variablen, Konstanten, Arrays
 private ServerMonitor sm = Main.getSM();
 private MultiServerMonitor msm;
 private MultiServerThread mst;
 ...
  //Constructor
 MainWindow(){
  ...
  startServer(); //startet den Server
   }  

 void startServer(){
	 msm = new MultiServerMonitor(); 
 }

//dieser Aufruf steht in einem ButtonListener im Moment
 mst.sendMessage("hello World!"); //soll Methode in MultiServerThread ansteuern und hello World an die Clients senden


}
```

Klasse  MultiServerMonitor:

```
public class MultiServerMonitor {
 
		
        ServerSocket serverSocket = null;
        
        boolean listening = true;
        
        
        MultiServerMonitor(){
        try {
            serverSocket = new ServerSocket(40444);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 40444.");
            System.exit(-1);
        }

        while (listening)
        	try{
        	   new MultiServerThread(serverSocket.accept()).start();
        	}catch (IOException e){
        		System.err.println("unaible to start ServerThread");
        	}

        }
        
        
        public void exitConnection(){
        	try{
        		serverSocket.close();
        	}catch (IOException e) {
        		System.err.println("Was unailbe to close connection.");
        	}
        }
    
}
```

Klasse MultiServerThread:


```
public class MultiServerThread extends Thread {
    private Socket socket = null;

    public MultiServerThread(Socket socket) {
    
	super("MultiServerThread");
	this.socket = socket;
	
    }
    

    /*
    public void run() {

    //schleife, die messages an die clients sendet:
    for(;;){
    	try {
    		PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
    		out.println("Server says: hello client, welcome to the show!");

    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    }
    }
    */
    
    
    
    
    public void sendMessage(String message){
    	try{
    		PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
    		out.println(message);
    	} catch (IOException e){
    		System.err.println("Could not handle PrintWriter out.");
    	}
    }
   
    
    
}
```


Starte ich das alles zusammen mit einem Client, dann bekomme ich bei Drücken des Buttons eine NPE.
Grund ist, dass MainWindow  die Klasse MultiServerThread nicht kennt. Wie übergebe ich das konkret in meinem Konstrukt? Ich komme einfach nicht drauf. Wäre für Hilfe sehr, sehr dankbar.

Grüße,
dansmo


----------



## SlaterB (10. Dez 2009)

es kann auch mehrere Clients geben, der MultiServerMonitor sollte als Klassenattribut eine Liste der MultiServerThreads verwalten,
in JFrame brauchst du nicht unbedingt ein weiteres Attribut mst,

da ginge auch 
msm.getClientThreadList() 
oder von mir aus
msm.getDenEinzigenClientDerGeradeDaIst() 

auf null solltest du in jedem Fall prüfen, man kann den Button auch dann drücken, wenn kein Client da ist


----------



## dansmo (10. Dez 2009)

Du meinst so in der Art?


```
List<MultiServerThread> multiServerThreads = new ArrayList<MultiServerThread>();
...
while (listening)
        	try{
        	   multiServerThreads.add(new MultiServerThread(serverSocket.accept()).start());
        	}catch (IOException e){
        		System.err.println("unaible to start ServerThread");
        	}

        }
...
```
Soweit war ich auch schonmal, da meckert Eclipse aber "The method add is not applicable for the arguments (void)".


----------



## SlaterB (10. Dez 2009)

du hast
list.add(new X().y())

richtig ist
X x = ..;
x.y();
list.add(x);


----------



## dansmo (11. Dez 2009)

Habe das jetzt modifiziert. Allerdings bekomme ich nun einen NPE, die mir auch unerklärlich ist.

In MainWindow:

```
private MultiServerMonitor msm;
 ...
 //Constructor
 MainWindow(){
  ...
  startServer();
 }

  void startServer(){
	msm = new MultiServerMonitor(); 
  }

  //Und dann beim Buttonlistener:
      System.out.println("class" + msm);
    for(int i = 0; i <= msm.multiServerThreads.size(); i++){
        mst = msm.multiServerThreads.get(i);
        mst.sendMessage("hello Client # " + i + " !");
    }
```

Leider ist msm = null. Warum? Ich habe doch in startServer msm initialisiert? Wo ist mein Fehler?


----------



## SlaterB (11. Dez 2009)

der Fehler ist, kein vollständiges Programm zu posten,
es ist nicht erkennbar, was oder wo 'Buttonlistener' ist, ob das auf eine andere gleichnamige lokale Variable msm zugreift oder ein anderes Objekt vom Typ MainWindow oder ob zwischendurch wieder auf null gesetzt wird 
oder der Listener schon vor der Initialisierung drankommt oder oder,
denkbar ist vieles, sichtbar bisher wenig


----------



## dansmo (11. Dez 2009)

Hast recht. MainWindow umfasst 600 Zeilen, wollte das Forum damit nicht erschlagen.

Das ist der vollständige Konstruktor:

```
//Constructor
 MainWindow(){
  L.info("starting main Window...");
  setTitle("Exchange Monitor");  // Titel der Fenster setzen
  initComponents(); // Komponente (Tabelle, Button, Menü etc.) erstellen
  this.setSize(300,150);
  this.setVisible(true);
  api.addStatusListener(new MyExtendedStatusListener());
  L.info("Loaded Main Window");
  startServer();
 }
```

 initComponents erstellt die Elemente des GUI, u.a. einen Button auf dem der ButtonListener sitzt.
 ButtonListener ist eine Klasse in MainWindow.java:

```
class ButtonListener implements ActionListener{
  
  MyQuoteListener quoteListener; // Enthält ein Bezug auf ein MyQuoteListener
  boolean quotesRunning; // Ob der Kurse gerade abonniert ist oder nicht
  Quote quote; // Der Kurs-Objekt, wie wir ihn von der API bekommen
  
  //Constructor für ButtonListener
  ButtonListener(){
   quoteListener = new MyQuoteListener(); // Erstelle ein neuen MyQuoteListener, weise es die Variabel quoteListener zu
   quotesRunning = false; // Am Anfang laufen die Kurse nicht
  }
  
  //Methode für Buttonlistener
  public void actionPerformed(ActionEvent ae){  
   if (quotesRunning){ 
	   /*
	   for(int i = 0;i<=quotes.length-1;i++){
		   api.removeQuoteListener(quotes[i], quoteListener); 
	   }
	   */
    quotesRunning = false; // Die Kurse laufen nun nicht mehr
    textArea.setText("");
    for(int i = 0; i<=warned.length-1;i++){
    	warned[i] = false;
    }
    t.cancel();
    count = 0;
    textArea.setBackground(Color.orange);
    cancelOrders();
    startBtn.setText("Start"); // Button-Text anpassen
    return; // Fertig mit der Button-Klick
   }

    
    quotesRunning = true; // Die Kurse laufen jetzt
    startBtn.setText("Stop"); 
    t= new Timer("recheckTimer");
	//checkDataConnections();
	//checkTradeConnections();
    System.out.println("class" + msm);
    for(int i = 0; i <= msm.multiServerThreads.size(); i++){
        mst = msm.multiServerThreads.get(i);
        mst.sendMessage("hello Client # " + i + " !");
    }

 
   startBtn.setText("Stop"); // Button-Text anpassen
   
  
  }
```

 msm kommt sonst nirgendwo vor. 
Hier noch der Ausschnitt aus void initComponents:

```
startBtn = new JButton("Start"); // Neuen JButton erstellen, mit Text "Start"
  ButtonListener bl = new ButtonListener(); // Ein neuen ButtonListener (wie oben definiert) erstellen
  startBtn.addActionListener(bl); // Die ButtonListener zum Ereignis-Abhörer-Liste des Buttons hinzufügen.  Somit wird Java die actionPerformed()
           // Methode von der ButtonListener aufrufen wenn das Button geklickt wird.
 
  startBtn.setBackground(lightBlue);
  btnPanel.add(startBtn);
```


----------



## SlaterB (11. Dez 2009)

nicht schlecht, aber nicht ausreichend,
kopieren, ausführen, läuft, vorher geht fast gar nix

ich zumindest kann den Fehler weiterhin nicht erkennen,
und wenn du mehr postest, dann kann das auch ganz leicht weniger sein,
Quark wie MyQuoteListener, MyExtendedStatusListener, L.info(), setBackground(lightBlue) und wer weiß was alles hat sicher nix mit dem Problem zu tun, 
kann testweise alles gelöscht werden, 
auch MultiServerMonitor muss kein Server mehr sein, kann im Grunde eine leere Dummy-Klasse sein, es geht darum ob es null ist oder eben nicht,

falls bei derartigen Vereinfachungen der Fehler verschwindet -> aha, schon hast du selber was herausgefunden

macht sicher 5-20 Min. Mühe, vielleicht gar neues Projekt anlegen und kopieren usw., aber diese Arbeit kannst du dir ruhig machen um sie anderen zu ersparen

-------

startBtn.addActionListener(bl);
// Die ButtonListener zum Ereignis-Abhörer-Liste des Buttons hinzufügen.  Somit wird Java die actionPerformed()
           // Methode von der ButtonListener aufrufen wenn das Button geklickt wird.

für wen ist eigentlich dieser Kommentar? wer immer auch nur die leiseste Ahnung von JButton + ActionListener hat, wird diesen Zusammenhang doch hoffentlich kennen,
falls es nicht eine Gedankenstütze für dich ist, bringen solche offensichtlichen Informationen praktisch nix, stören eher


----------



## dansmo (11. Dez 2009)

Gute Idee. Habe ein neues Projekt angelegt und alles entschlackt. Fehler bleibt der Gleiche:

Main:

```
public class Main {

	 public static void main(String[] args){
		
		new MainWindow();	

	}
}
```

MainWindow:


```
public class MainWindow extends JFrame{
 //Variablen, Konstanten, Arrays
 private MultiServerMonitor msm;
 private MultiServerThread mst;
 
 JButton startBtn;
 JPanel btnPanel;
 
 //Constructor
 MainWindow(){
  setTitle("Exchange Monitor");  // Titel der Fenster setzen
  initComponents(); // Komponente (Tabelle, Button, Menü etc.) erstellen
  this.setSize(300,150);
  this.setVisible(true);
  startServer();
 }

MultiServerMonitor und MultiServerThread habe ich so gelassen wie sie sind.
 
 
 void startServer(){
	msm = new MultiServerMonitor(); 
 }


 //ButtonListener inkl. Methode für Quotes-Button
 class ButtonListener implements ActionListener{
  
 
  //Constructor für ButtonListener
  ButtonListener(){
  
  }
  
  //Methode für Buttonlistener
  public void actionPerformed(ActionEvent ae){  

    System.out.println("class" + msm);
    for(int i = 0; i <= msm.multiServerThreads.size(); i++){
        mst = msm.multiServerThreads.get(i);
        mst.sendMessage("hello Client # " + i + " !");
    }  
  
  }
  
 }

  
   //Methode zum Erstellen der grafischen Komponenten
   void initComponents(){
  
  // Füge einige Buttons ein!
  btnPanel = new JPanel();//Ein JPanel ist abgetrennt vom Rest des Frames
  btnPanel.setBorder(BorderFactory.createEtchedBorder());
    
  startBtn = new JButton("Start"); // Neuen JButton erstellen, mit Text "Start"
  ButtonListener bl = new ButtonListener(); // Ein neuen ButtonListener (wie oben definiert) erstellen
  startBtn.addActionListener(bl); // Die ButtonListener zum Ereignis-Abhörer-Liste des Buttons hinzufügen.  Somit wird Java die actionPerformed()
           // Methode von der ButtonListener aufrufen wenn das Button geklickt wird.
 
  btnPanel.add(startBtn);
  
  getContentPane().add(btnPanel, BorderLayout.NORTH); //Dem Panel sagen wo es sich anordnen soll
  
   
  setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Wenn der Fenster geschlossen wird, so soll die Anwendung beendet werden
 }
 
   
   

   
}
```


----------



## SlaterB (11. Dez 2009)

```
public class Test
{
    public static void main(String[] args)
    {
        new MainWindow();
    }
}

class MainWindow
    extends JFrame
{
    private MultiServerMonitor msm;
    JButton startBtn;
    JPanel btnPanel;

    MainWindow()
    {
        setTitle("Exchange Monitor");
        initComponents();
        this.setSize(300, 150);
        this.setVisible(true);
        startServer();
    }

    void startServer()
    {
        msm = new MultiServerMonitor();
    }

    void initComponents()
    {
        btnPanel = new JPanel();
        btnPanel.setBorder(BorderFactory.createEtchedBorder());

        startBtn = new JButton("Start");
        ButtonListener bl = new ButtonListener();
        startBtn.addActionListener(bl);

        btnPanel.add(startBtn);

        getContentPane().add(btnPanel, BorderLayout.NORTH);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    class ButtonListener
        implements ActionListener
    {
        public void actionPerformed(ActionEvent ae)
        {
            System.out.println("msm: " + msm);
        }
    }
}
class MultiServerMonitor {}
```
starten, Button drücken, Ausgabe:
msm: test.MultiServerMonitor@1a05308

msm ist nicht null, vielleicht bekommst du eine Exception beim Zugriff auf msm.multiServerThreads?


----------



## dansmo (11. Dez 2009)

Also wenn ich MutliServerMonitor nur als dummy Klasse mache, dann habe ich das jetzt auch festgestellt, dass ich kein null bekomme.
Benutze ich aber die MulitServerMonitor so wie im 1. Posting, dann habe ich weiterhin null.
Das Problem muss also irgendwie in MultiServerMonitor liegen, was aber doch keinen Sinn macht, oder?


----------



## SlaterB (11. Dez 2009)

->


SlaterB hat gesagt.:


> vielleicht bekommst du eine Exception beim Zugriff auf msm.multiServerThreads?


<-

schreib doch nochmal die Original-Fehlermeldung,
was ergab die Ausgabe bei class direkt vor dem Zugriff?


----------



## dansmo (11. Dez 2009)

```
classnull
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
	at com.mmwarburg.meander.MainWindow$ButtonListener.actionPerformed(MainWindow.java:63)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
	at java.awt.Component.processMouseEvent(Component.java:6041)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3265)
	at java.awt.Component.processEvent(Component.java:5806)
	at java.awt.Container.processEvent(Container.java:2058)
	at java.awt.Component.dispatchEventImpl(Component.java:4413)
	at java.awt.Container.dispatchEventImpl(Container.java:2116)
	at java.awt.Component.dispatchEvent(Component.java:4243)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4322)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3986)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3916)
	at java.awt.Container.dispatchEventImpl(Container.java:2102)
	at java.awt.Window.dispatchEventImpl(Window.java:2440)
	at java.awt.Component.dispatchEvent(Component.java:4243)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:273)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:183)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:173)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:168)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:160)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:121)
```

Zeile 63 ist bei mir  

```
for(int i = 0; i <= msm.multiServerThreads.size(); i++){
```

Wobei die Ausgabe ja eindeutig null ist.


----------



## SlaterB (11. Dez 2009)

falls nicht zufällig toString() des Objektes null oder "null" zurückgibt 
noch sicherer ist 
System.out.println("msn null? "+(msn == null));

-------

tja, mir ist noch keine weitere Erläuchtung gekommen,
wäre aber inzwischen so interessiert, dass du mir das Programm als zip zuschicken könntest,
hier im Forum oder sonstwo hochladen oder an slaterb gmx de

oder du schaust dir weitere Zwischenschritte zwischen funktionierender und nicht funktionierender Version an, 
irgendwann muss eine kleine Änderung die entscheidene sein


----------



## dansmo (11. Dez 2009)

Es grenzt sich so langsam ein, es muss an dem Code-Ausschnitt liegen. Zur besseren Übersichtlichkeit habe ich
die Methode installServer vom Rest abgegrenzt. Wird die Methode nicht aufgerufen, bekomme ich kein null. Wir sie aufgerfuden habe ich null. 


```
public class MultiServerMonitor {
 
		
        ServerSocket serverSocket = null;
        
        boolean listening = true;
        MultiServerThread mst;
        List<MultiServerThread> multiServerThreads = new ArrayList<MultiServerThread>();
        
        //Constructor
        MultiServerMonitor(){
        	installServer();
        }
        
        void installServer(){
        try {
            serverSocket = new ServerSocket(40444);
        } catch (IOException e) {
            System.err.println("Could not listen on port: 40444.");
            System.exit(-1);
        }
        
        while (listening)
        	try{      		
        		mst = new MultiServerThread(serverSocket.accept());
        		mst.start();
        	    multiServerThreads.add(mst);
        	}catch (IOException e){
        		System.err.println("unaible to start ServerThread");
        	}

        }
        
        
        
        public void exitConnection(){
        	try{
        		serverSocket.close();
        	}catch (IOException e) {
        		System.err.println("Was unailbe to close connection.");
        	}
        }
        
        
    
}
```


----------



## SlaterB (11. Dez 2009)

aja, daran hatte ich nicht gedacht, 
war dann aber wirklich schon in den ersten Postings zu erkennen, sorry 

also, du hast im Konstruktor eine Endlosschleife eingebaut, daher ist der Konstruktor nie fertig 
und genauso hängt auch die Methode startServer() von MainWindow,

im Grunde hatte ich auch das bedacht, als ich schrieb 'oder der Listener schon vor der Initialisierung drankommt'
denn die Methode startServer() ist auch dann noch nicht fertig, wenn die GUI schon angezeigt wird und der Button gedrückt wird,

das fällt sonst nicht weiter auf, weil nach startServer() nichts weiter passieren muss, 
im Grunde ist aber auch der MainWindow-Konstruktor noch nicht fertig und die main-Methode ebensowenig,

sauber wäre, wenn im MultiServerMonitor-Konstruktor nix passiert,
aus 
void startServer(){
    msm = new MultiServerMonitor(); 
 }
mache

```
void startServer(){
    msm = new MultiServerMonitor(); 
    Runnable r = new Runnable() {
      public void run() {
           msn.starteDeineLangeSchleife();
      }
    }
    new Thread(r).start();
    System.out.println("msn erfolgreich initialisiert");
}
```
oder baue MultiServerMonitor auch zu einer Thread-Klasse um


----------



## dansmo (11. Dez 2009)

Sehr gut, danke! Jetzt geht es und ich habe es sogar verstanden!


----------

